From 6399355d20ee87365e19ef94aa8798c602f697e6 Mon Sep 17 00:00:00 2001 From: thlo Date: Tue, 25 Dec 2012 11:36:09 +0100 Subject: Initial Version --- vdr-smarttvweb/Makefile | 109 ++ vdr-smarttvweb/httpresource-hmm.c | 1130 ++++++++++++++++++++ vdr-smarttvweb/httpresource-hmm.h | 94 ++ vdr-smarttvweb/httpresource.c | 2122 +++++++++++++++++++++++++++++++++++++ vdr-smarttvweb/httpresource.h | 188 ++++ vdr-smarttvweb/log.c | 74 ++ vdr-smarttvweb/log.h | 50 + vdr-smarttvweb/smarttvfactory.c | 382 +++++++ vdr-smarttvweb/smarttvfactory.h | 88 ++ vdr-smarttvweb/smarttvweb.c | 152 +++ vdr-smarttvweb/stvw_cfg.c | 165 +++ vdr-smarttvweb/stvw_cfg.h | 86 ++ vdr-smarttvweb/url.c | 302 ++++++ vdr-smarttvweb/url.h | 49 + 14 files changed, 4991 insertions(+) create mode 100644 vdr-smarttvweb/Makefile create mode 100644 vdr-smarttvweb/httpresource-hmm.c create mode 100644 vdr-smarttvweb/httpresource-hmm.h create mode 100644 vdr-smarttvweb/httpresource.c create mode 100644 vdr-smarttvweb/httpresource.h create mode 100644 vdr-smarttvweb/log.c create mode 100644 vdr-smarttvweb/log.h create mode 100644 vdr-smarttvweb/smarttvfactory.c create mode 100644 vdr-smarttvweb/smarttvfactory.h create mode 100644 vdr-smarttvweb/smarttvweb.c create mode 100644 vdr-smarttvweb/stvw_cfg.c create mode 100644 vdr-smarttvweb/stvw_cfg.h create mode 100644 vdr-smarttvweb/url.c create mode 100644 vdr-smarttvweb/url.h diff --git a/vdr-smarttvweb/Makefile b/vdr-smarttvweb/Makefile new file mode 100644 index 0000000..b04d143 --- /dev/null +++ b/vdr-smarttvweb/Makefile @@ -0,0 +1,109 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# +PLUGIN = smarttvweb + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + + +CXX ?= g++ +ifdef DEBUG +CXXFLAGS ?= -g -O0 -fPIC -Wall -Woverloaded-virtual #-Werror +else +CXXFLAGS ?= -fPIC -Wall -Woverloaded-virtual #-Werror +#CXXFLAGS ?= -O2 -fPIC -Wall -Woverloaded-virtual #-Werror +endif + +### The directory environment: + +#VDRDIR = ../../.. +VDRDIR = /usr/include/vdr +LIBDIR = . +#LIBDIR = ../../lib +TMPDIR = /tmp + +### Allow user defined options to overwrite defaults: + +#-include $(VDRDIR)/Make.config + +### read standlone settings if there +-include .standalone + +### The version number of VDR (taken from VDR's "config.h"): + +VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') +APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE + +### The object files (add further files here): + +OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o log.o url.o stvw_cfg.o + +OBJS2 = + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< + +# Dependencies: + +MAKEDEP = g++ -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Targets: + +all: allbase libvdr-$(PLUGIN).so +standalone: standalonebase smarttvweb-standalone + +objectsstandalone: $(OBJS) +objects: $(OBJS) $(OBJS2) + +allbase: + ( if [ -f .standalone ] ; then ( rm -f .standalone; make clean ; make objects ) ; else exit 0 ;fi ) +standalonebase: + ( if [ ! -f .standalone ] ; then ( make clean; echo "DEFINES+=-DSTANDALONE" > .standalone; echo "DEFINES+=-D_FILE_OFFSET_BITS=64" >> .standalone; make objectsstandalone ) ; else exit 0 ;fi ) + +libvdr-$(PLUGIN).so: objects + $(CXX) $(CXXFLAGS) -shared $(OBJS) $(OBJS2) -o $@ + @cp $@ $(LIBDIR)/$@.$(APIVERSION) + +smarttvweb-standalone: objectsstandalone + $(CXX) $(CXXFLAGS) $(OBJS) -lpthread -o $@ + chmod u+x $@ + +dist: clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tgz + +clean: + rm -f $(OBJS) $(OBJS2) $(DEPFILE) libvdr*.so.* *.tgz core* *~ .standalone smarttvweb-standalone + diff --git a/vdr-smarttvweb/httpresource-hmm.c b/vdr-smarttvweb/httpresource-hmm.c new file mode 100644 index 0000000..1c2117f --- /dev/null +++ b/vdr-smarttvweb/httpresource-hmm.c @@ -0,0 +1,1130 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "httpresource.h" + +#ifndef VOMPSTANDALONE +#include +#include +#endif + +#define SERVER "SmartTvWeb/0.1" +#define PROTOCOL "HTTP/1.1" +#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" + +#define MAXLEN 4096 +#define OKAY 0 +#define ERROR (-1) +#define DEBUG_REGHEADERS +#define DEBUGPREFIX "mReqId= " << mReqId << " fd= " << mFd +#define DEBUGHDR " " << __PRETTY_FUNCTION__ << " (" << __LINE__ << ") " + +using namespace std; + +struct sVdrFileEntry { + unsigned long long sSize; + unsigned long long sCumSize; + int sIdx; + + sVdrFileEntry () {}; + sVdrFileEntry (off_t s, off_t t, int i) + : sSize(s), sCumSize(t), sIdx(i) {}; +}; + +void HttpResourceStartThread(void* arg) { + cHttpResource* m = (cHttpResource*)arg; + m->threadLoop(); + delete m; + pthread_exit(NULL); +} + +// An HTTP resource has two states: Either Read data or write data. +// In read data (until the header and the full body is received), there is no data to write +// in Write data, there is no data to read +// The only think for both states is to watch the socket close state. +cHttpResource::cHttpResource(int f, int id,string addr, int port): mServerAddr(addr), mServerPort(port), mFd(f), mReqId(id), mConnected(true), mConnState(WAITING), + mMethod(), mDataBuffer(NULL), mBlkData(false), mBlkPos(0), mBlkLen(0), mPath(), mVersion(), protocol(), + mAcceptRanges(true), rangeHdr(), mFileSize(-1), mRemLength(0) { + + mLog = Log::getInstance(); + + *(mLog->log()) << DEBUGPREFIX + << " ------- Hello ------- " + << DEBUGHDR<< endl; + + pthread_mutex_init(&mSendLock, NULL); + mDataBuffer = new char[MAXLEN]; +} + + +cHttpResource::~cHttpResource() { + *(mLog->log())<< DEBUGPREFIX + << " ------- Bye ----- " + << " mConnState= " << getConnStateName() + << DEBUGHDR << endl; + delete mDataBuffer; +} + +void cHttpResource::setNonBlocking() { + *(mLog->log())<< DEBUGPREFIX + << " Set Socket to non-blocking" + << DEBUGHDR << endl; + int oldflags = fcntl(mFd, F_GETFL, 0); + oldflags |= O_NONBLOCK; + fcntl(mFd, F_SETFL, oldflags); +} + +int cHttpResource::run() { + if (pthread_create(&mThreadId, NULL, (void*(*)(void*))HttpResourceStartThread, (void *)this) == -1) + return 0; + return 1; +} + +void cHttpResource::threadLoop() { + *(mLog->log())<< DEBUGPREFIX + << " Thread Started" + << DEBUGHDR << endl; + + // The default is to read one HTTP request and then close... + + fd_set read_state; + int maxfd; + struct timeval waitd; + FD_ZERO(&read_state); + FD_SET(mFd, &read_state); + maxfd = mFd; + + for (;;) { + fd_set readfds; + int ret; + + waitd.tv_sec = 1; // Make select wait up to 1 second for data + waitd.tv_usec = 0; // and 0 milliseconds. + + readfds = read_state; + ret = select(maxfd + 1, &readfds, NULL, NULL, &waitd); + if ((ret < 0) && (errno == EINTR)) { + *(mLog->log())<< DEBUGPREFIX + << " Error: " << strerror(errno) + << DEBUGHDR << endl; + continue; + } + + // Check for data on already accepted connections + if (FD_ISSET(mFd, &readfds)) { + *(mLog->log())<< endl; + *(mLog->log())<< DEBUGPREFIX + << " Request Received from Client fd= "<< mFd + << DEBUGHDR << endl; + + ret = readFromClient(); + if (ret <0) { + *(mLog->log())<< DEBUGPREFIX + << " Closing Connection" + << DEBUGHDR + << endl; + close(mFd); + break; + } + } + } // for (;;) + + *(mLog->log())<< DEBUGPREFIX + << " Left loop to terminate fd= " << mFd + << DEBUGHDR << endl; +} + +int cHttpResource::readFromClient() { + // int ret =0; + struct stat statbuf; + int ret = OKAY; + + *(mLog->log())<< DEBUGPREFIX + << DEBUGHDR << endl; + + if (mConnState != WAITING) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: State is not WAITING" + << DEBUGHDR << endl; + return OKAY; + } + + processHttpHeaderNew(); + *(mLog->log())<< " mPath= " << mPath << endl; + // << " mPath(utf8)= " << iso8859ToUtf8(mPath) + + setNonBlocking(); + mConnState = SERVING; + + // done - look for range header + if (strcasecmp(mMethod.c_str(), "GET") != 0){ + sendError(501, "Not supported", NULL, "Method is not supported."); + return ERROR; + } + + if (mPath.compare("/recordings.html") == 0) { + *(mLog->log())<< DEBUGPREFIX + << "generating /recordings.html" + << DEBUGHDR << endl; + + ret = sendRecordingsHtml( &statbuf); + return ERROR; + } + if (mPath.compare("/recordings.xml") == 0) { + *(mLog->log())<< DEBUGPREFIX + << "generating /recordings.xml" + << DEBUGHDR << endl; + + ret = sendRecordingsXml( &statbuf); + return ERROR; + } + + if (stat(mPath.c_str(), &statbuf) < 0) { + sendError(404, "Not Found", NULL, "File not found."); + return ERROR; + } + else { + if (S_ISDIR(statbuf.st_mode)) { + if (mPath.find(".rec") != string::npos) { + ret = sendVdrDir( &statbuf); + } + else { + sendDir( &statbuf); + } + } + else { + printf ("file send\n"); + mFileSize = statbuf.st_size; + ret = sendFirstChunk(&statbuf); + } + } + if (mRemLength <=0) + ret = ERROR; + if (mConnState == TOCLOSE) + ret = ERROR; + + return ret; +} + +void cHttpResource::sendError(int status, const char *title, const char *extra, const char *text) { + char f[400]; + + mConnState = TOCLOSE; + string hdr = ""; + sendHeaders(status, title, extra, "text/html", -1, -1); + snprintf(f, sizeof(f), "%d %s\r\n", status, title); + hdr += f; + snprintf(f, sizeof(f), "

%d %s

\r\n", status, title); + hdr += f; + snprintf(f, sizeof(f), "%s\r\n", text); + hdr += f; + snprintf(f, sizeof(f), "\r\n"); + hdr += f; + + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return; + } +} + + +int cHttpResource::sendDir(struct stat *statbuf) { + char pathbuf[4096]; + char f[400]; + int len; + + mConnState = TOCLOSE; + *(mLog->log())<< "sendDir:" << endl; + *(mLog->log())<< "path= " << mPath << endl; + len = mPath.length(); + int ret = OKAY; + + if (len == 0 || mPath[len - 1] != '/') { + snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mPath.c_str()); + sendError(302, "Found", pathbuf, "Directories must end with a slash."); + ret = ERROR; + } + else { + snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mPath.c_str()); + if (stat(pathbuf, statbuf) >= 0) { + mPath = pathbuf; + sendFirstChunk(statbuf); + // sendFile(pathbuf, statbuf); // found an index.html file in the directory + } + else { + DIR *dir; + struct dirent *de; + + sendHeaders(200, "OK", NULL, "text/html", -1, statbuf->st_mtime); + + string hdr = ""; + snprintf(f, sizeof(f), "Index of %s\r\n", mPath.c_str()); + hdr += f; + snprintf(f, sizeof(f), "

Index of %s

\r\n
\n", mPath.c_str());
+      hdr += f;
+      snprintf(f, sizeof(f), "Name                             Last Modified              Size\r\n");
+      hdr += f;
+      snprintf(f, sizeof(f), "
\r\n"); + hdr += f; + + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + + if (len > 1) { + snprintf(f, sizeof(f), "..\r\n"); + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + } + + dir = opendir(mPath.c_str()); + while ((de = readdir(dir)) != NULL) { + char timebuf[32]; + struct tm *tm; + strcpy(pathbuf, mPath.c_str()); + printf (" -Entry: %s\n", de->d_name); + strcat(pathbuf, de->d_name); + + stat(pathbuf, statbuf); + tm = gmtime(&(statbuf->st_mtime)); + strftime(timebuf, sizeof(timebuf), "%d-%b-%Y %H:%M:%S", tm); + + hdr = ""; + snprintf(f, sizeof(f), "", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : ""); + hdr += f; + + snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : " "); + hdr += f; + + if (strlen(de->d_name) < 32) { + snprintf(f, sizeof(f), "%*s", 32 - strlen(de->d_name), ""); + hdr += f; + } + if (S_ISDIR(statbuf->st_mode)) { + snprintf(f, sizeof(f), "%s\r\n", timebuf); + hdr += f; + } + else { + snprintf(f, sizeof(f), "%s\r\n", timebuf); + hdr += f; + } + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + } + closedir(dir); + *(mLog->log())<< DEBUGPREFIX + << "Done" + << DEBUGHDR << endl; + + snprintf(f, sizeof(f), "
\r\n
\r\n
%s
\r\n\r\n", SERVER); + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + } + } + if (mRemLength != 0) + *(mLog->log())<< " WARNING: mRemLength not zero" << endl; + mRemLength = 0; + return ret; +} + +string cHttpResource::removeEtChar(string line) { + bool done = false; + size_t cur_pos = 0; + size_t pos = 0; + string res = ""; + + int end_after_done = 0; + + while (!done) { + pos = line.find('&', cur_pos); + if (pos == string::npos) { + done = true; + res += line.substr(cur_pos); + break; + } + if (pos >= 0) { + res += line.substr(cur_pos, (pos-cur_pos)) + "&"; + // cur_pos = cur_pos+ pos +1; + cur_pos = pos +1; + end_after_done ++; + } + } + if (end_after_done != 0) { + *(mLog->log())<< "removeEtChar" << " line= " << line; + *(mLog->log())<< " res= " << res << " occurances= " << end_after_done << endl; + } + return res; +} + +int cHttpResource::sendRecordingsXml(struct stat *statbuf) { + sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "\n"; + // hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + + char buff[20]; + char f[400]; + cRecordings Recordings; + Recordings.Load(); + + int count = 0; + for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { + hdr = ""; + if (recording->HierarchyLevels() == 0) { + if (++count != 11) + continue; + hdr += "\n"; + strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&recording->start)); + hdr += ""; + snprintf(f, sizeof(f), "%s - %s", buff, removeEtChar(recording->Name()).c_str()); + hdr += f; + hdr += "\n"; + hdr += ""; + snprintf(f, sizeof(f), "http://%s:%d%s/", mServerAddr.c_str(), mServerPort, removeEtChar(recording->FileName()).c_str()); + hdr += f; + hdr += "\n"; + hdr += ""; + // TODO Title + const cRecordingInfo* info = recording->Info(); + // snprintf(f, sizeof(f), "title= %s\n", info->Title()); + // hdr += f; + hdr += "Hallo"; + hdr += "HexDump title= \n" + hexDump(recording->Name()); + hdr += "\nHexDump link= \n" + hexDump(recording->FileName()) + "\n"; + *(mLog->log())<< DEBUGPREFIX + << " *** " + << " HexDump title= \n" << hexDump(recording->Name()) << endl + << " HexDump link= \n" << hexDump(recording->FileName()) << endl + << DEBUGHDR + << endl; + + // snprintf(f, sizeof(f), "short= %s\n", info->ShortText()); + // hdr += f; + // snprintf(f, sizeof(f), "desc= %s\n", info->Description()); + // hdr += f; + + hdr += "\n"; + hdr += "\n"; + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + } + // start is time_t + } + + hdr += "\n"; + hdr += "\n"; + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + + return ERROR; +} +int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { + sendHeaders(200, "OK", NULL, "text/html", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "Recordings\r\n"; + hdr += "\r\n"; + hdr += ""; + hdr += "

Recordings

\r\n
\n";
+  hdr += "
\r\n"; + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + + char buff[20]; + char f[400]; + cRecordings Recordings; + Recordings.Load(); + for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { + hdr = ""; + strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&recording->start)); + snprintf(f, sizeof(f), "%s - %d %s\r\n", buff, recording->HierarchyLevels(), recording->FileName(), recording->Name()); + hdr += f; + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return ERROR; + } + // start is time_t + } + return ERROR; +} + +int cHttpResource::sendVdrDir(struct stat *statbuf) { + *(mLog->log())<< DEBUGPREFIX + << " *** " + << DEBUGHDR + << endl; + char pathbuf[4096]; + char f[400]; + int vdr_idx = 0; + off_t total_file_size = 0; + int ret = OKAY; + + string vdr_dir = mPath; + vector file_sizes; + bool more_to_go = true; + + while (more_to_go) { + vdr_idx ++; + snprintf(pathbuf, sizeof(pathbuf), "%s%03d.vdr", mPath.c_str(), vdr_idx); + if (stat(pathbuf, statbuf) >= 0) { + *(mLog->log())<< " found for " << pathbuf << endl; + file_sizes.push_back(sVdrFileEntry(statbuf->st_size, total_file_size, vdr_idx)); + total_file_size += statbuf->st_size; + } + else { + more_to_go = false; + } + } + if (file_sizes.size() < 1) { + // There seems to be vdr video file in the directory + *(mLog->log())<< DEBUGPREFIX + << " No video file in the directory" + << DEBUGHDR << endl; + sendError(404, "Not Found", NULL, "File not found."); + ret = ERROR; + return ret; + } + *(mLog->log())<< DEBUGPREFIX + << " vdr filesize list " + << DEBUGHDR << endl; + + for (uint i = 0; i < file_sizes.size(); i++) + *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " tSize= " << file_sizes[i].sCumSize << endl; + // *(mLog->log())<< endl; + *(mLog->log())<< " total_file_size= " << total_file_size << endl; + + uint cur_idx = 0; + + if (!rangeHdr.isRangeRequest) { + snprintf(pathbuf, sizeof(pathbuf), "%s%03d.vdr", mPath.c_str(), file_sizes[cur_idx].sIdx); + + if (openFile(pathbuf) != OKAY) + return ERROR; + + mRemLength = total_file_size; + sendHeaders(200, "OK", NULL, "video/mpeg", total_file_size, statbuf->st_mtime); + } + else { // Range request + // idenify the first file + *(mLog->log())<< DEBUGPREFIX + << " Range Request Handling" + << DEBUGHDR << endl; + + cur_idx = file_sizes.size() -1; + for (uint i = 0; i < file_sizes.size(); i++) { + if (file_sizes[i].sCumSize > rangeHdr.begin) { + cur_idx = i -1; + break; + } + } + *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sCumSize= " + << file_sizes[cur_idx].sCumSize << " rangeHdr.begin= " << rangeHdr.begin + << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; + snprintf(pathbuf, sizeof(pathbuf), "%s%03d.vdr", mPath.c_str(), file_sizes[cur_idx].sIdx); + *(mLog->log())<< " file identified= " << pathbuf << endl; + if (openFile(pathbuf) != OKAY) + return ERROR; + + mPath = pathbuf; + *(mLog->log())<< " Seeking into file= " << (rangeHdr.begin - file_sizes[cur_idx].sCumSize) + << " cur_idx= " << cur_idx + << " file_sizes[cur_idx].sCumSize= " << file_sizes[cur_idx].sCumSize + << endl; + + fseek(mFile, (rangeHdr.begin - file_sizes[cur_idx].sCumSize), SEEK_SET); + if (rangeHdr.end == 0) + rangeHdr.end = total_file_size; + mRemLength = (rangeHdr.end-rangeHdr.begin); + snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), total_file_size); + sendHeaders(206, "Partial Content", f, "video/mpeg", total_file_size, statbuf->st_mtime); + } + + *(mLog->log())<< " ***** Yes, vdr dir found ***** mPath= " << mPath<< endl; + + while (cur_idx < file_sizes.size()) { + ret = sendDataChunk(); + fclose(mFile); + + if (ret == ERROR) { + /* *(mLog->log())<< DEBUGPREFIX + << " Error, returning" + << DEBUGHDR << endl; + */ + return ERROR; + break; + } + cur_idx ++; + if (cur_idx == file_sizes.size()) { + *(mLog->log())<< DEBUGPREFIX + << " Done " + << DEBUGHDR << endl; + break; + } + if (ret == OKAY) { + snprintf(pathbuf, sizeof(pathbuf), "%s%03d.vdr", mPath.c_str(), file_sizes[cur_idx].sIdx); + *(mLog->log())<< " Next File pathbuf= " << pathbuf << endl; + if (openFile(pathbuf) != OKAY) + return ERROR; + } + } + return ret; +} + +void cHttpResource::sendHeaders(int status, const char *title, const char *extra, const char *mime, + off_t length, time_t date) { + + time_t now; + char timebuf[128]; + char f[400]; + + string hdr = ""; + snprintf(f, sizeof(f), "%s %d %s\r\n", PROTOCOL, status, title); + hdr += f; + snprintf(f, sizeof(f), "Server: %s\r\n", SERVER); + hdr += f; + now = time(NULL); + strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&now)); + snprintf(f, sizeof(f), "Date: %s\r\n", timebuf); + hdr += f; + if (extra) { + snprintf(f, sizeof(f), "%s\r\n", extra); + hdr += f; + } + if (mime) { + snprintf(f, sizeof(f), "Content-Type: %s\r\n", mime); + hdr += f; + } + if (length >= 0) { + snprintf(f, sizeof(f), "Content-Length: %lld\r\n", length); + *(mLog->log())<< DEBUGPREFIX + << " length= " << length + << DEBUGHDR << endl; + hdr += f; + } + if (date != -1) { + strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date)); + snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf); + hdr += f; + } + if (mAcceptRanges) { + snprintf(f, sizeof(f), "Accept-Ranges: bytes\r\n"); + hdr += f; + } + snprintf(f, sizeof(f), "Connection: close\r\n"); + hdr += f; + + snprintf(f, sizeof(f), "\r\n"); + hdr += f; + if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { + return; + } + +} + +int cHttpResource::sendFirstChunk(struct stat *statbuf) { + // Send the First Datachunk, incl all headers + + *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId << " mPath= " << mPath + << DEBUGHDR + << endl; + + char f[400]; + + if (openFile(mPath.c_str()) == ERROR) + return ERROR; + mFile = fopen(mPath.c_str(), "r"); + int ret = OKAY; + + if (!mFile) { + sendError(403, "Forbidden", NULL, "Access denied."); + ret = ERROR; + } + else { + mFileSize = S_ISREG(statbuf->st_mode) ? statbuf->st_size : -1; + + if (!rangeHdr.isRangeRequest) { + mRemLength = mFileSize; + sendHeaders(200, "OK", NULL, getMimeType(mPath.c_str()), mFileSize, statbuf->st_mtime); + } + else { // Range request + fseek(mFile, rangeHdr.begin, SEEK_SET); + if (rangeHdr.end == 0) + rangeHdr.end = mFileSize; + mRemLength = (rangeHdr.end-rangeHdr.begin); + snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), mFileSize); + sendHeaders(206, "Partial Content", f, getMimeType(mPath.c_str()), (rangeHdr.end-rangeHdr.begin), statbuf->st_mtime); + } + + *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId + << ": Done mRemLength= "<< mRemLength << " ret= " << ret + << DEBUGHDR << endl; + + ret = sendDataChunk(); + *(mLog->log())<< " Close File" << endl; + fclose(mFile); + } + return ret; + +} + +int cHttpResource::sendDataChunk() { + // the the paload of an open file + *(mLog->log())<< DEBUGPREFIX + << " mConnState= " << getConnStateName() << " mRemLength= "<< mRemLength + << DEBUGHDR << endl; + + int n; + int chunk_no =0; + int bytes_to_send = 0; + + int ret = 0; + + char buf[MAXLEN]; + int buflen = sizeof(buf); + + if (!mConnected) + return ERROR; + + if (mConnState == WAITING) { + *(mLog->log())<< "fd= " << mFd + << " Something wrong: Should not be here" + << DEBUGHDR << endl; + return OKAY; + } + if (mRemLength ==0) { + *(mLog->log())<< " closing connection" + << DEBUGHDR << endl; + mConnected = false; + close (mFd); + return ERROR; + } + if (mConnState == TOCLOSE) { + *(mLog->log())<< DEBUGPREFIX + << " closing connection" + << DEBUGHDR << endl; + mConnected = false; + close (mFd); + return ERROR; + } + + bool done = false; + while (!done) { + // Check whethere there is no other data to send (from last time) + if (mRemLength == 0) { + *(mLog->log())<< DEBUGPREFIX + << " mRemLength == 0 --> closing connection" + << DEBUGHDR << endl; + mConnected = false; + close (mFd); + return ERROR; + + } + if (mRemLength >= buflen) { + bytes_to_send = buflen; + } + else + bytes_to_send = mRemLength; + + n = fread(buf, 1, bytes_to_send, mFile); + if (n != bytes_to_send) { + *(mLog->log())<< DEBUGPREFIX + << " -- Something wrong here - n= " << n << " bytes_to_send= " << bytes_to_send + << DEBUGHDR << endl; + done = true; + } + + ret = writeToClient( buf, bytes_to_send); + if (ret == ERROR) { + *(mLog->log())<< DEBUGPREFIX + << " Stopping - Client closed connection " + << DEBUGHDR << endl; + + // socket had blocket. wait until select comes back. + done = true; + // fclose(mFile); + ret = ERROR; + break; + } + + mRemLength -= bytes_to_send; + chunk_no ++; + // *(mLog->log())<< " chunk_no= " << chunk_no << endl; + } + + return ret; +} + +int cHttpResource::writeToClient(const char *buf, size_t buflen) { + // *(mLog->log())<< __PRETTY_FUNCTION__ << " (" << __LINE__ << "): " + // << "fd= " << mFd << " mReqId=" << mReqId << " mConnected= " << ((mConnected)? "true":"false") + // << " buflen= " << buflen << " mRemLength= "<< mRemLength << endl; + + unsigned int bytes_written = 0; + int this_write; + int retries = 0; + + struct timeval timeout; + + int ret = 0; + fd_set write_set; + + FD_ZERO(&write_set); + + pthread_mutex_lock(&mSendLock); + + if (!mConnected) { + *(mLog->log())<< DEBUGPREFIX + << " not connected anymore" + DEBUGHDR << endl; + pthread_mutex_unlock(&mSendLock); + return ERROR; + } + + if (mConnState == WAITING) { + *(mLog->log())<< DEBUGPREFIX + << " Should not be in WAITING state" + << DEBUGHDR << endl; + pthread_mutex_unlock(&mSendLock); + return OKAY; + } + + bool done = false; + while (!done) { + FD_ZERO(&write_set); + FD_SET(mFd, &write_set); + timeout.tv_sec = 10; + timeout.tv_usec = 0; + ret = select(mFd + 1, NULL, &write_set, NULL, NULL); + if (ret < 1) { + *(mLog->log())<< DEBUGPREFIX + << " Select returned error -- Closing connection" + << DEBUGHDR << endl; + mConnected = false; + pthread_mutex_unlock(&mSendLock); + close(mFd); + return ERROR; // error, or timeout + } + if (ret == 0) { + *(mLog->log())<< DEBUGPREFIX + << " Select returned ZERO -- Closing connection" + << DEBUGHDR << endl; + mConnected = false; + pthread_mutex_unlock(&mSendLock); + close(mFd); + return ERROR; // error, or timeout + } + this_write = write(mFd, &buf[bytes_written], buflen - bytes_written); + if (this_write <=0) { + /* *(mLog->log())<< DEBUGPREFIX + << " ERROR: Stopped (Client terminated Connection)" + << DEBUGHDR << endl; + */ + mConnected = false; + close(mFd); + pthread_mutex_unlock(&mSendLock); + return ERROR; + } + bytes_written += this_write; + + if (bytes_written == buflen) { + done = true; + break; + } + else { + if (++retries == 100) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Too many retries " + << DEBUGHDR << endl; + mConnected = false; + close(mFd); + pthread_mutex_unlock(&mSendLock); + return ERROR; + } + } + } + // *(mLog->log())<< __PRETTY_FUNCTION__ << " (" << __LINE__ << "): " + // << " done "<< endl; + // Done with writing + pthread_mutex_unlock(&mSendLock); + + return OKAY; +} + + + + +char *cHttpResource::getMimeType(const char *name) { + char *ext = strrchr((char*)name, '.'); + if (!ext) + return NULL; + // if (ext.compare(".html") || ext.compare(".htm")) return "text/html"; + if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; + if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; + if (strcmp(ext, ".gif") == 0) return "image/gif"; + if (strcmp(ext, ".png") == 0) return "image/png"; + if (strcmp(ext, ".css") == 0) return "text/css"; + if (strcmp(ext, ".au") == 0) return "audio/basic"; + if (strcmp(ext, ".wav") == 0) return "audio/wav"; + if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; + if (strcmp(ext, ".mp4") == 0) return "video/mp4"; + if (strcmp(ext, ".vdr") == 0) return "video/mpeg"; + if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; + if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; + return NULL; +} + +int cHttpResource::processHttpHeaderNew() { + *(mLog->log())<< DEBUGPREFIX + << " processHttpHeaderNew " + << DEBUGHDR << endl; + + char buf[MAXLEN]; + int buflen = sizeof(buf); + + int line_count = 0; + bool hdr_end_found = false; + bool is_req = true; + // block until the entire request header is read + string rem_hdr = ""; + + while (!hdr_end_found) { + int count = 0; + while ((buflen = read(mFd, buf, sizeof(buf))) == -1) + count ++; + if (count != 0) + *(mLog->log())<< " Blocked for " << count << " Iterations " << endl; + //FIXME. Better return and wait. + if (buflen == -1) { + *(mLog->log())<< " Some Error" << endl; + return 2; // Nothing to read + } + #ifdef DEBUG_REQHEADERS + *(mLog->log())<< " Read " << buflen << " Bytes from " << fd << endl; + #endif + string req_line = rem_hdr + buf; + if (rem_hdr.size() != 0) { + *(mLog->log())<< DEBUGPREFIX + << " rem_hdr.size() = " << rem_hdr.size() + << DEBUGHDR << endl; + } + buflen += rem_hdr.size(); + + size_t last_pos = 0; + while (true) { + line_count ++; + size_t pos = req_line.find ("\r\n", last_pos); + if (pos > buflen) { + *(mLog->log())<< DEBUGPREFIX + << " Pos (" << pos << ") outside of read buffer" + << DEBUGHDR << endl; + rem_hdr = req_line.substr(last_pos, buflen - last_pos); + *(mLog->log())<< DEBUGPREFIX + << " No HdrEnd Found, read more data. rem_hdr= " << rem_hdr.size() + << DEBUGHDR << endl; + break; + } + if ((last_pos - pos) == 0) { + *(mLog->log())<< DEBUGPREFIX + << " Header End Found" + << DEBUGHDR << endl; + hdr_end_found = true; + break; + } + + if (pos == string::npos){ + // not found + rem_hdr = req_line.substr(last_pos, buflen - last_pos); + *(mLog->log())<< DEBUGPREFIX + << " No HdrEnd Found, read more data. rem_hdr= " << rem_hdr.size() + << DEBUGHDR << endl; + break; + } + + string line = req_line.substr(last_pos, (pos-last_pos)); + +#ifdef DEBUG_REQHEADERS + *(mLog->log())<< " Line= " << line << endl; +#endif + last_pos = pos +2; + if (is_req) { + is_req = false; + // Parse the request line + mMethod = line.substr(0, line.find_first_of(" ")); + mPath = line.substr(line.find_first_of(" ") +1, (line.find_last_of(" ") - line.find_first_of(" ") -1)); + mVersion = line.substr(line.find_last_of(" ") +1); + + *(mLog->log())<< DEBUGPREFIX + << " ReqLine= " << line << endl; + *(mLog->log())<< DEBUGPREFIX + << " mMethod= " << mMethod + << " mPath= " << mPath + // << " mPath(utf8)= " << iso8859ToUtf8(mPath) + << " mVer= " << mVersion + << " HexDump= " << endl << hexDump(mPath) + << DEBUGHDR << endl; + } + else { + string hdr_name = line.substr(0, line.find_first_of(":")); + string hdr_val = line.substr(line.find_first_of(":") +2); + if (hdr_name.compare("Range") == 0) { + parseRangeHeaderValue(hdr_val); + *(mLog->log())<< " Range: Begin= " << rangeHdr.begin + << " End= " << rangeHdr.end + << endl; + } + if (hdr_name.compare("User-Agent") == 0) { + // *(mLog->log())<< " ***" << hdr_name << endl; + *(mLog->log())<< " User-Agent: " << hdr_val + << endl; + } + + } + // *(mLog->log())<< " update last_pos= " << last_pos << endl;; + } + } + // exit(0); + return OKAY; +} + +string cHttpResource::getConnStateName() { + string state_string; + switch (mConnState) { + case WAITING: + state_string = "WAITING"; + break; + case SERVING: + state_string = "SERVING"; + break; + case TOCLOSE: + state_string = "TOCLOSE"; + break; + default: + state_string = "UNKNOWN"; + break; + } + return state_string; +} + +int cHttpResource::parseRangeHeaderValue(string val) { + rangeHdr.isRangeRequest = true; + size_t pos_equal = val.find_first_of('='); + size_t pos_minus = val.find_first_of('-'); + + string range_type = val.substr(0, pos_equal); + string first_val= val.substr(pos_equal+1, pos_minus -(pos_equal+1)); + rangeHdr.begin = atoll(first_val.c_str()); + + string sec_val = ""; + if ((pos_minus +1)< val.size()){ + sec_val = val.substr(pos_minus+1); + rangeHdr.end = atoll(sec_val.c_str()); + } + // *(mLog->log())<< " RangeType= (" << range_type + // << ") Begin= " << rangeHdr.begin + // << " End= " << rangeHdr.end + // << endl; + // *(mLog->log())<< " equal= " << equal << " minus= " << minus << " size= " << hdr_val.size() << endl; + +} + +int cHttpResource::openFile(const char *name) { + mFile = fopen(name, "r"); + if (!mFile) { + *(mLog->log())<< DEBUGPREFIX + << " fopen failed pathbuf= " << name + << DEBUGHDR << endl; + sendError(403, "Forbidden", NULL, "Access denied."); + return ERROR; + } + return OKAY; +} + +string cHttpResource::hexDump(string in) { + string res = ""; + string ascii = ""; + char buf[10]; + + int line_count = 0; + for (uint i = 0; i < in.size(); i++) { + unsigned char num = in[i]; + sprintf (buf, "%02hhX", num); + if ((num >= 32) && (num < 127)) { + ascii += char(num); + } + else + ascii += '.'; + res += buf; + + line_count++; + switch (line_count) { + case 8: + res += " "; + ascii += " "; + break; + case 16: + res += " " + ascii; + res += "\r\n"; + ascii = ""; + line_count = 0; + break; + default: + res += " "; + break; + } + } + if (line_count != 0) { + for (int i = 0; i < ((16 - line_count) * 3 ); i++) + res += " "; + if (line_count >= 8) + res += " "; + res += ascii; + } + return res; +} + + +string iso8859ToUtf8 (string input) { + string res = ""; + + /* for (uint i = 0; i < input.size(); i++) { + unsigned char num = input[i]; + if (num < 128) + res += char(num); + else { + // res += char(0xc2 + (num > 0xbf)); + // res += char((num & 0x3f) +0x80); + } + + }*/ + + return res; + // unsigned char *in, *out; + // while (*in) + // if (*in<128) *out++=*in++; + // else *out++=0xc2+(*in>0xbf), *out++=(*in++&0x3f)+0x80; + + } diff --git a/vdr-smarttvweb/httpresource-hmm.h b/vdr-smarttvweb/httpresource-hmm.h new file mode 100644 index 0000000..543377d --- /dev/null +++ b/vdr-smarttvweb/httpresource-hmm.h @@ -0,0 +1,94 @@ + +#ifndef __HTTPREQUEST_H__ +#define __HTTPREQUEST_H__ + +#include +#include +#include +#include "log.h" + +using namespace std; + +struct cRange { +cRange(): isRangeRequest(false), begin(0), end(0) {}; + bool isRangeRequest; + unsigned long long begin; + unsigned long long end; +}; + +enum eConnState { + WAITING, + SERVING, + TOCLOSE +}; + +class cHttpResource { + + public: + cHttpResource(int, int, string, int); + virtual ~cHttpResource(); + + int readFromClient(); + // int sendNextChunk(); + void threadLoop(); + int run(); + + private: + Log* mLog; + pthread_t mThreadId; + pthread_mutex_t mSendLock; + string mServerAddr; + int mServerPort; + int mFd; + int mReqId; + + bool mConnected; + eConnState mConnState; + string mMethod; + char *mDataBuffer; + bool mBlkData; + int mBlkPos; + int mBlkLen; + + // string path; + string mPath; + string mVersion; + string protocol; + + bool mAcceptRanges; + cRange rangeHdr; + unsigned long long mFileSize; + uint mRemLength; + FILE *mFile; + + + // int tcpServerWrite(const char buf[], int buflen); + int writeToClient(const char *buf, size_t buflen); + int sendDataChunk(); + + void setNonBlocking(); + + int processHttpHeaderNew(); + // int processHttpHeader(); + void sendError(int status, const char *title, const char *extra, const char *text); + int sendDir(struct stat *statbuf); + int sendVdrDir(struct stat *statbuf); + int sendRecordingsHtml (struct stat *statbuf); + int sendRecordingsXml (struct stat *statbuf); + string removeEtChar(string line); + + void sendHeaders(int status, const char *title, const char *extra, const char *mime, + off_t length, time_t date); + + int sendFirstChunk(struct stat *statbuf); + + // Helper Functions + char *getMimeType(const char *name); + string getConnStateName(); + int parseRangeHeaderValue(string); + int openFile(const char *name); + string hexDump(string in); + string iso8859ToUtf8 (string); + +}; +#endif diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c new file mode 100644 index 0000000..c10721b --- /dev/null +++ b/vdr-smarttvweb/httpresource.c @@ -0,0 +1,2122 @@ +/* + * httpresource.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "httpresource.h" +#include "smarttvfactory.h" +#include "stvw_cfg.h" + +#include "url.h" + +#ifndef STANDALONE +#include +#include +#include +#include +#include +#endif + +#define SERVER "SmartTvWeb/0.1" +#define PROTOCOL "HTTP/1.1" +#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" + +#define MAXLEN 4096 +#define OKAY 0 +#define ERROR (-1) +#define DEBUG_REGHEADERS +#define DEBUGPREFIX "mReqId= " << mReqId << " fd= " << mFd +#define DEBUGHDR " " << __PRETTY_FUNCTION__ << " (" << __LINE__ << ") " + +#define DEBUG + +#define SEGMENT_DURATION 10 + +using namespace std; + + +struct sVdrFileEntry { + unsigned long long sSize; + unsigned long long sFirstOffset; + int sIdx; + + sVdrFileEntry () {}; + sVdrFileEntry (off_t s, off_t t, int i) + : sSize(s), sFirstOffset(t), sIdx(i) {}; +}; + + +struct sTimerEntry { + string name; + time_t startTime; + int duration; +sTimerEntry(string t, time_t s, int d) : name(t), startTime(s), duration(d) {}; +}; + +// 8 Byte per entry +struct tIndexTs { + uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!) + int reserved:7; // reserved for future use + int independent:1; // marks frames that can be displayed by themselves (for trick modes) + uint16_t number:16; // up to 64K files per recording + }; + +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), + 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), + mPath(), mVersion(), protocol(), mReqContentLength(0), + mPayload(), mUserAgent(), + mAcceptRanges(true), rangeHdr(), mFileSize(-1), mRemLength(0), mFile(NULL), mVdrIdx(1), mFileStructure(), + mIsRecording(false), mRecProgress(0.0) { + + mLog = Log::getInstance(); + mPath = ""; + mConnTime = time(NULL); + setNonBlocking(); + mBlkData = new char[MAXLEN]; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " cHttpResource created" << endl; +#endif +} + + +cHttpResource::~cHttpResource() { +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " Destructor of cHttpResource called" + << endl; +#endif + delete[] mBlkData; + if (mFile != NULL) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: mFile still open. Closing now..." << endl; + fclose(mFile); + mFile = NULL; + } + +} + +int cHttpResource::checkStatus() { + time_t now = time(NULL); + + switch (mConnState) { + case WAITING: + case READHDR: + case READPAYLOAD: + if (now - mConnTime > 2) { + *(mLog->log()) << DEBUGPREFIX + << " checkStatus: no activity for 2sec " + << "mmConnState= " << getConnStateName() + << endl; + return ERROR; + } + break; + case TOCLOSE: + return ERROR; + break; + } + + // check for how much time the + return OKAY; +} + +void cHttpResource::setNonBlocking() { + int oldflags = fcntl(mFd, F_GETFL, 0); + oldflags |= O_NONBLOCK; + fcntl(mFd, F_SETFL, oldflags); +} + + +int cHttpResource::handleRead() { + mHandleReadCount ++; + if (mConnState == SERVING) { + *(mLog->log())<< DEBUGPREFIX + << " handleRead() in wrong state= " << getConnStateName() + << endl; + return OKAY; + } + if (mConnState == TOCLOSE) { + *(mLog->log())<< DEBUGPREFIX + << " handleRead() in wrong state= " << getConnStateName() + << endl; + return ERROR; + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " handleRead() state= " << getConnStateName() + << endl; +#endif + + char buf[MAXLEN]; + int buflen = sizeof(buf); + + int line_count = 0; + bool is_req = true; + string rem_hdr = ""; + + buflen = read(mFd, buf, sizeof(buf)); + + if (buflen == -1) { + *(mLog->log())<< " Some Error, no data received" << endl; + 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 + } + + if ( ((mConnState == READHDR) or (mConnState == READPAYLOAD)) and (mHandleReadCount > 5000)) { + *(mLog->log()) << DEBUGPREFIX << " ERROR Still not finished after mHandleReadCount= " << mHandleReadCount << endl; + return ERROR; // Nothing to read + } + + if (buflen == 0) { + return OKAY; // Nothing to read + } + + if (mConnState == READPAYLOAD) { + mPayload += string(buf, buflen); + if (mPayload.size() == mReqContentLength) { + //Done + mConnState = SERVING; + return processRequest(); + } + } + + if (mConnState == WAITING) { + mConnState = READHDR; + } + + string req_line = mReadBuffer + string(buf, buflen); + buflen += rem_hdr.size(); + + size_t last_pos = 0; + // Parse http header lines + while (true) { + line_count ++; + size_t pos = req_line.find ("\r\n", last_pos); + + if ((pos > buflen) or (pos == string::npos)) { + mReadBuffer = req_line.substr(last_pos, buflen - last_pos); +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " No HdrEnd Found, read more data. rem_hdr= " << mReadBuffer.size() + << " buflen= " << buflen + << DEBUGHDR << endl; + *(mLog->log())<< cUrlEncode::hexDump(mReadBuffer) << endl; +#endif + return OKAY; + } + + if ((last_pos - pos) == 0) { + // Header End +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " ---- Header End Found" << DEBUGHDR << endl; +#endif + + if (mReqContentLength != 0) { + mConnState = READPAYLOAD; + mPayload = req_line.substr(pos +2, buflen - (pos +2)); + if (mPayload.size() != mReqContentLength) + return OKAY; + else { + mConnState = SERVING; + return processRequest(); + } + } // if(content_length != 0) + else { + mConnState = SERVING; + return processRequest(); + } + } // if (header end) + + string line = req_line.substr(last_pos, (pos-last_pos)); +#ifdef DEBUG_REQHEADERS + *(mLog->log())<< " Line= " << line << endl; +#endif + last_pos = pos +2; + if (mPath.size() == 0) { +#ifndef DEBUG + *(mLog->log())<< " parsing Request Line= " << line << endl; +#endif + is_req = false; + // Parse the request line + if (parseHttpRequestLine(line) != OKAY) { + return ERROR; + }; + } + else { + parseHttpHeaderLine (line); + } + } + return OKAY; +} + +int cHttpResource::processRequest() { + // do stuff based on the request and the query +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " processRequest for mPath= " << mPath << DEBUGHDR << endl; +#endif + struct stat statbuf; + int ret = OKAY; + + if (mMethod.compare("POST")==0) { + return handlePost(); + } + + if (strcasecmp(mMethod.c_str(), "GET") != 0){ + sendError(501, "Not supported", NULL, "Method is not supported."); + return ERROR; + } + +#ifndef STANDALONE + if (mPath.compare("/recordings.html") == 0) { +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /recordings.html" + << DEBUGHDR << endl; +#endif + ret = sendRecordingsHtml( &statbuf); + return OKAY; + } + if (mPath.compare("/recordings.xml") == 0) { + + ret = sendRecordingsXml( &statbuf); + return OKAY; + } + + if (mPath.compare("/channels.xml") == 0) { + ret = sendChannelsXml( &statbuf); + return OKAY; + } + + if (mPath.compare("/epg.xml") == 0) { + ret = sendEpgXml( &statbuf); + return OKAY; + } +#endif + if (mPath.compare("/media.xml") == 0) { + ret = sendMediaXml( &statbuf); + return OKAY; + } + + if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { + ret = sendManifest( &statbuf, false); + return OKAY; + } + + if (mPath.compare(mPath.size() -9, 9, "-seg.m3u8") == 0) { + ret = sendManifest( &statbuf, true); + return OKAY; + } + + if (mPath.compare(mPath.size() -7, 7, "-seg.ts") == 0) { + ret = sendMediaSegment( &statbuf); + return OKAY; + } + + if (mPath.compare("/widget.conf") == 0) { + mPath = mFactory->getConfigDir() + "/widget.conf"; + } + + if (stat(mPath.c_str(), &statbuf) < 0) { + sendError(404, "Not Found", NULL, "File not found."); + return ERROR; + } + + if (S_ISDIR(statbuf.st_mode)) { + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " processRequest: isDir - mPath: " << mPath.c_str() << endl; +#endif + + + if (mPath.size() >4) { + if (mPath.compare(mPath.size() - 4, 4, ".rec") == 0) { + mContentType = VDRDIR; + return sendVdrDir( &statbuf); + } + } + // else { + sendDir( &statbuf); + mContentType = MEMBLOCK; + return OKAY; + // } + } + else { +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " processRequest: file send\n"; +#endif + + mFileSize = statbuf.st_size; + + mContentType = SINGLEFILE; + + return sendFile(&statbuf); + } +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " processRequest: Not Handled SHOULD not be here\n"; +#endif + return ERROR; +} + + +int cHttpResource::handleWrite() { + + if (mConnState == TOCLOSE) { + *(mLog->log())<< DEBUGPREFIX + << " handleWrite() in wrong state= " << getConnStateName() + << endl; + return ERROR; + } + + if (mConnState != SERVING) { + return OKAY; + } + + if (mBlkLen == mBlkPos) { + // note the mBlk may be filled with header info first. + if (fillDataBlk() != OKAY) { + return ERROR; + } + } + + int this_write = write(mFd, &mBlkData[mBlkPos], mBlkLen - mBlkPos); + if (this_write <=0) { + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " ERROR after write: Stopped (Client terminated Connection)" + << " mBlkPos= " << mBlkPos << " mBlkLen= " << mBlkLen + << DEBUGHDR << endl; +#endif + mConnState = TOCLOSE; + mConnected = false; + return ERROR; + } + mBlkPos += this_write; + + return OKAY; +} + + +int cHttpResource::fillDataBlk() { + char pathbuf[4096]; + + mBlkPos = 0; + int to_read = 0; + + switch(mContentType) { + case NYD: + + break; + case VDRDIR: + // Range requests are assumed to be all open + if (mFile == NULL) { + *(mLog->log()) << DEBUGPREFIX << " no open file anymore " + << "--> Done " << endl; + return ERROR; + } + if (mRemLength == 0) { + *(mLog->log()) << DEBUGPREFIX << " mRemLength is zero " + << "--> Done " << endl; + fclose(mFile); + mFile = NULL; + return ERROR; + } + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + + mBlkLen = fread(mBlkData, 1, to_read, mFile); + mRemLength -= mBlkLen; + + if (mRemLength == 0) { + *(mLog->log()) << DEBUGPREFIX << " last Block read " + << "--> Almost Done " << endl; + return OKAY; + } + + if (mBlkLen != MAXLEN) { + fclose(mFile); + mFile = NULL; + mVdrIdx ++; + + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); + mPath = pathbuf; + if (openFile(pathbuf) != OKAY) { + *(mLog->log())<< DEBUGPREFIX << " Failed to open file= " << pathbuf << " mRemLength= " << mRemLength<< endl; + mFile = NULL; + if (mBlkLen == 0) { + *(mLog->log()) << DEBUGPREFIX << " mBlkLen is zero --> Done " << endl; + return ERROR; + } + else + *(mLog->log()) << DEBUGPREFIX << " Still data to send mBlkLen= " << mBlkLen <<" --> continue " << endl; + return OKAY; + } + + if (mBlkLen == 0) { + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + mBlkLen = fread(mBlkData, 1, to_read, mFile); + } + mRemLength -= mBlkLen; + } + break; + case SINGLEFILE: + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + mBlkLen = fread(mBlkData, 1, to_read, mFile); + mRemLength -= mBlkLen; + if (mBlkLen == 0) { + + // read until EOF + fclose(mFile); + mFile = NULL; + return ERROR; + } + break; + case MEMBLOCK: + int rem_len = mResponseMessage->size() - mResponseMessagePos; + if (rem_len == 0) { + *(mLog->log())<< DEBUGPREFIX + << " fillDataBlock: MEMBLOCK done" << endl; + delete mResponseMessage; + mResponseMessagePos = 0; + mConnState = TOCLOSE; + return ERROR; + } + if (rem_len > MAXLEN) + rem_len = MAXLEN; + + string sub_msg = mResponseMessage->substr(mResponseMessagePos, rem_len); + mResponseMessagePos += rem_len; + mBlkLen = sub_msg.size(); + memcpy(mBlkData, sub_msg.c_str(), rem_len); + break; + } + + return OKAY; +} + + +int cHttpResource::parseResume(cResumeEntry &entry, string &id) { + bool done = false; + size_t cur_pos = 0; + + bool have_devid = false; + bool have_title = false; + bool have_start = false; + bool have_resume = false; + + while (!done) { + size_t pos = mPayload.find('\n', cur_pos); + if (pos == string::npos) { + done = true; + continue; + } + size_t pos_col = mPayload.find(':', cur_pos); + string attr= mPayload.substr(cur_pos, (pos_col- cur_pos)); + string val = mPayload.substr(pos_col +1, (pos - pos_col-1)); + + if (attr== "devid") { + have_devid = true; + id = val; + } + else if (attr == "title") { + have_title = true; + entry.mTitle = val; + } + else if (attr == "start") { + have_start = true; + entry.mStartTime = atoi(val.c_str()); + } + else if (attr == "resume") { + have_resume = true; + entry.mResume = atoi(val.c_str()); + } + else { + *(mLog->log())<< DEBUGPREFIX + << " parseResume: ERROR: Unknown attr= " << attr + << " with val= " << val + << endl; + } + cur_pos = pos +1; + if (cur_pos >= mPayload.size()) + done= true; + } + if (have_resume && have_start && have_title && have_devid) + return OKAY; + else + return ERROR; +} + +int cHttpResource::handlePost() { + mConnState = SERVING; + mContentType = MEMBLOCK; + + // sent an empty response message with just the OK header + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + /* + Resume support: + Key for recordings: Title plus start time. Value: Current PlayTime in Sec + Structure: Either, the plugin reads the list from file when the first client connects + Question: How to get files into /var/lib/vdr/plugins + + First: Create a list of resumes (use vdr cList ). + Write the list to file + Read the list from file + + */ + + if (mPath.compare("/log") == 0) { + *(mLog->log())<< mPayload + << endl; + } + + if (mPath.compare("/resume") == 0) { + + string dev_id; + cResumeEntry entry; + if (parseResume(entry, dev_id) == ERROR) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR parsing resume" + << endl; + } + *(mLog->log())<< DEBUGPREFIX + << " Resume: id= " << dev_id + << " resume= " << entry << endl; + } + + sendHeaders(200, "OK", NULL, NULL, -1, -1); + + return OKAY; +} + + +void cHttpResource::sendError(int status, const char *title, const char *extra, const char *text) { + char f[400]; + + mConnState = SERVING; + mContentType = MEMBLOCK; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + string hdr = ""; + sendHeaders(status, title, extra, "text/html", -1, -1); + + snprintf(f, sizeof(f), "%d %s\r\n", status, title); + hdr += f; + snprintf(f, sizeof(f), "

%d %s

\r\n", status, title); + hdr += f; + snprintf(f, sizeof(f), "%s\r\n", text); + hdr += f; + snprintf(f, sizeof(f), "\r\n"); + hdr += f; + + + strcpy(&(mBlkData[mBlkLen]), hdr.c_str()); + mBlkLen += hdr.size(); + +} + + +int cHttpResource::sendDir(struct stat *statbuf) { + char pathbuf[4096]; + char f[400]; + int len; + + mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendDir: mPath= " << mPath << endl; +#endif + len = mPath.length(); + // int ret = OKAY; + + if (len == 0 || mPath[len - 1] != '/') { + snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mPath.c_str()); + sendError(302, "Found", pathbuf, "Directories must end with a slash."); + return OKAY; + } + + snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mPath.c_str()); + if (stat(pathbuf, statbuf) >= 0) { + mPath = pathbuf; + sendFile(statbuf); + } + else { +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendDir: create index.html " << endl; +#endif + DIR *dir; + struct dirent *de; + + sendHeaders(200, "OK", NULL, "text/html", -1, statbuf->st_mtime); + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + + string hdr = ""; + snprintf(f, sizeof(f), "Index of %s\r\n", mPath.c_str()); + hdr += f; + snprintf(f, sizeof(f), "

Index of %s

\r\n
\n", mPath.c_str());
+    hdr += f;
+    snprintf(f, sizeof(f), "Name                             Last Modified              Size\r\n");
+    hdr += f;
+    snprintf(f, sizeof(f), "
\r\n"); + hdr += f; + + *mResponseMessage += hdr; + hdr = ""; + + if (len > 1) { + snprintf(f, sizeof(f), "..\r\n"); + hdr += f; + } + *mResponseMessage += hdr; + + dir = opendir(mPath.c_str()); + while ((de = readdir(dir)) != NULL) { + char timebuf[32]; + struct tm *tm; + strcpy(pathbuf, mPath.c_str()); + // printf (" -Entry: %s\n", de->d_name); + strcat(pathbuf, de->d_name); + + stat(pathbuf, statbuf); + tm = gmtime(&(statbuf->st_mtime)); + strftime(timebuf, sizeof(timebuf), "%d-%b-%Y %H:%M:%S", tm); + + hdr = ""; + snprintf(f, sizeof(f), "", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : ""); + hdr += f; + + snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : " "); + hdr += f; + + if (strlen(de->d_name) < 32) { + snprintf(f, sizeof(f), "%*s", 32 - strlen(de->d_name), ""); + hdr += f; + } + if (S_ISDIR(statbuf->st_mode)) { + snprintf(f, sizeof(f), "%s\r\n", timebuf); + hdr += f; + } + else { + snprintf(f, sizeof(f), "%s\r\n", timebuf); + hdr += f; + } + + *mResponseMessage += hdr; + } + closedir(dir); + snprintf(f, sizeof(f), "
\r\n
\r\n
%s
\r\n\r\n", SERVER); + *mResponseMessage += f; + + } + + mRemLength = 0; + return OKAY; +} + + +int cHttpResource::writeXmlItem(string name, string link, string programme, string desc, string guid, time_t start, int dur) { + string hdr = ""; + char f[400]; + + hdr += "\n"; + // snprintf(f, sizeof(f), "%s - %s", ); + hdr += "" + name +"\n"; + hdr += "" +link + "\n"; + hdr += "" + guid + "\n"; + + hdr += "" + programme +"\n"; + hdr += "" + desc + "\n"; + + snprintf(f, sizeof(f), "%ld", start); + hdr += ""; + hdr += f; + hdr += "\n"; + + hdr += ""; + if (start != 0) { + strftime(f, sizeof(f), "%y%m%d %H:%M", localtime(&start)); + hdr += f; + } + else + hdr += "0 0"; + hdr += "\n"; + + snprintf(f, sizeof(f), "%d", dur); + hdr += ""; + hdr += f; + hdr += "\n"; + + hdr += "\n"; + + *mResponseMessage += hdr; + + // return writeToClient(hdr.c_str(), hdr.size()); + return OKAY; +} + +int cHttpResource::parseQueryLine (vector *avps) { + bool done = false; + size_t cur_pos = 0; + while (!done) { + size_t end_pos = mQuery.find('&', cur_pos); + size_t pos_eq = mQuery.find('=', cur_pos); + + if (pos_eq != cur_pos) { + avps->push_back(sQueryAVP(mQuery.substr(cur_pos, (pos_eq -cur_pos)), mQuery.substr(pos_eq+1, (end_pos -pos_eq-1)) )); + } + if (end_pos == string::npos) + done = true; + else + cur_pos = end_pos +1; + } + + return OKAY; +} + +int cHttpResource::getQueryAttributeValue(vector *avps, string attr, string &val) { + int found = ERROR; + for (uint i = 0; i < avps->size(); i++) { + if ((*avps)[i].attribute == attr) { + val = (*avps)[i].value; + found = OKAY; + } +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " a= " + << (*avps)[i].attribute + << " v= " << (*avps)[i].value + << endl; +#endif + } + return found; +} + +int cHttpResource::parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf) { + char pathbuf[4096]; + string link; + // char f[400]; + // int len; + + DIR *dir; + struct dirent *de; + string dir_comp; + dir_comp = dir_base + dir_name + "/"; + + *(mLog->log()) << DEBUGPREFIX + << " parseFiles: Prefix= " << prefix + << " base= " << dir_base + << " dir= " << dir_name + << " comp= " << dir_comp + << endl; + + dir = opendir(dir_comp.c_str()); + while ((de = readdir(dir)) != NULL) { + if ((strcmp(de->d_name, ".") == 0) or (strcmp(de->d_name, "..") == 0)) { + continue; + } + + // *(mLog->log()) << DEBUGPREFIX << " " << de->d_name << endl; + strcpy(pathbuf, dir_comp.c_str()); + strcat(pathbuf, de->d_name); + + stat(pathbuf, statbuf); + + if (S_ISDIR(statbuf->st_mode)) { + if (strcmp(&(pathbuf[strlen(pathbuf)-4]), ".rec") == 0) { + time_t now = time(NULL); + struct tm tm_r; + struct tm t = *localtime_r(&now, &tm_r); + t.tm_isdst = -1; + // char [20] rest; + int start = -1; + sscanf(de->d_name, "%4d-%02d-%02d.%02d%.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); + t.tm_year -= 1900; + t.tm_mon--; + t.tm_sec = 0; + start = mktime(&t); + + *(mLog->log()) << DEBUGPREFIX + << " Vdr Folder Found: " << pathbuf << " start= " << start << endl; + + entries->push_back(sFileEntry(dir_name, pathbuf, start)); + } + else { + parseFiles(entries, prefix + de->d_name + "~", dir_comp, de->d_name, statbuf); + } + } + else { + entries->push_back(sFileEntry(prefix+de->d_name, pathbuf, 1)); + } + } + closedir(dir); + return OKAY; +} + +int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) { +#ifndef STANDALONE + + size_t pos = mPath.find_last_of ("/"); + + mDir = mPath.substr(0, pos); + string mpd_name = mPath.substr(pos+1); + + float seg_dur = mFactory->getSegmentDuration() *1.0; + // float seg_dur = SEGMENT_DURATION *1.0; + + + cRecordings* recordings = &Recordings; + cRecording* rec = recordings->GetByName(mDir.c_str()); + float duration = rec->NumFrames() / rec->FramesPerSecond(); + time_t now = time(NULL); + + if (rec->Info() != NULL){ + if (rec->Info()->GetEvent() != NULL) { + if (rec->Info()->GetEvent()->EndTime() > now) { + + float corr = (now - rec->Info()->GetEvent()->StartTime()) - duration; + duration = rec->Info()->GetEvent()->Duration() -int(corr); + *(mLog->log()) << DEBUGPREFIX + << " is Recording: Duration= " << duration << " sec" + << " correction: " << int(corr) + << endl; + } + } + else + *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info()->GetEvent() is NULL " << endl; + } + else + *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info() is NULL " << endl; + + // duration is now either the actual duration of the asset or the target duration of the asset + int end_seg = int (duration / seg_dur) +1; + // FIXME: Test Only + /* if (rec->FramesPerSecond() > 40) + end_seg = int (duration *2 / seg_dur) +1; +*/ + *(mLog->log()) << DEBUGPREFIX + << " m3u8 for mDir= " << mDir + << " duration= " << duration + << " seg_dur= " << seg_dur + << " end_seg= " << end_seg + << endl; + + + + if (is_hls) { + writeM3U8(duration, seg_dur, end_seg); + } + else { + writeMPD(duration, seg_dur, end_seg); + } + +#endif + return OKAY; +} + +void cHttpResource::writeM3U8(float duration, float seg_dur, int end_seg) { + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + mContentType = MEMBLOCK; + + mConnState = SERVING; + char buf[30]; + + string hdr = ""; + + sendHeaders(200, "OK", NULL, "application/x-mpegURL", -1, -1); + + *mResponseMessage += "#EXTM3U\n"; + // snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", (seg_dur-1)); + snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", int(seg_dur)); + hdr = buf; + *mResponseMessage += hdr; + + *mResponseMessage += "#EXT-X-MEDIA-SEQUENCE:1\n"; + *mResponseMessage += "#EXT-X-KEY:METHOD=NONE\n"; + + for (int i = 1; i < end_seg; i++){ + // snprintf(buf, sizeof(buf), "#EXTINF:%.1f,\n", (seg_dur-0.5)); + snprintf(buf, sizeof(buf), "#EXTINF:%.2f,\n", seg_dur); + hdr = buf; + *mResponseMessage += hdr; + + snprintf(buf, sizeof(buf), "%d-seg.ts\n", i); + hdr = buf; + *mResponseMessage += hdr; + } + *mResponseMessage += "#EXT-X-ENDLIST\n"; + +} + +void cHttpResource::writeMPD(float duration, float seg_dur, int end_seg) { + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + mContentType = MEMBLOCK; + + mConnState = SERVING; + char buf[30]; + char line[400]; + + string hdr = ""; + + // sendHeaders(200, "OK", NULL, "application/xml", -1, -1); + + *mResponseMessage += "\n"; + + snprintf(line, sizeof(line), "getHasMinBufferTime(), duration); + *mResponseMessage = *mResponseMessage + line; + + // *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + snprintf(line, sizeof(line), "\n", mFactory->getHasBitrate()); + *mResponseMessage = *mResponseMessage + line; + // *mResponseMessage += "\n"; + + // *mResponseMessage += "\n"; + + hdr = "\n"; + *mResponseMessage += hdr; + + snprintf(buf, sizeof(buf), "\"%d\"", end_seg); + *mResponseMessage += "\n"; + + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += ""; + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); +} + +int cHttpResource::sendMediaSegment (struct stat *statbuf) { +#ifndef STANDALONE + + *(mLog->log()) << DEBUGPREFIX << " sendMediaSegment " << mPath << endl; + size_t pos = mPath.find_last_of ("/"); + + mDir = mPath.substr(0, pos); + string seg_name = mPath.substr(pos+1); + int seg_number; + + int seg_dur = mFactory->getSegmentDuration(); +// int seg_dur = SEGMENT_DURATION; + int frames_per_seg = 0; + + sscanf(seg_name.c_str(), "%d-seg.ts", &seg_number); + + //FIXME: Do some consistency checks on the seg_number + //* Does the segment exist + + cRecordings* recordings = &Recordings; + cRecording* rec = recordings->GetByName(mDir.c_str()); + if (rec != NULL) { + frames_per_seg = seg_dur * rec->FramesPerSecond(); + } + else { + *(mLog->log()) << DEBUGPREFIX << " ERROR: Ooops, rec is NULL, assuming 25 fps " << endl; + frames_per_seg = seg_dur * 25; + } + //FIXME: HD Fix + // frames_per_seg = seg_dur * 25; + + *(mLog->log()) << DEBUGPREFIX + << " mDir= " << mDir + << " seg_name= " << seg_name + << " seg_number= "<< seg_number + << " fps= " << rec->FramesPerSecond() + << " frames_per_seg= " << frames_per_seg + << endl; + int start_frame_count = (seg_number -1) * frames_per_seg; + + FILE* idx_file = fopen((mDir +"/index").c_str(), "r"); + if (idx_file == NULL){ + *(mLog->log()) << DEBUGPREFIX + << " failed to open idx file = "<< (mDir +"/index").c_str() + << endl; + sendError(404, "Not Found", NULL, "Failed to open Index file"); + return OKAY; + } + + char *index_buf = new char[(frames_per_seg +3) *8]; + + // fseek to start_frame_count * sizeof(in_read) + fseek(idx_file, start_frame_count * 8, SEEK_SET); + + // read to (seg_number * frames_per_seg +1) * sizeof(in_read) + // buffersize is frames_per_seg * seg_number * sizeof(in_read) + int buffered_indexes = fread(index_buf, 8, (frames_per_seg +2), idx_file); + + fclose(idx_file); + + if(buffered_indexes <= 0 ) { + *(mLog->log())<log()) << DEBUGPREFIX + << " GenSegment: start (no/idx)= " << start_idx << " / " << start_offset + << " to (no/idx)= " << end_idx << " / " << end_offset + << endl; + /*#endif*/ + + delete[] index_buf; + + int rem_len = 0; + bool error = false; + if (start_idx == end_idx){ + mRemLength = (end_offset - start_offset); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " start_idx == end_idx: mRemLength= " <log()) << DEBUGPREFIX + << " start_idx < end_idx " + << endl; + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); + if (stat(seg_fn, statbuf) < 0) { + *(mLog->log()) << DEBUGPREFIX + << " file= " <st_size - start_offset; // remaining length of the first segment + for (int idx = (start_idx+1); idx < end_idx; idx ++) { + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), idx); + if (stat(seg_fn, statbuf) < 0) { + *(mLog->log()) << DEBUGPREFIX + << " for loop file= " <st_size; // remaining length of the first segment + } + rem_len += end_offset; // + mRemLength = rem_len; + *(mLog->log()) << DEBUGPREFIX + << " start_idx= " << start_idx << " != end_idx= "<< end_idx <<": mRemLength= " <log())<< DEBUGPREFIX << " Failed to open file= " << seg_fn + << " mRemLength= " << mRemLength<< endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + fseek(mFile, start_offset, SEEK_SET); + + sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); + +#endif + return OKAY; +} + +int cHttpResource::sendMediaXml (struct stat *statbuf) { + char pathbuf[4096]; + string link; + string media_folder = "/hd2/mpeg"; + + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + mContentType = MEMBLOCK; + + mConnState = SERVING; + + *(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl; + + vector entries; + parseFiles(&entries, "", media_folder, "", statbuf); + + sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + *mResponseMessage += hdr; + + hdr = ""; + + for (uint i=0; i < entries.size(); i++) { + + snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", mServerAddr.c_str(), mServerPort, + cUrlEncode::doUrlSaveEncode(entries[i].sPath).c_str()); + if (writeXmlItem(cUrlEncode::doXmlSaveEncode(entries[i].sName), pathbuf, "NA", "NA", "-", + entries[i].sStart, -1) == ERROR) + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + *mResponseMessage += hdr; + + return OKAY; +} + +int cHttpResource::sendEpgXml (struct stat *statbuf) { +#ifndef STANDALONE + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + mContentType = MEMBLOCK; + + mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /epg.xml" + << DEBUGHDR << endl; +#endif + vector avps; + parseQueryLine(&avps); + string id = "S19.2E-1-1107-17500"; + if (getQueryAttributeValue(&avps, "id", id) == ERROR){ + *(mLog->log())<< DEBUGPREFIX + << " ERROR: id not found" + << DEBUGHDR << endl; + sendError(400, "Bad Request", NULL, "no id in query line"); + return OKAY; + } + + + /* for (int i = 0; i < avps.size(); i++) { + if (avps[i].attribute == "id") + id = avps[i].value; + *(mLog->log())<< DEBUGPREFIX + << " a= " + << avps[i].attribute + << " v= " << avps[i].value + << endl; + }*/ + tChannelID chan_id = tChannelID::FromString (id.c_str()); + if ( chan_id == tChannelID::InvalidID) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Not possible to get the ChannelId from the string" + << DEBUGHDR << endl; + // ERROR + // FIXME: Should send a proper error code + return ERROR; + } + + cSchedulesLock * lock = new cSchedulesLock(false, 500); + const cSchedules *schedules = cSchedules::Schedules(*lock); + + const cSchedule *schedule = schedules->GetSchedule(chan_id); + const cEvent * ev = schedule->GetPresentEvent(); + + delete lock; + + sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + *mResponseMessage += hdr; + // Payload here + + string title = cUrlEncode::doXmlSaveEncode(ev->Title()); + hdr = "" + title +"\n"; + hdr += ""; + hdr += cUrlEncode::doXmlSaveEncode(ev->Description()); + hdr += "\n"; + + snprintf(f, sizeof(f), "%ld\n", ev->StartTime()); + hdr += f ; + + snprintf(f, sizeof(f), "%ld\n", ev->EndTime()); + hdr += f; + *mResponseMessage += hdr; + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + + +#endif + return OKAY; +} + +int cHttpResource::sendChannelsXml (struct stat *statbuf) { +#ifndef STANDALONE + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + mContentType = MEMBLOCK; + + mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /channels.xml" + << DEBUGHDR << endl; +#endif + + vector avps; + parseQueryLine(&avps); + string mode = ""; + bool add_desc = true; + if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode == "nodesc") { + add_desc = false; + *(mLog->log())<< DEBUGPREFIX + << " Mode: No Description" + << endl; + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Mode: Unknown" + << endl; + } + } + + sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + *mResponseMessage += hdr; + + int count = mFactory->getLiveChannels(); + cSchedulesLock * lock = new cSchedulesLock(false, 500); + const cSchedules *schedules = cSchedules::Schedules(*lock); + + for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { + if (channel->GroupSep()) + continue; + if (--count == 0) { + break; + } + + snprintf(f, sizeof(f), "http://%s:3000/%s.ts", mServerAddr.c_str(), *(channel->GetChannelID()).ToString()); + string link = f; + + const cSchedule *schedule = schedules->GetSchedule(channel->GetChannelID()); + string desc = "No description available"; + string title = "Not available"; + time_t start_time = 0; + int duration = 0; + + if (schedule != NULL) { + const cEvent *ev = schedule->GetPresentEvent(); + if (ev != NULL) { + if ((ev->Description() != NULL) && add_desc) + desc = cUrlEncode::doXmlSaveEncode(ev->Description()); + + if ((ev->Title() != NULL) && add_desc) + title = cUrlEncode::doXmlSaveEncode(ev->Title()); + start_time = ev->StartTime(); + duration = ev->Duration(); + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Event Info is Zero for Count= " + << count + << " Name= " << channel->Name() << endl; + } + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Schedule is Zero for Count= " + << count + << " Name= " << channel->Name() << endl; + } + + if (writeXmlItem(channel->Name(), link, title, desc, *(channel->GetChannelID()).ToString(), start_time, duration) == ERROR) + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + delete lock; + +#endif + return OKAY; +} + +int cHttpResource::sendRecordingsXml(struct stat *statbuf) { +#ifndef STANDALONE + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + mContentType = MEMBLOCK; + + mConnState = SERVING; + + vector avps; + parseQueryLine(&avps); + string model = ""; + string link_ext = ""; + string type = ""; + string has_4_hd_str = ""; + bool has_4_hd = true; + + if (getQueryAttributeValue(&avps, "model", model) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Model Parameter: " << model + << endl; + if (model == "samsung") + link_ext = "/manifest-seg.m3u8|COMPONENT=HLS"; + } + + if (getQueryAttributeValue(&avps, "type", type) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Type Parameter: " << type + << endl; + if (type == "hls") { + if (model == "samsung") + link_ext = "/manifest-seg.m3u8|COMPONENT=HLS"; + else + link_ext = "/manifest-seg.m3u8"; + } + if (type == "has") { + if (model == "samsung") + link_ext = "/manifest-seg.mpd|COMPONENT=HAS"; + else + link_ext = "/manifest-seg.mpd"; + } + } + + if (getQueryAttributeValue(&avps, "has4hd", has_4_hd_str) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Has4Hd Parameter: " << has_4_hd_str + << endl; + if (has_4_hd_str == "false") + has_4_hd = false; + } + + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /recordings.xml" + << DEBUGHDR << endl; +#endif + sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + *mResponseMessage += hdr; + + if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", + "-", 0, 0) == ERROR) + return ERROR; + + if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", + "-", 0, 0) == ERROR) + return ERROR; + + if (writeXmlItem("HAS - The Town", "http://192.168.1.122/sm/TheTown/manifest-seg.mpd|COMPONENT=HAS", "NA", "HD Test", + "-", 0, 0) == ERROR) + return ERROR; + + + //-------------------- + cRecordings* recordings = &Recordings; + // char buff[20]; + char f[600]; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " recordings->Count()= " << recordings->Count() + << DEBUGHDR << endl; +#endif + + // List of recording timer + time_t now = time(NULL); + + vector act_rec; +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " checking avtive timer" + << endl; +#endif + for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ + ti->Matches(); + + 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() + << " Duration= " << ti->Event()->Duration() + << endl; + act_rec.push_back(sTimerEntry(ti->File(), ti->Event()->StartTime(), ti->Event()->Duration())); + } + } + + *(mLog->log())<< DEBUGPREFIX + << " Found " << act_rec.size() + << " running timers" + << endl; + + int rec_dur = 0; + for (cRecording *recording = recordings->First(); recording; recording = recordings->Next(recording)) { + hdr = ""; + + // if (recording->IsPesRecording() or ((recording->FramesPerSecond() > 30.0) and !(mFactory->getConfig()->useHasForHd()))) + if (recording->IsPesRecording() or ((recording->FramesPerSecond() > 30.0) and !has_4_hd )) + snprintf(f, sizeof(f), "http://%s:%d%s", mServerAddr.c_str(), mServerPort, + cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str()); + else + snprintf(f, sizeof(f), "http://%s:%d%s%s", mServerAddr.c_str(), mServerPort, + cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), link_ext.c_str()); + + string link = f; + string desc = "No description available"; + rec_dur = recording->LengthInSeconds(); + + string name = recording->Name(); + + for (uint x = 0; x < act_rec.size(); x++) { + if (act_rec[x].name == name) { + + *(mLog->log())<< DEBUGPREFIX + << " !!!!! Found active Recording !!! " + << endl; + // link += "/hm-seg.m3u8"; + rec_dur += (act_rec[x].startTime + act_rec[x].duration - now); + + + } + } // for + + if (recording->Info() != NULL) { + if (recording->Info()->Description() != NULL) { + desc = cUrlEncode::doXmlSaveEncode(recording->Info()->Description()); + } + + } + + if (writeXmlItem(cUrlEncode::doXmlSaveEncode(recording->Name()), link, "NA", desc, "-", + recording->start, rec_dur) == ERROR) + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + + +#endif + return OKAY; +} + +int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { +#ifndef STANDALONE + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + mContentType = MEMBLOCK; + + mConnState = SERVING; + + sendHeaders(200, "OK", NULL, "text/html", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "Recordings\r\n"; + hdr += "\r\n"; + hdr += ""; + hdr += "

Recordings

\r\n
\n";
+  hdr += "
\r\n"; + + *mResponseMessage += hdr; + + char buff[20]; + char f[400]; + for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { + hdr = ""; + strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&recording->start)); + snprintf(f, sizeof(f), "%s - %d %s\r\n", buff, recording->HierarchyLevels(), recording->FileName(), recording->Name()); + hdr += f; + *mResponseMessage += hdr; + // start is time_t + } +#endif + return OKAY; +} + + +int cHttpResource::sendVdrDir(struct stat *statbuf) { +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " *** sendVdrDir mPath= " << mPath << endl; +#endif + char pathbuf[4096]; + char f[400]; + int vdr_idx = 0; + off_t total_file_size = 0; + // int ret = OKAY; + string vdr_dir = mPath; + vector file_sizes; + bool more_to_go = true; + + checkRecording(); + + mVdrIdx = 1; + mFileStructure = "%s/%03d.vdr"; + + if (mPath.compare(mPath.size() - 9, 9, "99.99.rec") != 0) { + mFileStructure = "%s/%05d.ts"; +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " using dir format: " << mFileStructure.c_str() << endl; +#endif + } + + // --- looup all vdr files in the dir --- + while (more_to_go) { + vdr_idx ++; + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), vdr_idx); + if (stat(pathbuf, statbuf) >= 0) { +#ifndef DEBUG + *(mLog->log())<< " found for " << pathbuf << endl; +#endif + file_sizes.push_back(sVdrFileEntry(statbuf->st_size, total_file_size, vdr_idx)); + total_file_size += statbuf->st_size; + } + else { + more_to_go = false; + } + } + if (file_sizes.size() < 1) { + // There seems to be vdr video file in the directory + *(mLog->log())<< DEBUGPREFIX + << " No video file in the directory" + << DEBUGHDR << endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " vdr filesize list " + << DEBUGHDR << endl; + for (uint i = 0; i < file_sizes.size(); i++) + *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; + *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; +#endif + + // total_file_size (on disk) + + // ---------------- file sizes --------------------- + + uint cur_idx = 0; + +#ifndef DEBUG + if (mIsRecording) { + snprintf(f, sizeof(f), " CurFileSize= %lld mRecProgress= %f ExtFileSize= %lld", total_file_size, mRecProgress, (long long int)(mRecProgress * total_file_size)); + *(mLog->log()) << DEBUGPREFIX + << endl << " isRecording: " << f + << endl; + } +#endif + + if (!rangeHdr.isRangeRequest) { + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), file_sizes[cur_idx].sIdx); + + if (openFile(pathbuf) != OKAY) { + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + + mRemLength = total_file_size; + + sendHeaders(200, "OK", NULL, "video/mpeg", ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size ), statbuf->st_mtime); + } + else { // Range request + // idenify the first file +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << endl <<" --- Range Request Handling ---" + << DEBUGHDR << endl; +#endif + if (mIsRecording && (rangeHdr.begin > total_file_size)) { + *(mLog->log()) << DEBUGPREFIX + << " ERROR: Not yet available" << endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + cur_idx = file_sizes.size() -1; + for (uint i = 1; i < file_sizes.size(); i++) { + if (rangeHdr.begin < file_sizes[i].sFirstOffset ) { + cur_idx = i -1; + break; + } + } + +#ifndef DEBUG + *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " + << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin + << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; +#endif + + mVdrIdx = file_sizes[cur_idx].sIdx; + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), file_sizes[cur_idx].sIdx); +#ifndef DEBUG + *(mLog->log())<< " file identified= " << pathbuf << endl; +#endif + if (openFile(pathbuf) != OKAY) { + *(mLog->log())<< "----- fopen failed dump ----------" << endl; + *(mLog->log())<< DEBUGPREFIX + << " vdr filesize list " + << DEBUGHDR << endl; + for (uint i = 0; i < file_sizes.size(); i++) + *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; + *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; + + *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " + << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin + << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; + *(mLog->log())<< "---------------" << endl; + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + mDir = mPath; + mPath = pathbuf; +#ifndef DEBUG + *(mLog->log())<< " Seeking into file= " << (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset) + << " cur_idx= " << cur_idx + << " file_sizes[cur_idx].sFirstOffset= " << file_sizes[cur_idx].sFirstOffset + << endl; +#endif + fseek(mFile, (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset), SEEK_SET); + if (rangeHdr.end == 0) + rangeHdr.end = ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size); + + mRemLength = (rangeHdr.end-rangeHdr.begin); + + snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), + ((mIsRecording) ? (long long int)(mRecProgress * total_file_size): total_file_size)); + + sendHeaders(206, "Partial Content", f, "video/mpeg", (rangeHdr.end-rangeHdr.begin), statbuf->st_mtime); + } + +#ifndef DEBUG + *(mLog->log())<< " ***** Yes, vdr dir found ***** mPath= " << mPath<< endl; +#endif + + return OKAY; // handleRead() done +} + +void cHttpResource::sendHeaders(int status, const char *title, const char *extra, const char *mime, + off_t length, time_t date) { + + time_t now; + char timebuf[128]; + char f[400]; + + string hdr = ""; + snprintf(f, sizeof(f), "%s %d %s\r\n", PROTOCOL, status, title); + hdr += f; + snprintf(f, sizeof(f), "Server: %s\r\n", SERVER); + hdr += f; + now = time(NULL); + strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&now)); + snprintf(f, sizeof(f), "Date: %s\r\n", timebuf); + hdr += f; + if (extra) { + snprintf(f, sizeof(f), "%s\r\n", extra); + *(mLog->log())<< DEBUGPREFIX << " " << f; + hdr += f; + } + if (mime) { + snprintf(f, sizeof(f), "Content-Type: %s\r\n", mime); + hdr += f; + } + if (length >= 0) { + snprintf(f, sizeof(f), "Content-Length: %lld\r\n", length); + *(mLog->log())<< DEBUGPREFIX << " " << f << endl; + hdr += f; + } + if (date != -1) { + strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date)); + snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf); + hdr += f; + } + if (mAcceptRanges) { + snprintf(f, sizeof(f), "Accept-Ranges: bytes\r\n"); + hdr += f; + } + snprintf(f, sizeof(f), "Connection: close\r\n"); + hdr += f; + + snprintf(f, sizeof(f), "\r\n"); + hdr += f; + + + if (mBlkLen != 0) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR in SendHeader: mBlkLen != 0!!! --> Overwriting" << endl; + } + mBlkLen = hdr.size(); + strcpy(mBlkData, hdr.c_str()); + +} + +int cHttpResource::sendFile(struct stat *statbuf) { + // Send the First Datachunk, incl all headers + + *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId << " mPath= " << mPath + << DEBUGHDR + << endl; + + char f[400]; + + if (openFile(mPath.c_str()) == ERROR) { + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + mFile = fopen(mPath.c_str(), "r"); + int ret = OKAY; + + if (!mFile) { + sendError(403, "Forbidden", NULL, "Access denied."); + ret = ERROR; + } + else { + mFileSize = S_ISREG(statbuf->st_mode) ? statbuf->st_size : -1; + + if (!rangeHdr.isRangeRequest) { + mRemLength = mFileSize; + sendHeaders(200, "OK", NULL, getMimeType(mPath.c_str()), mFileSize, statbuf->st_mtime); + } + else { // Range request + fseek(mFile, rangeHdr.begin, SEEK_SET); + if (rangeHdr.end == 0) + rangeHdr.end = mFileSize; + mRemLength = (rangeHdr.end-rangeHdr.begin); + snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), mFileSize); + sendHeaders(206, "Partial Content", f, getMimeType(mPath.c_str()), (rangeHdr.end-rangeHdr.begin), statbuf->st_mtime); + } + +#ifndef DEBUG + *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId + << ": Done mRemLength= "<< mRemLength << " ret= " << ret + << DEBUGHDR << endl; +#endif + mConnState = SERVING; + + } + return ret; + +} + +const char *cHttpResource::getMimeType(const char *name) { + char *ext = strrchr((char*)name, '.'); + if (!ext) + return NULL; + // if (ext.compare(".html") || ext.compare(".htm")) return "text/html"; + if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; + if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; + if (strcmp(ext, ".gif") == 0) return "image/gif"; + if (strcmp(ext, ".png") == 0) return "image/png"; + if (strcmp(ext, ".css") == 0) return "text/css"; + if (strcmp(ext, ".au") == 0) return "audio/basic"; + if (strcmp(ext, ".wav") == 0) return "audio/wav"; + if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; + if (strcmp(ext, ".mp4") == 0) return "video/mp4"; + if (strcmp(ext, ".vdr") == 0) return "video/mpeg"; + if (strcmp(ext, ".ts") == 0) return "video/mpeg"; + if (strcmp(ext, ".mpd") == 0) return "application/dash+xml"; + if (strcmp(ext, ".xml") == 0) return "application/xml"; + if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; + if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; + return NULL; +} + + +string cHttpResource::getConnStateName() { + string state_string; + switch (mConnState) { + case WAITING: + state_string = "WAITING"; + break; + case READHDR: + state_string = "READ Req HDR"; + break; + case READPAYLOAD: + state_string = "READ Req Payload"; + break; + case SERVING: + state_string = "SERVING"; + break; + case TOCLOSE: + state_string = "TOCLOSE"; + break; + default: + state_string = "UNKNOWN"; + break; + } + return state_string; +} + +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)); + mVersion = line.substr(line.find_last_of(" ") +1); +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " ReqLine= " << line << endl; +#endif + if (mVersion.compare(0, 4, "HTTP") != 0) { +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " ERROR: No HTTP request -> Closing Connection" << line << endl; +#endif + return ERROR; + } + + size_t pos = mRequest.find('?'); + if (pos != string::npos) + mQuery = mRequest.substr (pos+1, string::npos); + mPath = cUrlEncode::doUrlSaveDecode(mRequest.substr(0, mRequest.find('?'))); + *(mLog->log())<< DEBUGPREFIX + << " mMethod= " << mMethod + << " mPath= " << mPath + << " mVer= " << mVersion + << " mQuery= " << mQuery + // << " HexDump= " << endl << cUrlEncode::hexDump(mPath) << endl + << endl; + return OKAY; +} + +int cHttpResource::parseHttpHeaderLine (string line) { + string hdr_name = line.substr(0, line.find_first_of(":")); + string hdr_val = line.substr(line.find_first_of(":") +2); + + if (hdr_name.compare("Range") == 0) { + parseRangeHeaderValue(hdr_val); + *(mLog->log()) << DEBUGPREFIX + << " Range: Begin= " << rangeHdr.begin + << " End= " << rangeHdr.end + << endl; + } + if (hdr_name.compare("User-Agent") == 0) { + mUserAgent = hdr_val; + *(mLog->log())<< " User-Agent: " << hdr_val + << endl; + } + if (hdr_name.compare("Content-Length") == 0) { + mReqContentLength = atoll(hdr_val.c_str()); +#ifndef DEBUG + *(mLog->log())<< " Content-Length: " << mReqContentLength + << endl; +#endif + } + return 0; +} + +void cHttpResource::checkRecording() { + // sets mIsRecording to true when the recording is still on-going + mIsRecording = false; + time_t now = time(NULL); + +#ifndef STANDALONE + + // cRecordings* recordings = mFactory->getRecordings(); + cRecordings* recordings = &Recordings; +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " GetByName(" <GetByName(mPath.c_str()); + if (rec != NULL) { + const cEvent *ev = rec->Info()->GetEvent(); + if (ev != NULL) { + if (now < ev->EndTime()) { + // still recording + mIsRecording = true; + + // mRecProgress * curFileSize = estimated File Size + mRecProgress = (ev->EndTime() - ev->StartTime()) *1.1 / (rec->NumFrames() / rec->FramesPerSecond()); + // mRecProgress = (ev->EndTime() - ev->StartTime()) *1.0/ (now - ev->StartTime()); +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " **** is still recording for mIsRecording= " + << mIsRecording + << " mRecProgress= " << mRecProgress << endl; +#endif + } + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " checking, whether recording is on-going" + << " now= " << now << " start= " << rec->Start() + << " curDur= " << rec->LengthInSeconds() + << endl; +#endif + } +#ifndef DEBUG + else { + *(mLog->log())<< DEBUGPREFIX + << " **** Recording Entry Not found **** " << endl; + } +#endif +#endif +} + +int cHttpResource::parseRangeHeaderValue(string val) { + rangeHdr.isRangeRequest = true; + size_t pos_equal = val.find_first_of('='); + size_t pos_minus = val.find_first_of('-'); + + string range_type = val.substr(0, pos_equal); + string first_val= val.substr(pos_equal+1, pos_minus -(pos_equal+1)); + rangeHdr.begin = atoll(first_val.c_str()); + + string sec_val = ""; + if ((pos_minus +1)< val.size()){ + sec_val = val.substr(pos_minus+1); + rangeHdr.end = atoll(sec_val.c_str()); + } + return 0; +} + +int cHttpResource::openFile(const char *name) { + mFile = fopen(name, "r"); + if (!mFile) { + *(mLog->log())<< DEBUGPREFIX + << " fopen failed pathbuf= " << name + << endl; + // sendError(403, "Forbidden", NULL, "Access denied."); + return ERROR; + } + return OKAY; +} + diff --git a/vdr-smarttvweb/httpresource.h b/vdr-smarttvweb/httpresource.h new file mode 100644 index 0000000..22e44c4 --- /dev/null +++ b/vdr-smarttvweb/httpresource.h @@ -0,0 +1,188 @@ +/* + * httpresource.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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_H__ +#define __HTTPREQUEST_H__ + +#include +#include +#include +#include "log.h" + +using namespace std; + +struct cRange { +cRange(): isRangeRequest(false), begin(0), end(0) {}; + bool isRangeRequest; + unsigned long long begin; + unsigned long long end; +}; + +struct sQueryAVP { + string attribute; + string value; +sQueryAVP(string a, string v) : attribute (a), value(v) {}; +}; + + +enum eConnState { + WAITING, + READHDR, + READPAYLOAD, + SERVING, + TOCLOSE +}; + +enum eContentType { + NYD, // Not Yet Defined + VDRDIR, + SINGLEFILE, + MEMBLOCK +}; + +struct sFileEntry { + string sName; + string sPath; + int sStart; + +sFileEntry(string n, string l, int s) : sName(n), sPath(l), sStart(s) { + }; +}; + +class SmartTvServer; +class cResumeEntry; + +class cHttpResource { + + public: + cHttpResource(int, int, string, int, SmartTvServer*); + virtual ~cHttpResource(); + + int handleRead(); + int handleWrite(); + + int checkStatus(); + + int readFromClient(); + void threadLoop(); + int run(); + + private: + SmartTvServer* mFactory; + Log* mLog; + + string mServerAddr; + int mServerPort; + int mFd; + int mReqId; + + time_t mConnTime; + int mHandleReadCount; + bool mConnected; + eConnState mConnState; + eContentType mContentType; + + string mReadBuffer; + string mMethod; + + string *mResponseMessage; + int mResponseMessagePos; + char* mBlkData; + int mBlkPos; + int mBlkLen; + + // string path; + string mRequest; + string mQuery; + string mPath; + string mDir; + string mVersion; + string protocol; + unsigned long long mReqContentLength; + string mPayload; + string mUserAgent; + + bool mAcceptRanges; + cRange rangeHdr; + unsigned long long mFileSize; + uint mRemLength; + FILE *mFile; + int mVdrIdx; + string mFileStructure; + bool mIsRecording; + float mRecProgress; + + // int writeToClient(const char *buf, size_t buflen); + // int sendDataChunk(); + + void setNonBlocking(); + int fillDataBlk(); + + int handlePost(); + int processRequest(); + int processHttpHeaderNew(); + + int readRequestPayload(); + // int processHttpHeader(); + void sendError(int status, const char *title, const char *extra, const char *text); + int sendDir(struct stat *statbuf); + int sendVdrDir(struct stat *statbuf); + int sendRecordingsHtml (struct stat *statbuf); + int sendRecordingsXml (struct stat *statbuf); + int sendChannelsXml (struct stat *statbuf); + int sendEpgXml (struct stat *statbuf); + int sendMediaXml (struct stat *statbuf); + + // int sendMPD (struct stat *statbuf); + int sendManifest (struct stat *statbuf, bool is_hls = true); + void writeM3U8(float duration, float seg_dur, int end_seg); + void writeMPD(float duration, float seg_dur, int end_seg); + + + int sendMediaSegment (struct stat *statbuf); + + void sendHeaders(int status, const char *title, const char *extra, const char *mime, + off_t length, time_t date); + + int sendFile(struct stat *statbuf); + + // Helper Functions + const char *getMimeType(const char *name); + string getConnStateName(); + void checkRecording(); + int parseRangeHeaderValue(string); + int parseHttpRequestLine(string); + int parseHttpHeaderLine (string); + int parseQueryLine (vector *avps); + int parseResume(cResumeEntry &entry, string &id); + + int parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf); + + int getQueryAttributeValue(vector *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); + // string removeEtChar(string line); + // string hexDump(string input); + // string convertUrl(string input); + // string convertBack(string input); +}; +#endif diff --git a/vdr-smarttvweb/log.c b/vdr-smarttvweb/log.c new file mode 100644 index 0000000..08a9ed5 --- /dev/null +++ b/vdr-smarttvweb/log.c @@ -0,0 +1,74 @@ +/* + * log.c: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 "log.h" +#include + +Log* Log::instance = NULL; + +Log::Log() { + if (instance) + return; + instance = this; + mLogFile = NULL; +} + +Log::~Log() { + instance = NULL; +} + +Log* Log::getInstance() { + return instance; +} +int Log::init(string fileName) { + char timebuf[128]; + time_t now = time(NULL); + strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); + + mLogFile = new ofstream(); + mLogFile->open(fileName.c_str(), ios::out ); + *mLogFile << "Log Created: " << timebuf << endl; + return 0; +} + +int Log::init(char* fileName) { + char timebuf[128]; + time_t now = time(NULL); + strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); + + mLogFile = new ofstream(); + mLogFile->open(fileName, ios::out ); + *mLogFile << "Log Created: " << timebuf << endl; + return 0; +} + +int Log::shutdown() { + if (mLogFile) + mLogFile->close(); + return 1; +} + +ofstream* Log::log() { + return mLogFile; +} + diff --git a/vdr-smarttvweb/log.h b/vdr-smarttvweb/log.h new file mode 100644 index 0000000..8a09fe3 --- /dev/null +++ b/vdr-smarttvweb/log.h @@ -0,0 +1,50 @@ +/* + * log.h.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 LOG_H +#define LOG_H + +#include +#include + +using namespace std; + +class Log +{ + public: + Log(); + ~Log(); + static Log* getInstance(); + + int init(char* fileName); + int init(string fileName); + int shutdown(); + ofstream* log(); + + private: + static Log* instance; + + ofstream *mLogFile; +}; + +#endif diff --git a/vdr-smarttvweb/smarttvfactory.c b/vdr-smarttvweb/smarttvfactory.c new file mode 100644 index 0000000..12a24bc --- /dev/null +++ b/vdr-smarttvweb/smarttvfactory.c @@ -0,0 +1,382 @@ +/* + * smarttvfactory.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 STANDALONE +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include "smarttvfactory.h" + +#ifndef STANDALONE +#define PORT 8000 +#else +#define PORT 9000 +#endif + +#define OKAY 0 +#define ERROR (-1) + +#define DEBUG + + +using namespace std; + +void SmartTvServerStartThread(void* arg) { + SmartTvServer* m = (SmartTvServer*)arg; + m->threadLoop(); + delete m; + pthread_exit(NULL); +} + +SmartTvServer::SmartTvServer(): mRequestCount(0), isInited(false), serverPort(PORT), mServerFd(-1), + mSegmentDuration(10), mHasMinBufferTime(40), mHasBitrate(6000000), mLiveChannels(20), + clientList(), mActiveSessions(0), mConfig(NULL) { +} + + +SmartTvServer::~SmartTvServer() { + if (mConfig != NULL) + delete mConfig; +} + +void SmartTvServer::cleanUp() { + mLog.shutdown(); +} + +int SmartTvServer::runAsThread() { + int res = pthread_create(&mThreadId, NULL, (void*(*)(void*))SmartTvServerStartThread, (void *)this); + if (res != 0) { + *(mLog.log()) << " Error creating thread. res= " << res + << endl; + return 0; + } + return 1; +} + +void SmartTvServer::threadLoop() { + *(mLog.log()) << " SmartTvServer Thread Started " << endl; + + loop(); + + *(mLog.log()) << " SmartTvServer Thread Stopped " << endl; +} + + +void SmartTvServer::loop() { + socklen_t addr_size = 0; + int rfd; + sockaddr_in sadr; + int req_id = 0; + int ret = 0; + struct timeval timeout; + + int maxfd; + + fd_set read_set; + fd_set write_set; + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + + FD_ZERO(&mReadState); + FD_ZERO(&mWriteState); + + 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; + + for (;;) { + FD_ZERO(&read_set); + FD_ZERO(&write_set); + read_set = mReadState; + write_set = mWriteState; + + if (ret != handeled_fds) { + *(mLog.log()) << "ERROR: Select-ret= " << ret + << " != handeled_fds= " << handeled_fds << endl; + /* FD_ZERO(&mReadState); + FD_ZERO(&mWriteState); + FD_SET(mServerFd, &mReadState); + maxfd = mServerFd; + + read_set = mReadState; + write_set = mWriteState; + for (uint idx= 0; idx < clientList.size(); idx++) { + if (clientList[idx] != NULL) { + close(idx); + delete clientList[idx]; + clientList[idx] = NULL; + } + } + mActiveSessions = 0; +*/ + } + + handeled_fds = 0; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + + ret = select(maxfd + 1, &read_set, &write_set, NULL, &timeout); + + if (ret == 0) { + for (uint idx= 0; idx < clientList.size(); idx++) { + if (clientList[idx] != NULL) + if (clientList[idx]->checkStatus() == ERROR) { + close(idx); + delete clientList[idx]; + clientList[idx] = NULL; + mActiveSessions--; + FD_CLR(idx, &mReadState); /* dead client */ + FD_CLR(idx, &mWriteState); + *(mLog.log()) << "WARNING: Timeout - Dead Client fd=" << idx << endl; + } + } + continue; + } // timeout + + if (ret < 0){ + *(mLog.log()) << "ERROR: select error " << errno << endl; + continue; + } // Error + + // new accept + if (FD_ISSET(mServerFd, &read_set)) { + if((rfd = accept(mServerFd, (sockaddr*)&sadr, &addr_size))!= -1){ + req_id ++; + handeled_fds ++; + +#ifndef DEBUG + *(mLog.log()) << "fd= " << rfd + << " --------------------- Received connection ---------------------" << endl; +#endif + + FD_SET(rfd, &mReadState); /* neuen Client fd dazu */ + FD_SET(rfd, &mWriteState); /* neuen Client fd dazu */ + + if (rfd > maxfd) { + maxfd = rfd; + } + + if (clientList.size() < (rfd+1)) { + clientList.resize(rfd+1, NULL); // Check. + } + clientList[rfd] = new cHttpResource(rfd, req_id, own_ip, serverPort, this); + mActiveSessions ++; + + } + else{ + *(mLog.log()) << "Error accepting " << errno << endl; + } + } + + // 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; + if (FD_ISSET(rfd, &read_set)) { + handeled_fds ++; + // HandleRead + if (clientList[rfd] == NULL) { + *(mLog.log()) << "ERROR in Check Read: oops - no cHttpResource anymore fd= " << rfd << endl; + close(rfd); + FD_CLR(rfd, &mReadState); /* remove dead client */ + FD_CLR(rfd, &mWriteState); + continue; + } + if ( clientList[rfd]->handleRead() < 0){ +#ifndef DEBUG + *(mLog.log()) << "fd= " << rfd << " --------------------- Check Read: Closing ---------------------" << endl; +#endif + close(rfd); + delete clientList[rfd]; + clientList[rfd] = NULL; + mActiveSessions--; + FD_CLR(rfd, &mReadState); /* dead client */ + FD_CLR(rfd, &mWriteState); + } + } + } + + // 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; + if (FD_ISSET(rfd, &write_set)) { + handeled_fds++; + // HandleWrite + if (clientList[rfd] == NULL) { + close(rfd); + FD_CLR(rfd, &mReadState); + FD_CLR(rfd, &mWriteState); + continue; + } + if ( clientList[rfd]->handleWrite() < 0){ + *(mLog.log()) << "fd= " << rfd << " --------------------- Check Write: Closing ---------------------" << endl; + close(rfd); + delete clientList[rfd]; + clientList[rfd] = NULL; + mActiveSessions--; + FD_CLR(rfd, &mReadState); + FD_CLR(rfd, &mWriteState); + } + } + } + + // selfcheck + /* *(mLog.log()) << "Select Summary: ret= " << ret + << " handeled_fds=" << handeled_fds + << " mActiveSessions= " << mActiveSessions + << " clientList.size()= " << clientList.size() + << endl; +*/ + //Check for active sessions + /* + *(mLog.log()) << "checking number of active sessions clientList.size()= " << clientList.size() << endl; + + int act_ses = 0; + for (uint idx= 0; idx < clientList.size(); idx++) { + if (clientList[idx] != NULL) + act_ses++; + } + if (act_ses != mActiveSessions) { + *(mLog.log()) << "ERROR: Lost somewhere a session: " + << "mActiveSessions= " << mActiveSessions + << "act_ses= " << act_ses + << endl; + mActiveSessions = act_ses; + } + *(mLog.log()) << "checking number of active sessions - done mActiveSessions= " << mActiveSessions << endl; +*/ + } // for (;;) +} // org bracket + +int SmartTvServer::isServing() { + return (mActiveSessions != 0 ? true : false); +} + +void SmartTvServer::initServer(string dir) { + /* This function initialtes the listening socket for the server + * and sets isInited to true + */ + mConfigDir = dir; + int ret; + struct sockaddr_in sock; + int yes = 1; + + +#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; + +#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 + + mSegmentDuration= mConfig->getSegmentDuration(); + mHasMinBufferTime= mConfig->getHasMinBufferTime(); + mHasBitrate = mConfig->getHasBitrate(); + mLiveChannels = mConfig->getLiveChannels(); + + *(mLog.log()) <<"HTTP server listening on port " << serverPort << endl; + + mServerFd = socket(PF_INET, SOCK_STREAM, 0); + if (mServerFd <0) { + *(mLog.log()) << "Error: Cannot create serving socket, exit" << endl; + exit(1); + } + + ret = setsockopt(mServerFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + if (ret <0) { + *(mLog.log()) << "Error: Cannot set sockopts on serving socket, exit" << endl; + exit(1); + } + + memset((char *) &sock, 0, sizeof(sock)); + sock.sin_family = AF_INET; + sock.sin_addr.s_addr = htonl(INADDR_ANY); + sock.sin_port = htons(serverPort); + + ret = bind(mServerFd, (struct sockaddr *) &sock, sizeof(sock)); + if (ret !=0) { + *(mLog.log()) << "Error: Cannot bind serving socket, exit" << endl; + exit(1); + } + + ret = listen(mServerFd, 5); + if (ret <0) { + *(mLog.log()) << "Error: Cannot set listening on serving socket, exit" << endl; + exit(1); + } + + isInited = true; +} + + + + diff --git a/vdr-smarttvweb/smarttvfactory.h b/vdr-smarttvweb/smarttvfactory.h new file mode 100644 index 0000000..e2936bf --- /dev/null +++ b/vdr-smarttvweb/smarttvfactory.h @@ -0,0 +1,88 @@ +/* + * smarttvfactory.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 __SMARTTVSERVER_H__ +#define __SMARTTVSERVER_H__ + +#include +#include +#include +#include +#include "httpresource.h" +#include "log.h" +#include "stvw_cfg.h" + +#ifndef STANDALONE +#include +#endif + +using namespace std; + +class SmartTvServer { + public: + SmartTvServer(); + virtual ~SmartTvServer(); + + void initServer(string c_dir); + void loop(); + void cleanUp(); + int runAsThread(); + void threadLoop(); + + Log mLog; + + void readRecordings(); + int isServing(); + + string getConfigDir() { return mConfigDir; }; + unsigned int getSegmentDuration() { return mSegmentDuration; }; + int getHasMinBufferTime() { return mHasMinBufferTime; }; + unsigned int getHasBitrate() { return mHasBitrate; }; + int getLiveChannels() { return mLiveChannels; }; + + cSmartTvConfig* getConfig() { return mConfig; }; + + private: + pthread_t mThreadId; + int mRequestCount; + bool isInited; + int serverPort; + int mServerFd; + unsigned int mSegmentDuration; + int mHasMinBufferTime; + unsigned int mHasBitrate; + int mLiveChannels; + + vector clientList; + // listmFdList; + int mActiveSessions; + string mConfigDir; + cSmartTvConfig *mConfig; + + int mMaxFd; + fd_set mReadState; + fd_set mWriteState; +}; + + +#endif diff --git a/vdr-smarttvweb/smarttvweb.c b/vdr-smarttvweb/smarttvweb.c new file mode 100644 index 0000000..b7c907a --- /dev/null +++ b/vdr-smarttvweb/smarttvweb.c @@ -0,0 +1,152 @@ +/* + * smarttvweb.c: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 STANDALONE +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smarttvfactory.h" + + +static const char *VERSION = "0.9.0"; +static const char *DESCRIPTION = "SmartTV Web Server"; + + +using namespace std; + +#ifndef STANDALONE +class cPluginSmartTvWeb : public cPlugin +{ +public: + cPluginSmartTvWeb(void); + virtual ~cPluginSmartTvWeb(); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return DESCRIPTION; } + virtual const char *CommandLineHelp(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Initialize(void); + virtual bool Start(void); + virtual bool SetupParse(const char *Name, const char *Value); +#if VDRVERSNUM > 10300 + virtual cString Active(void); +#endif + +private: + SmartTvServer mServer; + string mConfigDir; +}; + +cPluginSmartTvWeb::cPluginSmartTvWeb(void) { + // Initialize any member variables here. + // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL + // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! + mConfigDir = ""; +} + +bool cPluginSmartTvWeb::Start(void) { + // Start any background activities the plugin shall perform. + + if (mConfigDir.compare("") == 0) { + const char* dir_name = cPlugin::ConfigDirectory(Name()); + if (!dir_name) { + dsyslog("SmartTvWeb: Could not get config dir from VDR"); + } + else + mConfigDir = string(dir_name); + } + + mServer.initServer(mConfigDir); + int success = mServer.runAsThread(); + + esyslog("SmartTvWeb: started %s", (success == 1) ? "sucessfully" : "failed!!!!"); + + return ((success == 1) ? true: false); +} + +cPluginSmartTvWeb::~cPluginSmartTvWeb() { + // Clean up after yourself! + mServer.cleanUp(); +} + +const char *cPluginSmartTvWeb::CommandLineHelp(void) +{ + // Return a string that describes all known command line options. + return " \n"; +} + +bool cPluginSmartTvWeb::ProcessArgs(int argc, char *argv[]) { + // Implement command line argument processing here if applicable. + return true; +} + +bool cPluginSmartTvWeb::Initialize(void) { + // Initialize any background activities the plugin shall perform. + esyslog("SmartTvWeb: Initialize called"); + + return true; +} + +bool cPluginSmartTvWeb::SetupParse(const char *Name, const char *Value) +{ + // Parse your own setup parameters and store their values. + return false; +} + +#if VDRVERSNUM > 10300 + +cString cPluginSmartTvWeb::Active(void) { + esyslog("SmartTvWeb: Active called Checkme"); + if (mServer.isServing()) + return tr("SmartTV client(s) serving"); + else + return NULL; +} + +#endif + +VDRPLUGINCREATOR(cPluginSmartTvWeb); // Don't touch this! + +#else //VOMPSTANDALONE + + +int main(int argc, char *argv[]) { + printf ("Starting up\n"); + + SmartTvServer server; + server.initServer("."); + server.loop(); +} +#endif diff --git a/vdr-smarttvweb/stvw_cfg.c b/vdr-smarttvweb/stvw_cfg.c new file mode 100644 index 0000000..bc0c4f6 --- /dev/null +++ b/vdr-smarttvweb/stvw_cfg.c @@ -0,0 +1,165 @@ +/* + * stvw_cfg.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 "stvw_cfg.h" + +#ifndef STANDALONE +#include +#endif + +#include +#include +#include +#include + +cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NULL), + mLogFile(), mMediaFolder(), mSegmentDuration(), mHasMinBufferTime(), mHasBitrate(), + mLiveChannels() { + +#ifndef STANDALONE + mLogFile= "/multimedia/video/smartvvweblog.txt"; +#else + mLogFile= "/tmp/smartvvweblog-standalone.txt"; +#endif + + // Defaults + mMediaFolder= "/hd2/mpeg"; + mSegmentDuration = 10; + mHasMinBufferTime = 40; + mHasBitrate = 6000000; + mLiveChannels = 30; + mLog = Log::getInstance(); + + readConfig(); +} + +cSmartTvConfig::~cSmartTvConfig() { + fclose(mCfgFile); +} + +void cSmartTvConfig::readConfig() { + string line; + char attr[200]; + char value[200]; + + ifstream myfile ((mConfigDir +"/smarttvweb.conf").c_str()); + + if (!myfile.is_open()) { +#ifndef STANDALONE + esyslog ("ERROR in SmartTvWeb: Cannot open config file. Expecting %s", (mConfigDir +"/smarttvweb.conf").c_str() ); +#else + cout << "ERROR: Cannot open config file. Expecting "<< (mConfigDir +"/smarttvweb.conf") << endl; +#endif + return; + } + + while ( myfile.good() ) { + getline (myfile, line); + + if ((line == "") or (line[0] == '#')) + continue; + + sscanf(line.c_str(), "%s %s", attr, value); + + if (strcmp(attr, "LogFile")==0) { + mLogFile = string(value); + // cout << " Found mLogFile= " << mLogFile << endl; + continue; + } + + if (strcmp(attr, "MediaFolder") == 0) { + mMediaFolder = string (value); + // cout << " Found mMediaFolder= " << mMediaFolder << endl; + continue; + } + if (strcmp(attr, "SegmentDuration") == 0) { + mSegmentDuration = atoi(value); + // cout << " Found mSegmentDuration= " << mSegmentDuration << endl; + continue; + } + + if (strcmp(attr, "HasMinBufferTime") == 0) { + mHasMinBufferTime = atoi(value); + // cout << " Found mHasMinBufferTime= " << mHasMinBufferTime << endl; + continue; + } + if (strcmp(attr, "HasBitrate") == 0) { + mHasBitrate = atoi(value); + // cout << " Found mHasBitrate= " <log()) << " readConfig: " << line << endl; + if (line == "") + continue; + + string t; + time_t st; + int r; + time_t lv; + int count = scanf (line.c_str(), "%s %lld %d %lld", &t, &st, &r, &lv); + if (count == 4) { + *(mLog->log()) << " read: " << t << " st= " << st << " r= " << r << " lv= " << lv << endl; + } + // first title + + + } + myfile.close(); + } + + else { + *(mLog->log()) << " readConfig: Cannot open file " << f << endl; + return NULL; + } + +*/ + + // open the file + // read the lines + return NULL; +} diff --git a/vdr-smarttvweb/stvw_cfg.h b/vdr-smarttvweb/stvw_cfg.h new file mode 100644 index 0000000..4d19953 --- /dev/null +++ b/vdr-smarttvweb/stvw_cfg.h @@ -0,0 +1,86 @@ +/* + * stvw_cfg.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 __SMARTTV_CONFIG_H__ +#define __SMARTTV_CONFIG_H__ + +#include +#include +#include +#include +#include "log.h" + +using namespace std; + +class cResumeEntry { + public: + string mTitle; + time_t mStartTime; // title is not unique + int mResume; + time_t mLastViewed; + + friend ostream& operator<<(ostream& out, const cResumeEntry& o) { + out << "mTitle= " << o.mTitle << " mStartTime= " << o.mStartTime << " mResume= " << o.mResume << endl; + return out; + }; +}; + +class cResumes { + public: + cResumes(string t) : mDevice(t) {}; + + vector mResumes; + + string mDevice; +}; + +class cSmartTvConfig { + private: + string mConfigDir; + Log* mLog; + FILE *mCfgFile; + + string mLogFile; + string mMediaFolder; + unsigned int mSegmentDuration; + int mHasMinBufferTime; + unsigned int mHasBitrate; + int mLiveChannels; + + public: + cSmartTvConfig(string dir); + ~cSmartTvConfig(); + + void readConfig(); + + cResumes* readConfig(string); + + string getLogFile() { return mLogFile; }; + string getMediaFolder() { return mMediaFolder; }; + unsigned int getSegmentDuration() {return mSegmentDuration; }; + int getHasMinBufferTime() { return mHasMinBufferTime; }; + unsigned int getHasBitrate() {return mHasBitrate; }; + int getLiveChannels() {return mLiveChannels; }; +}; + +#endif diff --git a/vdr-smarttvweb/url.c b/vdr-smarttvweb/url.c new file mode 100644 index 0000000..75b5817 --- /dev/null +++ b/vdr-smarttvweb/url.c @@ -0,0 +1,302 @@ +/* + * url.c: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 +#include"url.h" + + + +string cUrlEncode::doUrlSaveEncode(string in) { + string res = ""; + unsigned char num = 0; + char buf[5]; + + bool done = false; + unsigned int idx = 0; + while (!done) { + if (idx == in.size()) { + done = true; + continue; + } + num = in[idx]; + switch (num) { + case '&': + res += "%26"; + break; + case '%': + res += "%25"; + break; + case '#': + // break; + if (in.compare(idx, 3, "#3F") == 0) { + res += "%3F"; + idx +=3; + continue; + } + if (in.compare(idx, 3, "#3A") == 0) { + res += "%3A"; + idx += 3; + continue; + } + if (in.compare(idx, 3, "#2F") == 0) { + res += "%2F"; + idx += 3; + continue; + } + res += "%23"; // just a '#' char + break; + default: + // Copy the normal chars + if (num < 128) + res += char(num); + else { + sprintf (buf, "%02hhX", num); + res += "%"; + res += buf; + } + break; + } // switch + + idx ++; + } + return res; +} + + +string cUrlEncode::doUrlSaveDecode(string input) { + string res = ""; + unsigned int idx = 0; + int x; + while (idx < input.size()){ + if (input[idx] == '%') { + string num = input.substr(idx+1, 2); + sscanf(num.c_str(), "%X", &x); + idx+= 3; + switch (x) { + case 0x23: // '#' + res += "#"; + break; + case 0x25: // '%' + res += "%"; + break; + case 0x2f: // '/' + res += "#2F"; + break; + case 0x3a: // ':' + res += "#3A"; + break; + case 63: // '?' + res += "#3F"; + break; + default: + res += char(x); + break; + } + } + else { + res += input[idx]; + idx ++; + } + } + return res; +} + + +string cUrlEncode::doXmlSaveEncode(string in) { + string res = ""; + unsigned char num = 0; + char buf[5]; + + bool done = false; + unsigned int idx = 0; + while (!done) { + if (idx == in.size()) { + done = true; + continue; + } + num = in[idx]; + switch (num) { + case 0x26: // '&': + res += "&"; + break; + case 0x27: // '\'': + res += "'"; + break; + case 0x3c: // '<': + res += "<"; + break; + case 0x3e: // '>': + res += ">"; + break; + case 0x22: // '\"': + res += """; + break; + + /* case 0xc4: // Ä + res += "Ä"; + break; + case 0xe4: //'ä': + res += "ä"; + break; + case 0xd6: // Ö + res += "Ö"; + break; + case 0xf6: // 'ö': + res += "ö"; + break; + case 0xdc: //'Ü': + res += "Ü"; + break; + case 0xfc: //'ü': + res += "ü"; + break; + case 0xdf: //'ß': + res += "ß"; + break; +*/ + default: + // Copy the normal chars + res += char(num); + break; + } // switch + + idx ++; + } + return res; +} + + +string cUrlEncode::removeEtChar(string line, bool xml) { + bool done = false; + size_t cur_pos = 0; + size_t pos = 0; + string res = ""; + + int end_after_done = 0; + + while (!done) { + pos = line.find('&', cur_pos); + if (pos == string::npos) { + done = true; + res += line.substr(cur_pos); + break; + } + if (pos >= 0) { + if (xml) + res += line.substr(cur_pos, (pos-cur_pos)) + "&"; // xml save encoding + else + res += line.substr(cur_pos, (pos-cur_pos)) + "%26"; // url save encoding + // cur_pos = cur_pos+ pos +1; + cur_pos = pos +1; + end_after_done ++; + } + } + return res; +} + +string cUrlEncode::hexDump(char *line, int line_len) { + string res = ""; + string ascii = ""; + char buf[10]; + + int line_count = 0; + for (unsigned int i = 0; i < line_len; i++) { + unsigned char num = line[i]; + sprintf (buf, "%02hhX", num); + res += buf; + if ((num >= 32) && (num < 127)) { + ascii += char(num); + } + else + ascii += '.'; + + line_count++; + switch (line_count) { + case 8: + res += " "; + ascii += " "; + break; + case 17: + res += " " + ascii; + res += "\r\n"; + ascii = ""; + line_count = 0; + break; + default: + res += " "; + break; + } + } + if (line_count != 0) { + for (int i = 0; i < ((17 - line_count) * 3 ); i++) + res += " "; + if (line_count >= 8) + res += " "; + + res += " "; + res += ascii; + } + return res; +} + +string cUrlEncode::hexDump(string in) { + string res = ""; + string ascii = ""; + char buf[10]; + + int line_count = 0; + for (unsigned int i = 0; i < in.size(); i++) { + unsigned char num = in[i]; + sprintf (buf, "%02hhX", num); + res += buf; + if ((num >= 32) && (num < 127)) { + ascii += char(num); + } + else + ascii += '.'; + + line_count++; + switch (line_count) { + case 8: + res += " "; + ascii += " "; + break; + case 16: + res += " " + ascii; + res += "\r\n"; + ascii = ""; + line_count = 0; + break; + default: + res += " "; + break; + } + } + if (line_count != 0) { + for (int i = 0; i < ((16 - line_count) * 3 ); i++) + res += " "; + if (line_count >= 8) + res += " "; + res += ascii; + } + return res; +} diff --git a/vdr-smarttvweb/url.h b/vdr-smarttvweb/url.h new file mode 100644 index 0000000..4695f8b --- /dev/null +++ b/vdr-smarttvweb/url.h @@ -0,0 +1,49 @@ +/* + * url.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten 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 __URL_H__ +#define __URL_H__ + +#include + +using namespace std; + +class cUrlEncode { + + public: + cUrlEncode() {}; + cUrlEncode(string &) {}; + + static string doUrlSaveEncode (string); + static string doUrlSaveDecode (string); + + static string doXmlSaveEncode (string); + static string removeEtChar(string line, bool xml=true); + static string hexDump(char *line, int line_len); + static string hexDump(string); + + private: + +}; + +#endif -- cgit v1.2.3 From 504925aa69338fc41156cc40b526c79b62ad71a0 Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 26 Dec 2012 07:15:03 +0100 Subject: Better Log support and addition of missing files --- vdr-smarttvweb/httpresource.c | 19 +++++++++++++------ vdr-smarttvweb/log.c | 23 +++++++++++++++++------ vdr-smarttvweb/smarttvweb.conf | 15 +++++++++++++++ vdr-smarttvweb/stvw_cfg.c | 4 ++-- vdr-smarttvweb/widget.conf | 11 +++++++++++ 5 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 vdr-smarttvweb/smarttvweb.conf create mode 100644 vdr-smarttvweb/widget.conf diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index c10721b..8ca689a 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -1377,6 +1377,10 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { parseQueryLine(&avps); string mode = ""; bool add_desc = true; + + string no_channels_str = ""; + int no_channels = -1; + if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ if (mode == "nodesc") { add_desc = false; @@ -1390,6 +1394,10 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { << endl; } } + if (getQueryAttributeValue(&avps, "channels", no_channels_str) == OKAY){ + no_channels = atoi(no_channels_str.c_str()) ; + } + sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); @@ -1401,6 +1409,9 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { *mResponseMessage += hdr; int count = mFactory->getLiveChannels(); + if (no_channels > 0) + count = no_channels +1; + cSchedulesLock * lock = new cSchedulesLock(false, 500); const cSchedules *schedules = cSchedules::Schedules(*lock); @@ -1526,6 +1537,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { *mResponseMessage += hdr; + /* if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", "-", 0, 0) == ERROR) return ERROR; @@ -1533,11 +1545,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", "-", 0, 0) == ERROR) return ERROR; - - if (writeXmlItem("HAS - The Town", "http://192.168.1.122/sm/TheTown/manifest-seg.mpd|COMPONENT=HAS", "NA", "HD Test", - "-", 0, 0) == ERROR) - return ERROR; - +*/ //-------------------- cRecordings* recordings = &Recordings; @@ -1587,7 +1595,6 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { for (cRecording *recording = recordings->First(); recording; recording = recordings->Next(recording)) { hdr = ""; - // if (recording->IsPesRecording() or ((recording->FramesPerSecond() > 30.0) and !(mFactory->getConfig()->useHasForHd()))) if (recording->IsPesRecording() or ((recording->FramesPerSecond() > 30.0) and !has_4_hd )) snprintf(f, sizeof(f), "http://%s:%d%s", mServerAddr.c_str(), mServerPort, cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str()); diff --git a/vdr-smarttvweb/log.c b/vdr-smarttvweb/log.c index 08a9ed5..685bce3 100644 --- a/vdr-smarttvweb/log.c +++ b/vdr-smarttvweb/log.c @@ -23,6 +23,7 @@ #include "log.h" #include +#include Log* Log::instance = NULL; @@ -45,20 +46,30 @@ int Log::init(string fileName) { time_t now = time(NULL); strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); - mLogFile = new ofstream(); - mLogFile->open(fileName.c_str(), ios::out ); - *mLogFile << "Log Created: " << timebuf << endl; + if (fileName != "") { + mLogFile = new ofstream(); + + mLogFile->open(fileName.c_str(), ios::out ); + *mLogFile << "Log Created: " << timebuf << endl; + } + else + mLogFile = new ofstream("/dev/null"); return 0; } int Log::init(char* fileName) { + char timebuf[128]; time_t now = time(NULL); strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); - mLogFile = new ofstream(); - mLogFile->open(fileName, ios::out ); - *mLogFile << "Log Created: " << timebuf << endl; + if (strcmp(fileName, "") !=0) { + mLogFile = new ofstream(); + mLogFile->open(fileName, ios::out ); + *mLogFile << "Log Created: " << timebuf << endl; + } + else + mLogFile = new ofstream("/dev/null"); return 0; } diff --git a/vdr-smarttvweb/smarttvweb.conf b/vdr-smarttvweb/smarttvweb.conf new file mode 100644 index 0000000..6afbbb9 --- /dev/null +++ b/vdr-smarttvweb/smarttvweb.conf @@ -0,0 +1,15 @@ +# + +LogFile /tmp/smarttvweb.txt + +MediaFolder /multimedia/video + +SegmentDuration 10 + +HasMinBufferTime 30 + +HasBitrate 6000 + +LiveChannels 30 + +UseHasForHd false \ No newline at end of file diff --git a/vdr-smarttvweb/stvw_cfg.c b/vdr-smarttvweb/stvw_cfg.c index bc0c4f6..0c943b5 100644 --- a/vdr-smarttvweb/stvw_cfg.c +++ b/vdr-smarttvweb/stvw_cfg.c @@ -36,9 +36,9 @@ cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NU mLiveChannels() { #ifndef STANDALONE - mLogFile= "/multimedia/video/smartvvweblog.txt"; + mLogFile= ""; #else - mLogFile= "/tmp/smartvvweblog-standalone.txt"; + mLogFile= "./smartvvweblog-standalone.txt"; #endif // Defaults diff --git a/vdr-smarttvweb/widget.conf b/vdr-smarttvweb/widget.conf new file mode 100644 index 0000000..c028671 --- /dev/null +++ b/vdr-smarttvweb/widget.conf @@ -0,0 +1,11 @@ + + +hls +6000000 +40 +20 +50 +5 +30 +20 + -- cgit v1.2.3 From 86fe990eb0641f74cee509e51079d7a5cab27d6f Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 26 Dec 2012 11:08:33 +0100 Subject: Initial Widget Version --- smarttv-client/.project | 17 + smarttv-client/.settings/.jsdtscope | 7 + .../org.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + smarttv-client/CSS/Main.css | 374 +++++++ smarttv-client/Images/icon/SmartTvWeb_106.png | Bin 0 -> 6671 bytes smarttv-client/Images/icon/SmartTvWeb_115.png | Bin 0 -> 7297 bytes smarttv-client/Images/icon/SmartTvWeb_85.png | Bin 0 -> 4325 bytes smarttv-client/Images/icon/SmartTvWeb_95.png | Bin 0 -> 5149 bytes smarttv-client/Images/mute.png | Bin 0 -> 284 bytes smarttv-client/Images/muteBar.png | Bin 0 -> 2032 bytes smarttv-client/Images/volume.png | Bin 0 -> 404 bytes smarttv-client/Images/volumeBar.png | Bin 0 -> 1203 bytes smarttv-client/Javascript/Audio.js | 31 + smarttv-client/Javascript/CVS/Entries | 13 + smarttv-client/Javascript/Config.js | 227 +++++ smarttv-client/Javascript/Data.js | 155 +++ smarttv-client/Javascript/Display.js | 757 ++++++++++++++ smarttv-client/Javascript/Main.js | 1029 ++++++++++++++++++++ smarttv-client/Javascript/Network.js | 25 + smarttv-client/Javascript/Options.js | 66 ++ smarttv-client/Javascript/Player.js | 350 +++++++ smarttv-client/Javascript/Server.js | 141 +++ smarttv-client/config.xml | 33 + smarttv-client/index.html | 125 +++ 25 files changed, 3352 insertions(+) create mode 100755 smarttv-client/.project create mode 100755 smarttv-client/.settings/.jsdtscope create mode 100755 smarttv-client/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100755 smarttv-client/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100755 smarttv-client/CSS/Main.css create mode 100755 smarttv-client/Images/icon/SmartTvWeb_106.png create mode 100755 smarttv-client/Images/icon/SmartTvWeb_115.png create mode 100755 smarttv-client/Images/icon/SmartTvWeb_85.png create mode 100755 smarttv-client/Images/icon/SmartTvWeb_95.png create mode 100755 smarttv-client/Images/mute.png create mode 100755 smarttv-client/Images/muteBar.png create mode 100755 smarttv-client/Images/volume.png create mode 100755 smarttv-client/Images/volumeBar.png create mode 100755 smarttv-client/Javascript/Audio.js create mode 100755 smarttv-client/Javascript/CVS/Entries create mode 100755 smarttv-client/Javascript/Config.js create mode 100755 smarttv-client/Javascript/Data.js create mode 100755 smarttv-client/Javascript/Display.js create mode 100755 smarttv-client/Javascript/Main.js create mode 100755 smarttv-client/Javascript/Network.js create mode 100755 smarttv-client/Javascript/Options.js create mode 100755 smarttv-client/Javascript/Player.js create mode 100755 smarttv-client/Javascript/Server.js create mode 100755 smarttv-client/config.xml create mode 100755 smarttv-client/index.html diff --git a/smarttv-client/.project b/smarttv-client/.project new file mode 100755 index 0000000..046ce41 --- /dev/null +++ b/smarttv-client/.project @@ -0,0 +1,17 @@ + + + VdrOnTv + + + + + + org.eclipse.wst.jsdt.core.javascriptValidator + + + + + + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/smarttv-client/.settings/.jsdtscope b/smarttv-client/.settings/.jsdtscope new file mode 100755 index 0000000..be3d72d --- /dev/null +++ b/smarttv-client/.settings/.jsdtscope @@ -0,0 +1,7 @@ + + + + + + + diff --git a/smarttv-client/.settings/org.eclipse.wst.jsdt.ui.superType.container b/smarttv-client/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100755 index 0000000..3bd5d0a --- /dev/null +++ b/smarttv-client/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/smarttv-client/.settings/org.eclipse.wst.jsdt.ui.superType.name b/smarttv-client/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100755 index 0000000..05bd71b --- /dev/null +++ b/smarttv-client/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/smarttv-client/CSS/Main.css b/smarttv-client/CSS/Main.css new file mode 100755 index 0000000..da91d1f --- /dev/null +++ b/smarttv-client/CSS/Main.css @@ -0,0 +1,374 @@ +/* Defaults */ + +* +{ + padding: 0; + margin: 0; + border: 0; + position: relative; + color:#FFFFFF; + background-color: transpant; + text-shadow: 1px 1px 1px #000; + +} + +/* Layout */ +body { + width: 960px; + height: 540px; +} + +#splashScreen +{ + position: absolute; + left: 0px; top: 0px; + width: 960px; height: 540px; + background-color: blue; +} + +#splashStatus { + position: absolute; + left: 30%; top: 40%; + width: 40%; height: 20%; + color: white; + text-align: center; + vertical-align: middle; + background-color: darkblue; + background-color: rgba(0,0,139,0.8); +} + +#selectScreen { + position: absolute; + left: 0px; top: 0px; + width: 960px; height: 540px; + background: -webkit-radial-gradient(80% 80%, ellipse cover, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); +} +/* background: -webkit-linear-gradient(top, rgba(64,150,238,1) 0%,rgba(64,150,238,1) 100%);*/ + + +#selectView { + position: absolute; + left: 240px; top: 135px; + width: 480px; height: 270px; + border-style:solid; + border-width:2px; + font-size:16px; + -webkit-border-radius: 7px; + padding-top:20px; + padding-left:20px; + padding-right:20px; + background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); + -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); +} + +#optionsScreen { + position: absolute; + left: 0px; top: 0px; + width: 960px; height: 540px; + display: none; + background: -webkit-radial-gradient(80% 80%, ellipse cover, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); +} + +#optionsView { + position: absolute; + left: 20px; top: 135px; + width: 480px; height: 270px; + border-style:solid; + border-width:2px; + font-size:16px; + -webkit-border-radius: 7px; + padding-top:20px; + padding-left:20px; + padding-right:20px; + background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); + -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); +} + +#statusPopup { + position: absolute; + left: 80%; top: 10px; + width: 10%; height: 6%; + -webkit-border-radius: 1em; + -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); + border-style:solid; + border-width:2px; + font-size:16px; + + padding-top:20px; + padding-left:20px; + padding-right:20px; + background-color: rgba(0,0,0,0.5); +} + +#popup{ + position: absolute; + display: none; + left: 120px; top: 10px; + width: 720px; height: 100px; + border-style:solid; + border-width:2px; + -webkit-border-radius: 7px; + z-index:10; + + overflow-y: scroll; + padding-top:20px; + padding-left:20px; + padding-right:20px; + background-color: darkblue; + background-color: rgba(0, 0, 139, 0.5); + background-color: -webkit-linear-gradient(-45deg, rgba(167,207,223,1) 0%,rgba(35,83,138,1) 100%); +} + +#main { + display: none; + position: absolute; + left: 0px; top: 0px; + width: 960px; height: 540px; + background: -webkit-linear-gradient(top, rgba(64,150,238,1) 0%,rgba(64,150,238,1) 100%); +} + +#logo { + position: absolute; + top: 0px; + height: 35px; + width: 90%; + background-repeat:repeat-x; + font-size:25px; + padding-top:3px; + padding-left:20px; + z-index:10; +} + + + +/* Right Half */ +#rightHalf { + position: absolute; + left:500px; top: 40px; + width: 45%; height: 90%; + border-style:solid; + -webkit-border-radius: 7px; + + border-width:1px; + background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); +} +/* background: -webkit-linear-gradient(top, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); +*/ +#status +{ + position:absolute; + left:75%; top:10px; + width: 98px; height:16px; + + text-align:left; + font-size:10px; +} + + +#description +{ + position:absolute; + left:5%; top:30px; + width:85%; height:88%; + padding-top:10px; + padding-left:10px; + padding-right:10px; + border-style:solid; + border-width:1px; + text-align:left; + font-size:16px; + overflow:hidden; + -webkit-border-radius: 7px; + + text-overflow : ellipsis; + -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); + background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); +} + +/* Left Half */ +#leftHalf { + position:absolute; + left: 10px; top:40px; + width:50%; height:90%; + background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); + border-style:solid; + border-width:1px; + -webkit-border-radius: 7px; + +} +/* background: -webkit-linear-gradient(top, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); +*/ + +#videoList { + position:absolute; + left:5%; top:30px; + width: 85%; height:90%; + font-size:14px; + border-style:solid; + border-width:1px; + padding-left:10px; + padding-right:10px; + padding-top:3px; + -webkit-border-radius: 7px; + + -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); + background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); +} + +.style_videoList +{ + color:#FFFFFF; + text-align:left; + font-size:10px; +} + +#videoCount +{ + position:absolute; + left:330px; top:10px; + width:100px; height:20px; + + text-align:left; + font-size:10px; +} + + + +#volume +{ + display: none; + border-style:solid; + border-width:1px; + position:absolute; + left:8px; top:480px; + width:472px; height:24px; + z-index:20; + background-color: black; + background-color: rgba(0, 0, 0, 0.5); +} + +#volumeIcon +{ + position:absolute; + left:4px; top:4px; + width:17px; height:16px; + background-image:url("../Images/volume.png"); +} + +#volumeBarBG +{ + position:absolute; + left:22px; top:8px; + width:85%; height:8px; + border-style:solid; + border-width:1px; +} + +#volumeBar +{ + position:absolute; + left:0px; top:0px; + width:0%; height:8px; + background: -webkit-radial-gradient(bottom right, ellipse cover, #a90329 0%,#8f0222 44%,#6d0019 100%); +} + + +#volumeInfo +{ + position:absolute; + left:88%; top:4px; + width:32px; height:16px; + + text-align:left; + font-size:14px; +} + + +#overlay +{ + display: none; + position: absolute; + top: 480px; + height: 60px; + width: 100%; + background-repeat:repeat-x; + background-color: black; + background-color: rgba(0, 0, 0, 0.5); + z-index:20; +} + +#olProgressBarBG { + position:absolute; + left:8%; top:35px; + width:75%; height:8px; + border-style:solid; + border-width:1px; + background-color: rgba(0, 0, 0, 0.2); +} + +#olProgressBar { + position:absolute; + left:0px; top:0px; + width:50%; height:100%; + background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); +} + +#olRecProgressBar { + display: none; + position:absolute; + left:60%; top:0px; + width:40%; height:100%; + background: -webkit-radial-gradient(right, ellipse cover, #a90329 0%,#8f0222 44%,#6d0019 100%); +} +/*background-color: red;*/ + + +#olNow { + position:absolute; + left:0px; top:30px; + width:8%; height:16px; + text-align:center; + font-size:14px; +} + +#olTitle { + position:absolute; + left:8%; top:5px; + width:50%; height:16px; + text-align:left; + font-size:16px; +} + +#olStartStop { + position:absolute; + left:65%; top:5px; + width:20%; height:16px; + text-align:center; + font-size:16px; +} + +#buffering { + position:absolute; + left:89%; top:10px; + width:7%; height:16px; +} + +#bufferingBG { + width:100%; height:9px; + border-style:solid; + border-width:1px; +} + +#bufferingBar { + width:0%; height:100%; + background: -webkit-radial-gradient(bottom right, ellipse cover, #cfe7fa 0%,#6393c1 100%); +} + +#olTimeInfo +{ + position:absolute; + left:83%; top:30px; + width:14%; height:16px; + text-align:center; + font-size:14px; +} diff --git a/smarttv-client/Images/icon/SmartTvWeb_106.png b/smarttv-client/Images/icon/SmartTvWeb_106.png new file mode 100755 index 0000000..527d28d Binary files /dev/null and b/smarttv-client/Images/icon/SmartTvWeb_106.png differ diff --git a/smarttv-client/Images/icon/SmartTvWeb_115.png b/smarttv-client/Images/icon/SmartTvWeb_115.png new file mode 100755 index 0000000..e3576c7 Binary files /dev/null and b/smarttv-client/Images/icon/SmartTvWeb_115.png differ diff --git a/smarttv-client/Images/icon/SmartTvWeb_85.png b/smarttv-client/Images/icon/SmartTvWeb_85.png new file mode 100755 index 0000000..0f831e2 Binary files /dev/null and b/smarttv-client/Images/icon/SmartTvWeb_85.png differ diff --git a/smarttv-client/Images/icon/SmartTvWeb_95.png b/smarttv-client/Images/icon/SmartTvWeb_95.png new file mode 100755 index 0000000..42dcb28 Binary files /dev/null and b/smarttv-client/Images/icon/SmartTvWeb_95.png differ diff --git a/smarttv-client/Images/mute.png b/smarttv-client/Images/mute.png new file mode 100755 index 0000000..3878270 Binary files /dev/null and b/smarttv-client/Images/mute.png differ diff --git a/smarttv-client/Images/muteBar.png b/smarttv-client/Images/muteBar.png new file mode 100755 index 0000000..a68776d Binary files /dev/null and b/smarttv-client/Images/muteBar.png differ diff --git a/smarttv-client/Images/volume.png b/smarttv-client/Images/volume.png new file mode 100755 index 0000000..a4ba06d Binary files /dev/null and b/smarttv-client/Images/volume.png differ diff --git a/smarttv-client/Images/volumeBar.png b/smarttv-client/Images/volumeBar.png new file mode 100755 index 0000000..956cb03 Binary files /dev/null and b/smarttv-client/Images/volumeBar.png differ diff --git a/smarttv-client/Javascript/Audio.js b/smarttv-client/Javascript/Audio.js new file mode 100755 index 0000000..6046c37 --- /dev/null +++ b/smarttv-client/Javascript/Audio.js @@ -0,0 +1,31 @@ +var Audio = +{ + plugin : null +}; + +Audio.init = function() +{ + var success = true; + + this.plugin = document.getElementById("pluginAudio"); + + if (!this.plugin) + { + success = false; + } + + return success; +}; + +Audio.setRelativeVolume = function(delta) +{ + this.plugin.SetVolumeWithKey(delta); + Display.setVolume( this.getVolume() ); + +}; + +Audio.getVolume = function() +{ + alert("Volume : " + this.plugin.GetVolume()); + return this.plugin.GetVolume(); +}; diff --git a/smarttv-client/Javascript/CVS/Entries b/smarttv-client/Javascript/CVS/Entries new file mode 100755 index 0000000..0ce6906 --- /dev/null +++ b/smarttv-client/Javascript/CVS/Entries @@ -0,0 +1,13 @@ +/Audio.js/1.2/Sun Oct 21 11:06:19 2012// +/Comm.js/-1.4/Wed Dec 12 19:36:09 2012// +/Config.js/1.2/Thu Dec 20 20:52:57 2012// +/Data.js/1.8/Thu Dec 13 18:51:43 2012// +/Display.js/1.33/Thu Dec 20 19:34:59 2012// +/HandlePages.js/-1.1/Sat Oct 6 06:18:38 2012// +/Main.js/1.38/Thu Dec 20 20:53:18 2012// +/Network.js/1.2/Tue Oct 23 18:04:28 2012// +/Options.js/1.2/Sun Dec 16 13:29:28 2012// +/Player.js/1.33/Thu Dec 20 20:42:11 2012// +/Server.js/1.13/Thu Dec 13 18:51:44 2012// +/debug.js/-1.2/Fri Oct 19 19:26:18 2012// +/dlna.js/-1.1/Wed Oct 17 13:56:23 2012// diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js new file mode 100755 index 0000000..b0742a4 --- /dev/null +++ b/smarttv-client/Javascript/Config.js @@ -0,0 +1,227 @@ +var Config = { + cfgFileName : curWidget.id + "/config.dta", + XHRObj : null, + xmlDocument : null, + serverUrl : "", // Will become the main URL for contacting the server + serverAddr : "192.168.1.122:8000", + format :"has", + tgtBufferBitrate : 6000, // kbps + totalBufferDuration : 30, // sec + initialBuffer : 10, // in percent + pendingBuffer: 40, // in percent + initialTimeOut: 3, // sec + skipDuration : 30, // sec + noLiveChannels : 30, + firstLaunch : false +}; + + +/* + * Check for First launch + * Launch Config Menu + * Write File in Confi Menu + * Come back here, read the file and continue + */ + + +Config.init = function () { + var fileSystemObj = new FileSystem(); + + if (fileSystemObj.isValidCommonPath(curWidget.id) == 0){ + Display.showPopup ("First Launch of the Widget --> Launching Config Menu"); + alert("First Launch of the Widget"); + // should switch to the config screen here + var res = fileSystemObj.createCommonDir(curWidget.id); + if (res == true) { + Config.firstLaunch = true; + +// Config.writeContext("192.168.1.122:8000"); + Main.changeState(4); + return; + } + else { + Display.showPopup ("ERROR: Cannot create widget folder"); + } + return; + } + + else { + Config.readContext(); + } + + Config.fetchConfig(); +}; + +Config.fetchConfig = function () { + if (this.XHRObj == null) { + this.XHRObj = new XMLHttpRequest(); + } + + if (this.XHRObj) { + + this.XHRObj.onreadystatechange = function() { + if (Config.XHRObj.readyState == 4) { + Config.processConfig(); + } + }; + + this.XHRObj.open("GET", this.serverUrl + "/widget.conf", true); + this.XHRObj.send(null); + } +}; + +Config.writeContext = function (addr) { + var fileSystemObj = new FileSystem(); + + var fd = fileSystemObj.openCommonFile(Config.cfgFileName,"w"); + + fd.writeLine('serverAddr ' + addr); // SHould be overwritten by Options menue + fileSystemObj.closeCommonFile(fd); +}; + +Config.updateContext = function (addr) { + var fileSystemObj = new FileSystem(); + + var fd = fileSystemObj.openCommonFile(Config.cfgFileName,"w"); + + fd.writeLine('serverAddr ' + addr); // SHould be overwritten by Options menue + fileSystemObj.closeCommonFile(fd); + + Config.serverAddr = addr; + Config.serverUrl = "http://" + Config.serverAddr; +}; + +Config.readContext = function () { + var fileSystemObj = new FileSystem(); + +// Display.showPopup ("Reading Config file for curWidget.id= " + curWidget.id); + + try { + var fd = fileSystemObj.openCommonFile(Config.cfgFileName, "r"); + + var line = ""; + + while (line = fd.readLine()) { + var avp = line.split(" "); +// Display.showPopup ("Reading Config: attr= " + avp[0] + " val= " + avp[1]); + if (avp.length > 1) { + Config.serverAddr = avp[1]; + Config.serverUrl = "http://" + Config.serverAddr; + } + else { + Display.showPopup ("WARNING: Error in Config File"); + } + } + fileSystemObj.closeCommonFile(fd); + } + catch (e) { + var res = fileSystemObj.createCommonDir(curWidget.id); + if (res == true) { + Display.showPopup ("*** Read Error and Widget Folder successfully created ***"); + alert("-------------- Error: res = true ------------------------"); + } + else { + Display.showPopup ("*** Read Error and Widget Folder creation failed ***"); + alert("-------------- Error: res = false ------------------------"); + } + + Config.firstLaunch = true; + Main.changeState(4); + } +}; + + +Config.getXmlValue = function (itm) { + var val = this.xmlDocument.getElementsByTagName(itm); + var res = 0; + try { + res = val[0].firstChild.data; + } + catch (e) { + Main.log("parsing widget.conf: Item= " + itm + " not found" + e); + alert ("parsing widget.conf: Item= " + itm + " not found e= " + e); + } + return res; + +}; + +Config.getXmlString = function (itm) { + var val = this.xmlDocument.getElementsByTagName(itm); + + var res = ""; + try { + res = val[0].firstChild.data; + } + catch (e) { + Main.log("parsing widget.conf: Item= " + itm + " not found" + e); + alert ("parsing widget.conf: Item= " + itm + " not found e= " + e); + }; + + return res; +}; + +Config.processConfig = function () { + if (this.XHRObj.status != 200) { + alert ("Config Server Error"); + Display.showPopup("Config Server Error " + this.XHRObj.status); + Main.log("Config Server Error " + this.XHRObj.status); + } + else { + var xmlResponse = this.XHRObj.responseXML; + if (xmlResponse == null) { + alert ("xml error"); + Display.showPopup("Error in XML Config File"); + return; + } + this.xmlDocument = xmlResponse.documentElement; + if (!this.xmlDocument ) { + alert("Failed to get valid Config XML"); + Display.showPopup("Failed to get valid Config XML"); + return; + } + else { + alert ("Paring config XML now"); + this.format = Config.getXmlString("format"); + var res = Config.getXmlValue("tgtBufferBitrate"); + if (res != 0) + this.tgtBufferBitrate = 1.0 * res; + res = Config.getXmlValue("totalBufferDuration"); + if (res != 0) this.totalBufferDuration = 1.0 * res; + + res= Config.getXmlValue("initialBuffer"); + if (res != 0) this.initialBuffer = 1.0 * res; + + res = Config.getXmlValue("pendingBuffer"); + if (res != 0) this.pendingBuffer = 1.0 * res; + + res = Config.getXmlValue("skipDuration"); + if (res != 0) this.skipDuration= 1.0 * res; + + res = Config.getXmlValue("initialTimeOut"); + if (res != 0) this.initialTimeOut = 1.0 * res; + + res = Config.getXmlValue("liveChannels"); + if (res != 0) noLiveChannels = res; + + alert("**** Config ****"); + alert("serverUrl= " + Config.serverUrl); + alert("format= " + Config.format); + alert("tgtBufferBitrate= " + Config.tgtBufferBitrate); + alert("totalBufferDuration= " + Config.totalBufferDuration); + alert("initialBuffer= " + Config.initialBuffer); + alert("pendingBuffer= " + Config.pendingBuffer); + alert("skipDuration= " + Config.skipDuration); + alert("initialTimeOut= " + Config.initialTimeOut); + alert("**** /Config ****"); + }; + + }; + + Main.init(); +}; + +// This function cleans up after un-installation +Config.reset = function () { + var fileSystemObj = new FileSystem(); + fileSystemObj.deleteCommonFile(curWidget.id + "/config.dta"); +}; diff --git a/smarttv-client/Javascript/Data.js b/smarttv-client/Javascript/Data.js new file mode 100755 index 0000000..a2de13f --- /dev/null +++ b/smarttv-client/Javascript/Data.js @@ -0,0 +1,155 @@ +var Data = +{ + assets : new Item, + folderList : [], +}; + +Data.reset = function() { + this.assets = null; + this.assets = new Item; + + this.folderList = []; + + this.folderList.push({item : this.assets, id: 0}); +}; + +Data.completed= function(sort) { + if (sort == true) + this.assets.sortPayload(); + + this.folderList.push({item : this.assets, id: 0}); + alert ("Data.completed()= " +this.folderList.length); + +}; + +Data.selectFolder = function (idx) { + this.folderList.push({item : this.getCurrentItem().childs[idx], id: idx}); +}; + +Data.isRootFolder = function() { + 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); +}; + +Data.dumpFolderStruct = function(){ + alert("---------- dumpFolderStruct ------------"); + this.assets.print(0); + alert("---------- dumpFolderStruct Done -------"); +}; + +Data.getCurrentItem = function () { + return this.folderList[this.folderList.length-1].item; +}; + +Data.getVideoCount = function() +{ + return this.folderList[this.folderList.length-1].item.childs.length; +}; + + +//----------------------------------------- +function Item() { + this.title = "root"; + this.isFolder = true; + this.childs = []; + this.payload = ""; // only set, if (isFolder == false) +} + +Item.prototype.isFolder = function() { + return this.isFolder; +}; + +Item.prototype.getTitle = function () { + return this.title; +}; + +Item.prototype.getPayload = function () { + if (this.isFolder == true) { + alert("WARNING: getting payload on a folder title=" +this.title); + } + return this.payload; +}; + +Item.prototype.getItem = function (title) { + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].title == title) { + return this.childs[i]; + } + } + return 0; +}; + +Item.prototype.addChild = function (key, pyld, level) { + if (key.length == 1) { + var folder = new Item; +// folder.title = pyld.startstr + " - " + key; + folder.title = key[0]; + folder.payload = pyld; + folder.isFolder = false; + this.childs.push(folder); +// this.titles.push({title: pyld.startstr + " - " + key , pyld : pyld}); + } + else { + if (level > 10) { + alert(" too many levels"); + return; + } + var t = key.shift(); + var found = false; + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].title == t) { + this.childs[i].addChild(key, pyld, level +1); + found = true; + break; + } + } + if (found == false) { + var folder = new Item; + folder.title = t; + folder.addChild(key, pyld, level+1); + this.childs.push(folder); + } + } +}; + +Item.prototype.print = function(level) { + var prefix= ""; + for (var i = 0; i < level; i++) + prefix += " "; + + for (var i = 0; i < this.childs.length; i++) { + alert(prefix + this.childs[i].title); + if (this.childs[i].isFolder == true) { + alert(prefix+"Childs:"); + this.childs[i].print(level +1); + } + } +}; + +Item.prototype.sortPayload = function() { + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].isFolder == true) { + this.childs[i].sortPayload(); + } + } + this.childs.sort(function(a,b) { + if (a.title == b.title) { + return (b.payload.start - a.payload.start); + } + else { + return ((a.title < b.title) ? -1 : 1); + } + }); +}; + diff --git a/smarttv-client/Javascript/Display.js b/smarttv-client/Javascript/Display.js new file mode 100755 index 0000000..fa90887 --- /dev/null +++ b/smarttv-client/Javascript/Display.js @@ -0,0 +1,757 @@ +var Display = +{ + pluginTime : null, + statusDiv : null, + statusPopup : null, + bufferingElm : null, + FIRSTIDX : 0, + LASTIDX : 15, + currentWindow : 0, + + olTitle : "", + olStartStop: "", + SELECTOR : 0, + LIST : 1, + +// folderPath : new Array(), + volOlHandler : null, + progOlHandler : null, + popupOlHandler : null, + videoList : new Array() +}; + + + +Display.init = function() +{ + var success = true; + + this.statusDiv = document.getElementById("status"); + this.statusPopup = document.getElementById("statusPopup"); + + this.pluginTime = document.getElementById("pluginTime"); + this.bufferingElm = document.getElementById("bufferingBar"); + + alert("Display.init now=" + this.pluginTime.GetEpochTime()); + + this.progOlHandler = new OverlayHandler("ProgHndl"); + this.volOlHandler = new OverlayHandler("VolHndl"); + this.popupOlHandler = new OverlayHandler("PopupHndl"); + this.progOlHandler.init(Display.handlerShowProgress, Display.handlerHideProgress); + this.volOlHandler.init(Display.handlerShowVolume, Display.handlerHideVolume); + this.popupOlHandler.init(Display.handlerShowPopup, Display.handlerHidePopup); + + Display.status("Stop"); + if (!this.statusDiv) + { + success = false; + } + for (var i = 0; i <= this.LASTIDX; i++) { + var elm = document.getElementById("video"+i); + elm.style.paddingLeft = "10px"; + elm.style.paddingTop = "4px"; + elm.style.paddingBottom = "6px"; + } + + var done = false; + var i = 0; + while (done != true) { + i ++; + var elm = document.getElementById("selectItem"+i); + if (elm == null) { + done = true; + alert( " only found to selectItem"+ (i-1)); + break; + } + elm.style.paddingBottom = "3px"; + elm.style.marginTop= " 5px"; + elm.style.marginBottom= " 5px"; + elm.style.textAlign = "center"; + } + + alert("Display initialized" ); + return success; +}; + +Display.resetSelectItems = function (itm) { + var done = false; + var i = 0; + while (done != true) { + i ++; + var elm = document.getElementById("selectItem"+i); + if (elm == null) { + done = true; + alert( " only found to selectItem"+ (i-1)); + break; + } + Display.unselectItem(elm); + } + Display.selectItem(document.getElementById("selectItem"+itm)); +}; + +Display.resetVideoList = function () { + var done = false; + var i = 0; + while (done != true) { + i ++; + var elm = document.getElementById("video"+i); + if (elm == null) { + done = true; + break; + } + Display.unselectItem(elm); + widgetAPI.putInnerHTML(elm, ""); + } + +}; + +Display.setOlTitle = function (title) { + this.olTitle = title; + var elm = document.getElementById("olTitle"); + widgetAPI.putInnerHTML(elm, Display.olTitle); +}; + +Display.setStartStop = function(start, stop) { + this.olStartStop = ""; + + var digi =new Date(start * 1000); + var hours=digi.getHours(); + var minutes=digi.getMinutes(); + if (minutes<=9) + minutes='0'+minutes; + this.olStartStop = hours + ":" + minutes + " - "; + + digi =new Date(stop * 1000); + hours=digi.getHours(); + minutes=digi.getMinutes(); + if (minutes<=9) + minutes='0'+minutes; + this.olStartStop = this.olStartStop + hours + ":" + minutes; + + var elm = document.getElementById("olStartStop"); + widgetAPI.putInnerHTML(elm, Display.olStartStop); +}; + +/* +//obsolete? +Display.setTotalTime = function(total) { + this.totalTime = total; +}; +*/ +/* + +// Player.OnCurrentPlayTime +Display.setTime = function(time) { + var timePercent = (100 * time) / this.totalTime; + var timeHTML = ""; + var timeHour = 0; var timeMinute = 0; var timeSecond = 0; + var totalTimeHour = 0; var totalTimeMinute = 0; + var totalTimesecond = 0; + + if(Player.state == Player.PLAYING) + { + totalTimeHour = Math.floor(this.totalTime/3600000); + timeHour = Math.floor(time/3600000); + + totalTimeMinute = Math.floor((this.totalTime%3600000)/60000); + timeMinute = Math.floor((time%3600000)/60000); + + totalTimeSecond = Math.floor((this.totalTime%60000)/1000); + timeSecond = Math.floor((time%60000)/1000); + + timeHTML = timeHour + ":"; + + if(timeMinute == 0) + timeHTML += "00:"; + else if(timeMinute <10) + timeHTML += "0" + timeMinute + ":"; + else + timeHTML += timeMinute + ":"; + + if(timeSecond == 0) + timeHTML += "00/"; + else if(timeSecond <10) + timeHTML += "0" + timeSecond + "/"; + else + timeHTML += timeSecond + "/"; + + timeHTML += totalTimeHour + ":"; + + if(totalTimeMinute == 0) + timeHTML += "00:"; + else if(totalTimeMinute <10) + timeHTML += "0" + totalTimeMinute; + else + timeHTML += totalTimeMinute; + + if(totalTimeSecond == 0) + timeHTML += "00"; + else if(totalTimeSecond <10) + timeHTML += "0" + totalTimeSecond; + else + timeHTML += totalTimeSecond; + } + else + timeHTML = "0:00:00/0:00:00"; +}; +*/ + +Display.getHumanTimeRepresentation = function(time) { + var totalTimeHour = 0; + var totalTimeMinute = 0; + var totalTimeSecond = 0; + + totalTimeHour = Math.floor(time/3600000); + totalTimeMinute = Math.floor((time%3600000)/60000); + totalTimeSecond = Math.floor((time%60000)/1000); + + var totalTimeStr = totalTimeHour + ":"; + + if(totalTimeMinute == 0) + totalTimeStr += "00:"; + else if(totalTimeMinute <10) + totalTimeStr += "0" + totalTimeMinute + ":"; + else + totalTimeStr += totalTimeMinute + ":"; + + if(totalTimeSecond == 0) + totalTimeStr += "00"; + else if(totalTimeSecond <10) + totalTimeStr += "0" + totalTimeSecond; + else + totalTimeStr += totalTimeSecond; + + return totalTimeStr; +}; + +/* +// called by Player.OnStreamInfoReady +Display.updateTotalTime = function(time) { + Player.totalTimeStr = Display.getHumanTimeRepresentation(time); +}; +*/ + +// Player.OnCurrentPlayTime +Display.updatePlayTime = function() { +// Player.curPlayTimeStr = Display.getHumanTimeRepresentation(Player.curPlayTime); + var timeElement = document.getElementById("olTimeInfo"); + widgetAPI.putInnerHTML(timeElement, Player.curPlayTimeStr + " / " + Player.totalTimeStr); +}; + +Display.updateProgressBar = function () { + var timePercent = (Player.curPlayTime *100)/ Player.totalTime; + document.getElementById("olProgressBar").style.width = Math.round(timePercent) + "%"; +}; + +Display.updateRecBar = function (start_time, duration){ + var now = Display.pluginTime.GetEpochTime(); + + var remaining = Math.round(((start_time + duration) - now) * 100/ duration); +// alert (" remaining= " + remaining + " start= " + start_time + " dur= " + duration); + var elm = document.getElementById("olRecProgressBar"); + elm.style.display="block"; + elm.style.width = remaining + "%"; + elm.style.left = (100 - remaining) + "%"; +}; + + +Display.status = function(status) { + alert(status); + widgetAPI.putInnerHTML(this.statusDiv, status); + widgetAPI.putInnerHTML(this.statusPopup, status); +}; + +Display.showStatus = function() { + this.statusPopup.style.display="block"; +}; + +Display.hideStatus = function() { + this.statusPopup.style.display="none"; +}; + + +Display.progress = function(status) { + widgetAPI.putInnerHTML(this.statusDiv, status); +}; + + +Display.durationString = function(time) { + var timeHour = 0; + var timeMinute = 0; + var timeSecond = 0; + var res = ""; + + timeHour = Math.floor(time/3600); + timeMinute = Math.floor((time%3600)/60); + timeSecond = Math.floor(time%60); + + res = "0:"; + if (timeHour != 0) + res = timeHour + ":"; + + if(timeMinute == 0) + res += "00:"; + else if(timeMinute <10) + res += "0" + timeMinute + ":"; + else + res += timeMinute + ":"; + + if(timeSecond == 0) + res += "00"; + else if(timeSecond <10) + res += "0" + timeSecond; + else + res += timeSecond; + + return res; +}; + + +Display.handleDescription =function (selected) { + + if (Data.getCurrentItem().childs[selected].isFolder == true) { + Display.setDescription( "Dir: " +Data.getCurrentItem().childs[selected].title ); + } + else { + var itm = Data.getCurrentItem().childs[selected]; + var title = itm.title; + var prog = itm.payload.prog; + var desc = itm.payload.desc; + var length = itm.payload.dur; + + var digi = new Date(parseInt(itm.payload.start*1000)); + var mon = Display.getNumString ((digi.getMonth()+1), 2); + var day = Display.getNumString (digi.getDate(), 2); + var hour = Display.getNumString (digi.getHours(), 2); + var min = Display.getNumString (digi.getMinutes(), 2); + + var d_str =""; +// alert("handleDescription: " +Data.getCurrentItem().childs[selected].payload.desc); + var msg = ""; + if (Main.state == 1) { + // Live + var now = pluginTime.GetEpochTime(); + + d_str = hour + ":" + min; + + msg += title + "
"; + msg += ""+ prog + "
"; + msg += "
Start: " + d_str + "
"; + msg += "Duration: " + Display.durationString(length) + "h
"; + msg += "Remaining: " + Display.durationString((itm.payload.start + length - now)); + } + else { + // on-demand + d_str = mon + "/" + day + " " + hour + ":" + min; + + msg += "" + title + ""; + msg += "

" + d_str; + msg += " Duration: " + Display.durationString(length) + "h"; + } + msg += "

"+ desc; + Display.setDescription(msg); + } + +}; + +Display.getNumString =function(num, fmt) { + var res = ""; + + if (num < 10) { + for (var i = 1; i < fmt; i ++) { + res += "0"; + }; + } else if (num < 100) { + for (var i = 2; i < fmt; i ++) { + res += "0"; + }; + } + + res = res + num; + + return res; +}; + +Display.getDisplayTitle = function(item) { + var res = ""; + switch (Main.state) { + case 1: + // Live + res = item.title; + break; + case 2: + case 3: + // Recordings + if (item.isFolder == true) { +// res = "<" + Display.getNumString(item.childs.length, 3) + ">-------- " + item.title; + res = "<" + Display.getNumString(item.childs.length, 3) + ">       - " + item.title; + + } + else { + var digi = new Date(parseInt(item.payload.start*1000)); + var mon = Display.getNumString ((digi.getMonth()+1), 2); + var day = Display.getNumString (digi.getDate(), 2); + var hour = Display.getNumString (digi.getHours(), 2); + var min = Display.getNumString (digi.getMinutes(), 2); + + var d_str = mon + "/" + day + " " + hour + ":" + min; + res = d_str + " - " + item.title; + } + break; + default: + alert("ERROR: Shall be in state 1 or 2. State= " + Main.state); + break; + } + return res; +}; + +Display.setVideoList = function(selected, first) { + // + var listHTML = ""; + var first_item = selected; + if(typeof first !='undefined') { + first_item = first; + alert("Display.setVideoList first_item= " + first_item); + } + + var i=0; + alert("Display.setVideoList title= " +Data.getCurrentItem().childs[selected].title + " selected= " + selected + " first_item= " + first_item); + this.handleDescription(selected); + + for (i = 0; i <= this.LASTIDX; i++) { + if ((first_item+i) >= Data.getVideoCount()) { + listHTML = ""; + } + else { +// alert(" - title[first_item+i]= " +Data.getCurrentItem().childs[(first_item +i)].title + " i= " + i); + listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[first_item+i]); + } + this.videoList[i] = document.getElementById("video"+i); + widgetAPI.putInnerHTML(this.videoList[i], listHTML); + this.unselectItem(this.videoList[i]); + } + + this.currentWindow = (selected - first_item); + this.selectItem(this.videoList[this.currentWindow]); + + listHTML = (selected +1) + " / " + Data.getVideoCount(); + + widgetAPI.putInnerHTML(document.getElementById("videoCount"), listHTML); +}; + +Display.selectItem = function (item) { + + item.style.color = "black"; + item.style.background = "-webkit-linear-gradient(top, rgba(246,248,249,1) 0%,rgba(229,235,238,1) 50%,rgba(215,222,227,1) 51%,rgba(245,247,249,1) 100%)"; + item.style.borderRadius= "3px"; + item.style["-webkit-box-shadow"] = "2px 2px 1px 2px rgba(0,0,0, 0.5)"; + // item.style.backgroundColor = "white"; + +}; + +Display.unselectItem = function (item) { + item.style.color = "white"; + item.style.backgroundColor = "transparent"; + item.style.background = "transparent"; + item.style["-webkit-box-shadow"] = ""; +}; + + +/* + * this.currentWindow: Cursor (selected item) + */ +Display.setVideoListPosition = function(position, move) +{ + var listHTML = ""; + alert ("Display.setVideoListPosition title= " +Data.getCurrentItem().childs[position].title + " move= " +move); + + this.handleDescription(position); + + listHTML = (position + 1) + " / " + Data.getVideoCount(); + widgetAPI.putInnerHTML(document.getElementById("videoCount"), listHTML); + + + if(Data.getVideoCount() < this.LASTIDX) { + for (var i = 0; i < Data.getVideoCount(); i++) + { + if(i == position) + this.selectItem(this.videoList[i]); + else + this.unselectItem(this.videoList[i]); + + } + } + else if((this.currentWindow!=this.LASTIDX && move==Main.DOWN) || (this.currentWindow!=this.FIRSTIDX && move==Main.UP)) + { + // Just move cursor + if(move == Main.DOWN) + this.currentWindow ++; + else + this.currentWindow --; + + for (var i = 0; i <= this.LASTIDX; i++) { + if(i == this.currentWindow) + this.selectItem(this.videoList[i]); + else + this.unselectItem(this.videoList[i]); + } + } + else if(this.currentWindow == this.LASTIDX && move == Main.DOWN) { + // Next Page + if(position == this.FIRSTIDX) { + // very top element selected + this.currentWindow = this.FIRSTIDX; + + for(i = 0; i <= this.LASTIDX; i++) { + listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i]); +// listHTML = Data.getCurrentItem().childs[i].title; + widgetAPI.putInnerHTML(this.videoList[i], listHTML); + + if(i == this.currentWindow) + this.selectItem(this.videoList[i]); + else + this.unselectItem(this.videoList[i]); + } + } + else { + for(i = 0; i <= this.LASTIDX; i++) { + listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position - this.currentWindow]); +// listHTML = Data.getCurrentItem().childs[i + position - this.currentWindow].title; + widgetAPI.putInnerHTML(this.videoList[i], listHTML); + } + } + } + else if(this.currentWindow == this.FIRSTIDX && move == Main.UP) { + // Previous Page + if(position == Data.getVideoCount()-1) { + // very bottom element selected + this.currentWindow = this.LASTIDX; + + for(i = 0; i <= this.LASTIDX; i++) { + listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position - this.currentWindow]); +// listHTML = Data.getCurrentItem().childs[i + position - this.currentWindow].title; + widgetAPI.putInnerHTML(this.videoList[i], listHTML); + + if(i == this.currentWindow) + this.selectItem(this.videoList[i]); + else + this.unselectItem(this.videoList[i]); + } + } + else { + for(i = 0; i <= this.LASTIDX; i++) { + listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position]); +// listHTML = Data.getCurrentItem().childs[i + position].title; + widgetAPI.putInnerHTML(this.videoList[i], listHTML); + } + } + } +}; + +Display.setDescription = function(description) { + var descriptionElement = document.getElementById("description"); + + widgetAPI.putInnerHTML(descriptionElement, description); +}; + +//-------------------------------------------------------- +Display.bufferUpdate = function() { + // Player.bufferState + // find buffering element and show status + this.bufferingElm.style.width= Player.bufferState+ "%"; +}; + +//-------------------------------------------------------- +Display.hide = function() +{ + document.getElementById("main").style.display="none"; +}; + +Display.show = function() +{ + // cancel ongoing overlays first + this.volOlHandler.cancel(); + this.progOlHandler.cancel(); + this.popupOlHandler.cancel(); + document.getElementById("main").style.display="block"; +}; + +//--------------------------------------------------------- +Display.setVolume = function(level) +{ + document.getElementById("volumeBar").style.width = level + "%"; + + var volumeElement = document.getElementById("volumeInfo"); + + widgetAPI.putInnerHTML(volumeElement, "     " + Audio.getVolume()); +}; + +// Called by main +Display.showVolume = function() { + this.volOlHandler.show(); +}; + +//called by handle +Display.handlerShowVolume = function() { + document.getElementById("volume").style.display="block"; +}; + + +//Called by handler +Display.handlerHideVolume = function() { + document.getElementById("volume").style.display="none"; +}; + + +//--------------------------------------------------------- +Display.showPopup = function(text) { + var oldHTML = document.getElementById("popup").innerHTML; + widgetAPI.putInnerHTML(document.getElementById("popup"), oldHTML + "
" + text); + this.popupOlHandler.show(); +}; + +Display.handlerShowPopup = function() { + document.getElementById("popup").style.display="block"; + +}; + +Display.handlerHidePopup = function() { + document.getElementById("popup").style.display="none"; + widgetAPI.putInnerHTML(document.getElementById("popup"), ""); +}; + +//--------------------------------------------------------- + +Display.showProgress = function() { + this.progOlHandler.show(); +}; + +Display.handlerHideProgress = function() { + document.getElementById("overlay").style.display="none"; +}; + +Display.handlerShowProgress = function() { + + document.getElementById("overlay").style.display="block"; + if (Player.isRecording == true) { + document.getElementById("olRecProgressBar").style.display="block"; + var now = pluginTime.GetEpochTime(); + var remaining = Math.round(((Player.startTime + Player.duration) - now) * 100/ Player.duration); + alert (" remaining= " + remaining); + var elm = document.getElementById("olRecProgressBar"); + elm.style.display="block"; + elm.style.width = remaining + "%"; + elm.style.left = (100 - remaining) + "%"; + } + else + document.getElementById("olRecProgressBar").style.display="none"; + + var timePercent = (Player.curPlayTime *100)/ Player.totalTime; + + alert("show OL Progress timePercent= " + timePercent); + + document.getElementById("olProgressBar").style.width = timePercent + "%"; + + var timeElement = document.getElementById("olTimeInfo"); + widgetAPI.putInnerHTML(timeElement, Player.curPlayTimeStr + " / " + Player.totalTimeStr); + + var nowElement = document.getElementById("olNow"); + var Digital=new Date(); + var hours=Digital.getHours(); + var minutes=Digital.getMinutes(); + if (minutes<=9) + minutes='0'+minutes; + widgetAPI.putInnerHTML(nowElement, hours + ':' + minutes); +}; + + + +//---------------------------------------- +function OverlayHandler (n) { + this.pluginTime = null; + this.active = false; + this.startTime = 0; + this.hideTime = 0; + this.olDelay = 3000; // in millis + this.timeoutObj = null; + + this.handlerName = n; // readable name for debugging + this.showCallback = null; // callback, which shows the element + this.hideCallback = null; // callback, which hides the element +}; + +OverlayHandler.prototype.init = function(showcb, hidecb) { + var success = true; + this.showCallback = showcb; + this.hideCallback = hidecb; + this.pluginTime = document.getElementById("pluginTime"); + if (!this.pluginTime) { + alert(this.handlerName + " cannot aquire time plugin : " + success); + success = false; + } +// alert(this.handlerName + " is initialized"); + return success; +}; + +OverlayHandler.prototype.checkHideCallback = function () { + var pluginTime = document.getElementById("pluginTime"); + var now = pluginTime.GetEpochTime(); +// alert(that.handlerName + "checkHideCallback: now= " + now + " hideTime= " + that.hideTime + " delta= " + (now - that.hideTime)); + if (now >= this.hideTime) { +// alert(this.handlerName + "hiding " + this.handlerName + " howDur: act= " + (now - this.startTime) + " int= " + (this.hideTime-this.startTime)); + + this.olDelay = 3000; + if (this.hideCallback) { + this.hideCallback(); + } + else + alert(this.handlerName + ": No hideCallback defined - ignoring " ); + + this.active = false; + return; + } + var delay = (this.hideTime - now) * 1000; +// alert(this.handlerName + "checkHideCallback: new timeout= " +delay); + + // pass an anonymous function + var self = this; + this.timeoutObj = window.setTimeout(function() {self.checkHideCallback(); }, delay); +}; + +OverlayHandler.prototype.show = function() { + if (!this.active ) { + this.startTime = this.pluginTime.GetEpochTime(); + this.hideTime = this.startTime + (this.olDelay / 1000); + +// alert(this.handlerName + " showing " + this.handlerName + " from= " + this.startTime + " to at least= " + this.hideTime); + if (this.showCallback) { + this.showCallback(); + + var self = this; + this.timeoutObj = window.setTimeout(function() {self.checkHideCallback();}, this.olDelay); + this.active = true; + } + else + alert(this.handlerName + ": No showCallback defined - ignoring " ); + } + else { +// alert(this.handlerName + " extending showtime for " + this.handlerName + " for another " + (this.olDelay /1000)+ "sec"); + this.hideTime = this.pluginTime.GetEpochTime() + (this.olDelay /1000); + } +}; + + +OverlayHandler.prototype.cancel = function () { + if (!this.active) + return; + +// alert("cancel for handler " + this.handlerName); + if (this.hideCallback) { + this.hideCallback(); + } + else + alert(this.handlerName + ": No hideCallback defined - ignoring " ); + + this.active = false; + window.clearTimeout(this.timeoutObj); +}; \ No newline at end of file diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js new file mode 100755 index 0000000..ef92172 --- /dev/null +++ b/smarttv-client/Javascript/Main.js @@ -0,0 +1,1029 @@ +var widgetAPI = new Common.API.Widget(); +var tvKey = new Common.API.TVKeyValue(); + +var custom = window.deviceapis.customdevice || {}; + + +/* + * TODO: + * Resume + * Audio Track Select + * Screensaver (using setOnScreenSaver() from common modules) + * VDR Status on main screne + */ + + +var Main = { + state : 0, // selectScreen + selectedVideo : 0, + olTimeout : 3000, + mode : 0, + mute : 0, + + + UP : 0, + DOWN : 1, + + WINDOW : 0, + FULLSCREEN : 1, + + NMUTE : 0, + YMUTE : 1, + + defKeyHndl : null, + selectMenuKeyHndl : null, + playStateKeyHndl : null, + livePlayStateKeyHndl : null, + menuKeyHndl : null +}; + +Main.onLoad = function() { + +// Config.init("http://192.168.1.122:8000/widget.conf"); + Network.init(); + Display.init(); + + this.defKeyHndl = new cDefaulKeyHndl; + this.playStateKeyHndl = new cPlayStateKeyHndl(this.defKeyHndl); + this.livePlayStateKeyHndl = new cLivePlayStateKeyHndl(this.defKeyHndl); + this.menuKeyHndl = new cMenuKeyHndl(this.defKeyHndl); + this.selectMenuKeyHndl = new cSelectMenuKeyHndl(this.defKeyHndl); + + alert (" created KeyHandlers"); + + Config.init(); +}; + +// Called by Config, when done +Main.init = function () { + alert("Main.init()"); + if ( Player.init() && Audio.init() && Server.init() ) { + Display.setVolume( Audio.getVolume() ); + + // Start retrieving data from server + Server.dataReceivedCallback = function() + { + /* Use video information when it has arrived */ + alert("Server.dataReceivedCallback"); + Display.setVideoList(Main.selectedVideo); + + Display.show(); + }; + + // Enable key event processing + this.enableKeys(); + + Display.selectItem(document.getElementById("selectItem1")); + + document.getElementById("splashScreen").style.display="none"; + + widgetAPI.sendReadyEvent(); + } + else { + alert("Failed to initialise"); + } + +}; + +Main.log = function (msg) { + var XHRObj = new XMLHttpRequest(); + XHRObj.open("POST", Config.serverUrl + "/log", true); + XHRObj.send("CLOG: " + msg); +}; + +Main.onUnload = function() +{ + Player.deinit(); +}; + +Main.changeState = function (state) { + alert("change state: new state= " + state); + var old_state = this.state; + this.state = state; + + switch (this.state) { + case 0: + Main.selectMenuKeyHndl.select = old_state; + alert ("old Select= " + Main.selectMenuKeyHndl.select); + Display.resetSelectItems(Main.selectMenuKeyHndl.select); + + document.getElementById("selectScreen").style.display="block"; + Display.hide(); + Display.resetVideoList(); + break; + case 1: + document.getElementById("selectScreen").style.display="none"; + Display.show(); + Data.reset (); + Main.liveSelected(); + break; + case 2: + document.getElementById("selectScreen").style.display="none"; + Display.show(); + Data.reset (); + Main.recordingsSelected(); + break; + case 3: + document.getElementById("selectScreen").style.display="none"; + Display.show(); + Data.reset (); + Main.mediaSelected(); + break; + case 4: + // Options + Options.init(); + document.getElementById("selectScreen").style.display="none"; + document.getElementById("optionsScreen").style.display="block"; + Main.optionsSelected(); + break; + + } +}; + +Main.recordingsSelected = function() { + Player.stopCallback = function() { + // + var msg = "devid:" + Network.getMac() + "\n"; + msg += "title:" + Data.getCurrentItem().childs[Main.selectedVideo].title + "\n"; + msg += "start:" +Data.getCurrentItem().childs[Main.selectedVideo].payload.start + "\n"; + msg += "resume:"+ (Player.curPlayTime/1000) + "\n" ; + + var XHRObj = new XMLHttpRequest(); + XHRObj.open("POST", Config.serverUrl + "/resume", true); + XHRObj.send(msg); + + Display.show(); + }; + Player.isLive = false; + Server.setSort(true); + if (Config.format == "") { + Server.fetchVideoList(Config.serverUrl + "/recordings.xml?model=samsung"); /* Request video information from server */ + alert("fetchVideoList from: " + Config.serverUrl + "/recordings.xml?model=samsung"); + } + else { + Main.log("Using format " + Config.format); + if (Config.format == "") + Server.fetchVideoList(Config.serverUrl + "/recordings.xml?model=samsung&has4hd=false"); /* Request video information from server */ + else + Server.fetchVideoList(Config.serverUrl + "/recordings.xml?model=samsung&has4hd=false&type="+Config.format); /* Request video information from server */ + } +}; + +Main.serverError = function(errorcode) { + if (Server.retries == 0) { + switch (this.state) { + case 1: // live + Server.fetchVideoList( Config.serverUrl + "/channels.xml?mode=nodesc"); /* Request video information from server */ + break; + } + } +}; + +Main.liveSelected = function() { + Server.retries = 0; + Player.stopCallback = function() { + }; + Player.isLive = true; + Server.setSort(false); + Server.errorCallback = Main.serverError; + Server.fetchVideoList(Config.serverUrl + "/channels.xml"); /* Request video information from server */ +}; + +Main.mediaSelected = function() { + Player.stopCallback = function() { + // + Display.show(); + }; + Player.isLive = false; + Server.setSort(true); + Server.fetchVideoList(Config.serverUrl + "/media.xml"); /* Request video information from server */ +}; + +Main.optionsSelected = function() { + alert ("Main.optionsSelected"); +}; + +Main.enableKeys = function() +{ + document.getElementById("anchor").focus(); +}; + +Main.keyDown = function() { + switch (this.state) { + case 0: + // selectView + this.selectMenuKeyHndl.handleKeyDown(); + break; + case 1: + // Live + alert("Live - Main.keyDown PlayerState= " + Player.getState()); + if(Player.getState() == Player.STOPPED) { + // Menu Key + this.menuKeyHndl.handleKeyDown(); + } + else { + // Live State Keys + this.livePlayStateKeyHndl.handleKeyDown(); + }; + + break; + case 2: + case 3: + // recordings + alert("Recordings - Main.keyDown PlayerState= " + Player.getState()); + if(Player.getState() == Player.STOPPED) { + // Menu Key + this.menuKeyHndl.handleKeyDown(); + } + else { + // Play State Keys + this.playStateKeyHndl.handleKeyDown(); + }; + + break; + case 4: + alert ("ERROR: Wrong State"); + break; + }; +}; + +Main.playItem = function (url) { + alert(Main.state + " playItem for " +Data.getCurrentItem().childs[Main.selectedVideo].payload.link); + var start_time = Data.getCurrentItem().childs[Main.selectedVideo].payload.start; + var duration = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur; + var now = Display.pluginTime.GetEpochTime(); + document.getElementById("olRecProgressBar").style.display="none"; + + switch (this.state) { + case 1: + // Live + Display.setOlTitle(Data.getCurrentItem().childs[Main.selectedVideo].payload.prog); + Display.setStartStop (start_time, (start_time + duration)); + Player.isLive = true; + Player.bufferState = 0; + Player.isRecording = false; + Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; +// Display.updateTotalTime(Player.totalTime); + var digi = new Date((Data.getCurrentItem().childs[Main.selectedVideo].payload.start*1000)); + alert (" Date(): StartTime= " + digi.getHours() + ":" + digi.getMinutes() + ":" + digi.getSeconds()); +// Player.cptOffset = (now - Data.getCurrentItem().childs[Main.selectedVideo].payload.start) * 1000; + Player.setCurrentPlayTimeOffset((now - Data.getCurrentItem().childs[Main.selectedVideo].payload.start) * 1000); + Player.OnCurrentPlayTime(0); + alert ("Live now= " + now + " StartTime= " + Data.getCurrentItem().childs[Main.selectedVideo].payload.start + " offset= " +Player.cptOffset ); + alert("Live Content= " + Data.getCurrentItem().childs[Main.selectedVideo].title + " dur= " + Data.getCurrentItem().childs[Main.selectedVideo].payload.dur); + break; + case 2: + case 3: + Player.setCurrentPlayTimeOffset(0); +// Player.cptOffset = 0; + Player.isLive = false; + Player.isRecording = false; + alert(" playItem: now= " + now + " start_time= " + start_time + " dur= " + duration + " (Start + Dur - now)= " + ((start_time + duration) -now)); + if ((now - (start_time + duration)) < 0) { + // still recording + alert("*** Still Recording! ***"); + Player.isRecording = true; + Player.startTime = start_time; + Player.duration = duration; + Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; + document.getElementById("olRecProgressBar").style.display="block"; + + Display.updateRecBar(start_time, duration); + } + else { + document.getElementById("olRecProgressBar").display="none"; + } + Display.setOlTitle(Data.getCurrentItem().childs[Main.selectedVideo].title); + Display.olStartStop = ""; + break; + }; + + Player.setVideoURL( Data.getCurrentItem().childs[Main.selectedVideo].payload.link); + Player.playVideo(); +}; + +Main.selectPageUp = function(up) { + if (this.selectedVideo == 0) { + Main.changeState(0); + return; + }; + + this.selectedVideo = (this.selectedVideo - (Display.LASTIDX + 1)); + if (this.selectedVideo < 0) { + this.selectedVideo = 0; + } + + var first_item = this.selectedVideo - Display.currentWindow; + if (first_item < 0 ) + first_item = 0; + alert("selectPageUp: this.selectedVideo= " + this.selectedVideo + " first_item= " + first_item); + + Display.setVideoList(this.selectedVideo, first_item); +}; + +Main.selectPageDown = function(down) { + this.selectedVideo = (this.selectedVideo + Display.LASTIDX + 1); + + if (this.selectedVideo >= Data.getVideoCount()) { + this.selectedVideo = Data.getVideoCount() -1; + } + var first_item = this.selectedVideo - Display.currentWindow; + + alert("selectPageDown: this.selectedVideo= " + this.selectedVideo + " first_item= " + first_item); + Display.setVideoList(this.selectedVideo, first_item); +}; + +Main.nextVideo = function(no) { + this.selectedVideo = (this.selectedVideo + no) % Data.getVideoCount(); + alert("nextVideo= " + this.selectedVideo); +}; + +Main.previousVideo = function(no) { + + this.selectedVideo = (this.selectedVideo - no); + if (this.selectedVideo < 0) { + this.selectedVideo += Data.getVideoCount(); + } + alert("previousVideo= " + this.selectedVideo); + +}; + +Main.selectNextVideo = function(down) +{ + Player.stopVideo(); + Main.nextVideo(1); + +// this.updateCurrentVideo(down); + Display.setVideoListPosition(this.selectedVideo, down); +}; + +Main.selectPreviousVideo = function(up) +{ + Player.stopVideo(); + Main.previousVideo(1); + +// this.updateCurrentVideo(up); + Display.setVideoListPosition(this.selectedVideo, up); +}; + + + +Main.setMuteMode = function() +{ + if (this.mute != this.YMUTE) + { + var volumeElement = document.getElementById("volumeInfo"); + //Audio.plugin.SetSystemMute(true); + Audio.plugin.SetUserMute(true); + document.getElementById("volumeBar").style.backgroundImage = "url(Images/muteBar.png)"; + document.getElementById("volumeIcon").style.backgroundImage = "url(Images/mute.png)"; + widgetAPI.putInnerHTML(volumeElement, "MUTE"); + this.mute = this.YMUTE; + } +}; + +Main.noMuteMode = function() +{ + if (this.mute != this.NMUTE) + { + Audio.plugin.SetUserMute(false); + document.getElementById("volumeBar").style.backgroundImage = "url(Images/volumeBar.png)"; + document.getElementById("volumeIcon").style.backgroundImage = "url(Images/volume.png)"; + Display.setVolume( Audio.getVolume() ); + this.mute = this.NMUTE; + } +}; + +Main.muteMode = function() +{ + switch (this.mute) + { + case this.NMUTE: + this.setMuteMode(); + break; + + case this.YMUTE: + this.noMuteMode(); + break; + + default: + alert("ERROR: unexpected mode in muteMode"); + break; + } +}; + +// ----------------------------------------------- + +Main.getKeyCode = function(code) { + var res = ""; + switch(code) { + case tvKey.KEY_1: + res = "KEY_1"; + break; + case tvKey.KEY_2: + res = "KEY_2"; + break; + case tvKey.KEY_3: + res = "KEY_3"; + break; + case tvKey.KEY_4: + res = "KEY_4"; + break; + case tvKey.KEY_5: + res = "KEY_5"; + break; + case tvKey.KEY_6: + res = "KEY_6"; + break; + case tvKey.KEY_7: + res = "KEY_7"; + break; + case tvKey.KEY_8: + res = "KEY_8"; + break; + case tvKey.KEY_9: + res = "KEY_9"; + break; + + case tvKey.KEY_TOOLS: + res = "KEY_TOOLS"; + break; + + case tvKey.KEY_TOOLS: + res = "KEY_TOOLS"; + break; + + case tvKey.KEY_TOOLS: + res = "KEY_TOOLS"; + break; + + case tvKey.KEY_UP: + res = "KEY_UP"; + break; + + case tvKey.KEY_DOWN: + res = "KEY_DOWN"; + break; + + case tvKey.KEY_LEFT: + res = "KEY_LEFT"; + break; + + case tvKey.KEY_RIGHT: + res = "KEY_RIGHT"; + break; + + case tvKey.KEY_WHEELDOWN: + res = "KEY_WHEELDOWN"; + break; + + case tvKey.KEY_WHEELUP: + res = "KEY_WHEELUP"; + break; + + case tvKey.KEY_ENTER: + res = "KEY_ENTER"; + break; + + case tvKey.KEY_INFO: + res = "KEY_INFO"; + break; + + case tvKey.KEY_EXIT: + res = "KEY_EXIT"; + break; + + case tvKey.KEY_RETURN: + res = "KEY_RETURN"; + break; + + case tvKey.KEY_RED: + res = "KEY_RED"; + break; + + case tvKey.KEY_GREEN: + res = "KEY_GREEN"; + break; + + case tvKey.KEY_YELLOW: + res = "KEY_YELLOW"; + break; + + case tvKey.KEY_BLUE: + res = "KEY_BLUE"; + break; + + case tvKey.KEY_INFOLINK: + res = "KEY_INFOLINK"; + break; + + case tvKey.KEY_RW: + res = "KEY_RW"; + break; + + case tvKey.KEY_PAUSE: + res = "KEY_PAUSE"; + break; + + case tvKey.KEY_FF: + res = "KEY_FF"; + break; + + case tvKey.KEY_PLAY: + res = "KEY_PLAY"; + break; + + case tvKey.KEY_STOP: + res = "KEY_STOP"; + break; + + case tvKey.KEY_EMPTY: + res = "KEY_EMPTY"; + break; + + case tvKey.KEY_PRECH: + res = "KEY_PRECH"; + break; + + case tvKey.KEY_SOURCE: + res = "KEY_SOURCE"; + break; + + case tvKey.KEY_CHLIST: + res = "KEY_CHLIST"; + break; + + case tvKey.KEY_MENU: + res = "KEY_MENU"; + break; + + case tvKey.KEY_WLINK: + res = "KEY_WLINK"; + break; + + case tvKey.KEY_CC: + res = "KEY_CC"; + break; + + case tvKey.KEY_CONTENT: + res = "KEY_CONTENT"; + break; + + case tvKey.KEY_FAVCH: + res = "KEY_FAVCH"; + break; + + case tvKey.KEY_REC: + res = "KEY_REC"; + break; + + case tvKey.KEY_EMODE: + res = "KEY_EMODE"; + break; + + case tvKey.KEY_DMA: + res = "KEY_DMA"; + break; + + case tvKey.KEY_VOL_UP: + res = "KEY_VOL_UP"; + break; + + case tvKey.KEY_VOL_DOWN: + res = "KEY_VOL_DOWN"; + break; + + case tvKey.KEY_PANEL_CH_UP: + res = "KEY_PANEL_CH_UP"; + break; + + case tvKey.KEY_PANEL_CH_DOWN: + res = "KEY_PANEL_CH_DOWN"; + break; + + case tvKey.KEY_PANEL_VOL_UP: + res = "KEY_PANEL_VOL_UP"; + break; + + case tvKey.KEY_PANEL_VOL_DOWN: + res = "KEY_PANEL_VOL_DOWN"; + break; + + case tvKey.KEY_PANEL_ENTER: + res = "KEY_PANEL_ENTER"; + break; + + case tvKey.KEY_PANEL_RETURN: + res = "KEY_PANEL_RETURN"; + break; + + case tvKey.KEY_PANEL_SOURCE: + res = "KEY_PANEL_SOURCE"; + break; + + case tvKey.KEY_PANEL_MENU: + res = "KEY_PANEL_MENU"; + break; + + case tvKey.KEY_PANEL_POWER: + res = "KEY_PANEL_POWER"; + break; + + + default: + res = "Unknown Key (" + code + ")"; + break; + } + return res; +}; + +//--------------------------------------------------- +// PlayState Key Handler +//--------------------------------------------------- + +function cPlayStateKeyHndl(def_hndl) { + this.defaultKeyHandler = def_hndl; + this.handlerName = "PlayStateKeyHanlder"; + alert(this.handlerName + " created"); + +}; + + +cPlayStateKeyHndl.prototype.handleKeyDown = function () { + var keyCode = event.keyCode; + + if(Player.getState() == Player.STOPPED) { + alert("ERROR: Wrong state - STOPPED"); + return; + } + + alert(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); + + switch(keyCode) + { + case tvKey.KEY_1: + alert("KEY_1 pressed"); + Display.showProgress(); + Player.jumpToVideo(10); + break; + case tvKey.KEY_2: + alert("KEY_2 pressed"); + Display.showProgress(); + Player.jumpToVideo(20); + break; + case tvKey.KEY_3: + alert("KEY_3 pressed"); + Display.showProgress(); + Player.jumpToVideo(30); + break; + case tvKey.KEY_4: + alert("KEY_4 pressed"); + Display.showProgress(); + Player.jumpToVideo(40); + break; + case tvKey.KEY_5: + alert("KEY_5 pressed"); + Display.showProgress(); + Player.jumpToVideo(50); + break; + case tvKey.KEY_6: + alert("KEY_6 pressed"); + Display.showProgress(); + Player.jumpToVideo(60); + break; + case tvKey.KEY_7: + alert("KEY_7 pressed"); + Display.showProgress(); + Player.jumpToVideo(70); + break; + case tvKey.KEY_8: + alert("KEY_8 pressed"); + Display.showProgress(); + Player.jumpToVideo(80); + break; + case tvKey.KEY_9: + alert("KEY_9 pressed"); + Display.showProgress(); + Player.jumpToVideo(90); + break; + + case tvKey.KEY_RIGHT: + case tvKey.KEY_FF: + alert("FF"); + Display.showProgress(); + Player.skipForwardVideo(); + break; + + case tvKey.KEY_LEFT: + case tvKey.KEY_RW: + alert("RW"); + Display.showProgress(); + Player.skipBackwardVideo(); + break; + + case tvKey.KEY_ENTER: + case tvKey.KEY_PLAY: + case tvKey.KEY_PANEL_ENTER: + alert("ENTER"); + if(Player.getState() == Player.PAUSED) { + Player.resumeVideo(); + } + Display.showProgress(); + break; + case tvKey.KEY_RETURN: + case tvKey.KEY_PANEL_RETURN: + case tvKey.KEY_STOP: + alert("STOP"); +// Player.setWindow(); + Player.stopVideo(); + break; + case tvKey.KEY_PAUSE: + alert("PAUSE"); + Player.pauseVideo(); + break; + + case tvKey.KEY_UP: + case tvKey.KEY_PANEL_VOL_UP: + case tvKey.KEY_VOL_UP: + alert("VOL_UP"); + Display.showVolume(); + if(Main.mute == 0) + Audio.setRelativeVolume(0); + break; + + case tvKey.KEY_DOWN: + case tvKey.KEY_PANEL_VOL_DOWN: + case tvKey.KEY_VOL_DOWN: + alert("VOL_DOWN"); + Display.showVolume(); + if(Main.mute == 0) + Audio.setRelativeVolume(1); + break; + + default: + alert("Calling Default Key Hanlder"); + this.defaultKeyHandler.handleDefKeyDown(keyCode); + break; + } +}; + + +//--------------------------------------------------- +// Live Play State Key Handler +//--------------------------------------------------- + +function cLivePlayStateKeyHndl(def_hndl) { + this.defaultKeyHandler = def_hndl; + this.handlerName = "LivePlayStateKeyHanlder"; + alert(this.handlerName + " created"); + +}; + + +cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { + var keyCode = event.keyCode; + + if(Player.getState() == Player.STOPPED) { + alert("ERROR: Wrong state - STOPPED"); + return; + } + + alert(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); + + switch(keyCode) { + case tvKey.KEY_1: + case tvKey.KEY_CH_UP: + alert("Prog Up"); + Display.showProgress(); + Player.stopVideo(); + Main.previousVideo(1); + + Main.playItem(); + break; + + case tvKey.KEY_4: + case tvKey.KEY_CH_DOWN: + alert("Prog Down"); + Display.showProgress(); + Player.stopVideo(); + Main.nextVideo(1); + + Main.playItem(); + break; + + case tvKey.KEY_ENTER: + case tvKey.KEY_PLAY: + case tvKey.KEY_PANEL_ENTER: + alert("ENTER"); + Display.hide(); + Display.showProgress(); + break; + case tvKey.KEY_LEFT: + case tvKey.KEY_RETURN: + case tvKey.KEY_PANEL_RETURN: + case tvKey.KEY_STOP: + alert("STOP"); + Player.stopVideo(); + Display.setVideoList(Main.selectedVideo); + Display.show(); + break; + case tvKey.KEY_PAUSE: + alert("PAUSE"); + break; + + case tvKey.KEY_UP: + case tvKey.KEY_PANEL_VOL_UP: + case tvKey.KEY_VOL_UP: + alert("VOL_UP"); + Display.showVolume(); + if(Main.mute == 0) + Audio.setRelativeVolume(0); + break; + + case tvKey.KEY_DOWN: + case tvKey.KEY_PANEL_VOL_DOWN: + case tvKey.KEY_VOL_DOWN: + alert("VOL_DOWN"); + Display.showVolume(); + if(Main.mute == 0) + Audio.setRelativeVolume(1); + break; + + default: + this.defaultKeyHandler.handleDefKeyDown(keyCode); + break; + } +}; + +//--------------------------------------------------- +//Menu Key Handler +//--------------------------------------------------- +function cMenuKeyHndl (def_hndl) { + this.defaultKeyHandler = def_hndl; + this.handlerName = "MenuKeyHandler"; + alert(this.handlerName + " created"); + +}; + +cMenuKeyHndl.prototype.handleKeyDown = function () { + var keyCode = event.keyCode; + alert(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); + + switch(keyCode) { + + + case tvKey.KEY_RIGHT: + alert("Right"); + Main.selectPageDown(Main.DOWN); + break; + + case tvKey.KEY_LEFT: + alert("Left"); + Main.selectPageUp(Main.UP); + break; + + case tvKey.KEY_ENTER: + case tvKey.KEY_PLAY: + case tvKey.KEY_PANEL_ENTER: + alert("ENTER"); + + if (Data.getCurrentItem().childs[Main.selectedVideo].isFolder == true) { + alert ("selectFolder= " +Main.selectedVideo); + Data.selectFolder(Main.selectedVideo); + Main.selectedVideo= 0; + Display.setVideoList(Main.selectedVideo); + } + else{ + Display.hide(); + Display.showProgress(); + + Main.playItem(); + } + break; + + case tvKey.KEY_EXIT: + case tvKey.KEY_RETURN: + case tvKey.KEY_PANEL_RETURN: + if (Data.isRootFolder() == true) { + alert ("root reached"); + Main.changeState(0); + } + else { + Main.selectedVideo = Data.folderUp(); + alert("folderUp selectedVideo= " + Main.selectedVideo); + Display.setVideoList(Main.selectedVideo); + } + break; + + + case tvKey.KEY_DOWN: + alert("DOWN"); + Main.selectNextVideo(Main.DOWN); + break; + + case tvKey.KEY_UP: + alert("UP"); + Main.selectPreviousVideo(Main.UP); + break; + + default: + this.defaultKeyHandler.handleDefKeyDown(keyCode); + break; + } +}; + + +//--------------------------------------------------- +// Select Menu Key Handler +//--------------------------------------------------- +function cSelectMenuKeyHndl (def_hndl) { + this.defaultKeyHandler = def_hndl; + this.handlerName = "SelectMenuKeyHandler"; + alert(this.handlerName + " created"); + + this.select = 1; + this.selectMax = 4; // Highest Select Entry +}; + +cSelectMenuKeyHndl.prototype.handleKeyDown = function () { + var keyCode = event.keyCode; + alert(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); + + switch(keyCode) { + + case tvKey.KEY_ENTER: + case tvKey.KEY_PLAY: + case tvKey.KEY_PANEL_ENTER: + alert("ENTER"); + alert ("CurSelect= " + this.select); + + Main.changeState (this.select); + + case tvKey.KEY_DOWN: + Display.unselectItem(document.getElementById("selectItem"+this.select)); + if (++this.select > this.selectMax) + this.select = 1; + Display.selectItem(document.getElementById("selectItem"+this.select)); + alert("DOWN " +this.select); + break; + + case tvKey.KEY_UP: + Display.unselectItem(document.getElementById("selectItem"+this.select)); + + if (--this.select < 1) + this.select = this.selectMax; + Display.selectItem(document.getElementById("selectItem"+this.select)); + + alert("UP "+ this.select); + break; + default: + this.defaultKeyHandler.handleDefKeyDown(keyCode); + break; + } +}; + + +//--------------------------------------------------- +// Default Key Handler +//--------------------------------------------------- + +function cDefaulKeyHndl() { + this.handlerName = "DefaultKeyHanlder"; + alert(this.handlerName + " created"); +}; + +cDefaulKeyHndl.prototype.handleDefKeyDown = function (keyCode) { + alert("cDefaulKeyHndl::handleKeyDown: " + Main.getKeyCode(keyCode)); + + switch(keyCode) { + case tvKey.KEY_EXIT: + alert(this.handlerName +"Exit"); + Player.stopVideo(); + widgetAPI.sendReturnEvent(); + break; + + case tvKey.KEY_VOL_UP: + alert(this.handlerName + "VOL_UP"); + Display.showVolume(); + if(Main.mute == 0) + Audio.setRelativeVolume(0); + break; + + case tvKey.KEY_VOL_DOWN: + alert(this.handlerName + "VOL_DOWN"); + Display.showVolume(); + if(Main.mute == 0) + Audio.setRelativeVolume(1); + break; + case tvKey.KEY_MUTE: + alert(this.handlerName + "MUTE"); + Main.muteMode(); + break; + default: + alert(this.handlerName + "Unhandled key"); + break; + } +}; + + +// --------------------------------------------- diff --git a/smarttv-client/Javascript/Network.js b/smarttv-client/Javascript/Network.js new file mode 100755 index 0000000..1dfcafc --- /dev/null +++ b/smarttv-client/Javascript/Network.js @@ -0,0 +1,25 @@ +var Network = { + plugin : null, + ownMac : "", + ownGw : "" +}; + +Network.init = function () { + this.plugin = document.getElementById("pluginNetwork"); + var nw_type = this.plugin.GetActiveType(); + if ((nw_type == 0) || (nw_type == 1)) { + this.ownMac = this.plugin.GetMAC(nw_type); + this.ownGw = this.plugin.GetGateway(nw_type); + } + alert( "ownMac= " + this.ownMac); + alert ("ownGw= " + this.ownGw); +} ; + + +Network.getMac = function () { + return this.ownMac; +}; + +Network.getGateway = function () { + return this.ownGw; +}; \ No newline at end of file diff --git a/smarttv-client/Javascript/Options.js b/smarttv-client/Javascript/Options.js new file mode 100755 index 0000000..d60a9ff --- /dev/null +++ b/smarttv-client/Javascript/Options.js @@ -0,0 +1,66 @@ +var Options = { + imeBox : null +}; + +Options.init = function() { + _g_ime.Recog_use_YN = false; + _g_ime.keySet = '12key'; + + this.imeBox = new IMEShell("widgetServerAddr", Options.onImeCreated, 'de'); + alert ("Options Initiated"); +}; + +Options.onComplete = function () { + alert("Colpleted"); +}; + +Options.onEnter = function () { + alert("Enter: " + document.getElementById("widgetServerAddr").value ); + Config.updateContext(document.getElementById("widgetServerAddr").value); + + document.getElementById('widgetServerAddr').blur(); + + document.getElementById("optionsScreen").style.display="none"; + + if (Config.firstLaunch == true) + Main.state = 1; + Main.changeState(0); + + Config.fetchConfig(); +// Main.enableKeys(); +}; + +Options.onBlue = function () { + var val = document.getElementById("widgetServerAddr").value + "."; + Options.imeBox.setString(val); +}; + +Options.onImeCreated = function(obj) { +// _g_ime.keySet ("12key"); +// obj.setKeySetFunc('12key'); + + obj.setKeyFunc(tvKey.KEY_RETURN, function(keyCode) { widgetAPI.sendReturnEvent(); return false; } ); + obj.setKeyFunc(tvKey.KEY_EXIT, function(keyCode) { widgetAPI.sendExitEvent(); return false; } ); + + obj.setKeypadPos(650, 135); + obj.setWordBoxPos(18, 6); + obj.setKeyFunc(tvKey.KEY_BLUE, Options.onBlue); + + obj.setString(Config.serverAddr); + obj.setEnterFunc(Options.onEnter); + + if (obj.setMode("_num") == false) { + Main.log("obj.setMode(\"_num\") returns false"); + } + + Options.imeBox.setOnCompleteFunc(Options.onComplete); + + Options.onReady (); +}; + +Options.onReady = function () { + document.getElementById('widgetServerAddr').focus(); + alert ("KeySet= " + this.imeBox.getKeySet()); +}; + + diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js new file mode 100755 index 0000000..a04c26f --- /dev/null +++ b/smarttv-client/Javascript/Player.js @@ -0,0 +1,350 @@ +var Player = +{ + plugin : null, + isLive : false, + isRecording : false, + + startTime : 0, + duration : 0, + + state : 0, + cptOffset : 0, // millis + curPlayTime : 0, // millis + totalTime : -1, // millis + + curPlayTimeStr : "00:00:00", // millis + totalTimeStr : "", + stopCallback : null, /* Callback function to be set by client */ + errorCallback : null, + originalSource : null, + + bufferState : 0, // buffer state in % + + STOPPED : 0, + PLAYING : 1, + PAUSED : 2, + FORWARD : 3, + REWIND : 4 +}; + +Player.init = function() { + var success = true; + alert("success vale : " + success); + this.state = this.STOPPED; + + this.plugin = document.getElementById("pluginPlayer"); + + if (!this.plugin) + { + alert("success vale this.plugin : " + success); + success = false; + } + + var vermsg = this.plugin.GetPlayerVersion(); + alert ("player plugin version: " +vermsg); + + this.plugin.OnCurrentPlayTime = 'Player.OnCurrentPlayTime'; + this.plugin.OnStreamInfoReady = 'Player.OnStreamInfoReady'; + this.plugin.OnBufferingStart = 'Player.onBufferingStart'; + this.plugin.OnBufferingProgress = 'Player.onBufferingProgress'; + this.plugin.OnBufferingComplete = 'Player.onBufferingComplete'; + this.plugin.OnConnectionFailed = 'Player.OnConnectionFailed'; // fails to connect to the streaming server + this.plugin.OnStreamNotFound = 'Player.OnStreamNotFound'; // 404 file not found + this.plugin.OnNetworkDisconnected = 'Player.OnNetworkDisconnected'; // when the ethernet is disconnected or the streaming server stops supporting the content in the middle of streaming. + this.plugin.OnRenderingComplete = 'Player.OnRenderingComplete'; + + alert("success= " + success); + return success; +}; + +Player.deinit = function() +{ + alert("Player deinit !!! " ); + + if (this.plugin) + { + this.plugin.Stop(); + } +}; + +Player.setWindow = function() { +// this.plugin.SetDisplayArea(458, 58, 472, 270); +}; + +Player.setFullscreen = function() { + this.plugin.SetDisplayArea(0, 0, 960, 540); +}; + +Player.setBuffer = function (btr){ + var res = true; +// var m_bitrate = Config.tgtBufferBitrate; +// var buffer_sec = Config.totalBufferDuration; +// var init_buffer_perc = Config.initialBuffer; +// var pend_buffer_perc = Config.pendingBuffer; + + var buffer_byte = (Config.totalBufferDuration * Config.tgtBufferBitrate) / 8.0; + + Main.log("Seting TotalBufferSize to " + Math.round(buffer_byte) +"Byte dur= " +Config.totalBufferDuration + "sec init_buffer_perc= " +Config.initialBuffer +"% pend_buffer_perc= " +Config.pendingBuffer + "% initialTimeOut= " +Config.initialTimeOut + "sec"); + + //The SetTotalBufferSize function sets the streaming buffer size of media player. + res = this.plugin.SetTotalBufferSize(Math.round(buffer_byte)); + if (res == false) { + Display.showPopup("SetTotalBufferSize(" + Math.round(buffer_byte) +") returns error"); + Main.log("SetTotalBufferSize(" + Math.round(buffer_byte) +") returns error"); + } + + // The SetInitialBuffer function sets the first buffering size as percentage of buffer size before starting playback. + res = this.plugin.SetInitialBuffer(Math.round( buffer_byte * Config.initialBuffer/ 100.0)); + if (res == false) { + Display.showPopup("SetInitialBuffer(" + Math.round(buffer_byte * Config.initialBuffer/ 100.0) +") returns error"); + Main.log("SetInitialBuffer(" + Math.round(buffer_byte * Config.initialBuffer/ 100.0) +") returns error"); + } + //he SetInitialTimeOut function sets the maximum time out value for initial buffering before starting playback. + res = this.plugin.SetInitialTimeOut(Config.initialTimeOut); + if (res == false) { + Display.showPopup("SetInitialTimeOut(" + 2 +") returns error"); + Main.log("SetInitialTimeOut(" + 2 +") returns error"); + } + + // The SetPendingBuffer function sets the size of a buffer as percentage of total buffer size that media player goes out from buffering status. + res = this.plugin.SetPendingBuffer(Math.round(buffer_byte * Config.pendingBuffer /100.0)); + if (res == false) { + Display.showPopup("SetPendingBuffer(" + Math.round(buffer_byte * Config.pendingBuffer /100.0) +") returns error"); + Main.log("SetPendingBuffer(" + Math.round(buffer_byte * Config.pendingBuffer /100.0) +") returns error"); + } + +}; + +Player.setVideoURL = function(url) { + this.url = url; + alert("URL = " + this.url); +}; + +Player.setCurrentPlayTimeOffset = function(val) { + // val in milli sec + this.cptOffset = val; +// Display.showPopup("CurrentPlayTimeOffset= " + this.cptOffset); +}; + +Player.playVideo = function() { + if (this.url == null) { + alert("No videos to play"); + } + else + { + +// Player.curPlayTime = 0; + Display.updatePlayTime(); + + Display.status("Play"); + Display.showStatus(); + Display.showProgress(); + this.state = this.PLAYING; + +// if (this.plugin.InitPlayer(this.url) == false) +// Display.showPopup("InitPlayer returns false"); + + Player.setBuffer(15000000.0); + + alert ("StartPlayback for " + this.url); +// if (this.plugin.StartPlayback() == false) +// Display.showPopup("StartPlayback returns false"); + + this.plugin.Play( this.url ); + Audio.plugin.SetSystemMute(false); + } +}; + +Player.pauseVideo = function() { + Display.showProgress(); + Main.log("pauseVideo"); + + this.state = this.PAUSED; + Display.status("Pause"); + var res = this.plugin.Pause(); + if (res == false) + Display.showPopup("pause ret= " + ((res == true) ? "True" : "False")); +}; + +Player.stopVideo = function() { + if (this.state != this.STOPPED) { + this.state = this.STOPPED; + Display.status("Stop"); + this.plugin.Stop(); + Player.bufferState = 0; +// Display.setTime(0); + + if (this.stopCallback) { + this.stopCallback(); + } + } + else { + alert("Ignoring stop request, not in correct state"); + } +}; + +Player.resumeVideo = function() { + Main.log("resumeVideo"); + Display.showProgress(); + this.state = this.PLAYING; + Display.status("Play"); + var res = this.plugin.Resume(); + if (res == false) + Display.showPopup("resume ret= " + ((res == true) ? "True" : "False")); + +}; + +Player.jumpToVideo = function(percent) { + if (this.isLive == true) { + return; + } + Player.bufferState = 0; + Display.showProgress(); + if (this.state != this.PLAYING) { + alert ("Player not Playing"); + return; + } + if (this.totalTime == -1 && this.isLive == false) + this.totalTime = this.plugin.GetDuration(); + var tgt = Math.round(((percent-2)/100.0) * this.totalTime/ 1000.0); + + alert("jumpToVideo= " + percent + "% of " + (this.totalTime/1000) + "sec tgt = " + tgt + "sec curPTime= " + (this.curPlayTime/1000) +"sec"); +// Display.showPopup("jumpToVideo= " + percent + "% of " + (this.totalTime/1000) + "sec
--> tgt = " + tgt + "sec curPTime= " + (this.curPlayTime/1000)+"sec"); + this.plugin.Stop(); + + Display.showStatus(); + + var res = this.plugin.ResumePlay(this.url, tgt ); + if (res == false) + Display.showPopup("ResumePlay ret= " + ((res == true) ? "True" : "False")); +}; + +Player.skipForwardVideo = function() { + var res = this.plugin.JumpForward(Config.skipDuration); + if (res == false) + Display.showPopup("Jump Forward ret= " + ((res == true) ? "True" : "False")); +}; + +Player.skipBackwardVideo = function() { + var res = this.plugin.JumpBackward(Config.skipDuration); + if (res == false) + Display.showPopup("Jump Backward ret= " + ((res == true) ? "True" : "False")); +}; + +Player.getState = function() { + return this.state; +}; + +// ------------------------------------------------ +// Global functions called directly by the player +//------------------------------------------------ + +Player.onBufferingStart = function() { + Main.log("Buffer Start: " + Player.curPlayTime); + Player.bufferStartTime = new Date().getTime(); + + Player.bufferState = 0; + Display.bufferUpdate(); + // should trigger from here the overlay + Display.showProgress(); + Display.status("Buffering..."); + Display.showStatus(); +}; + +Player.onBufferingProgress = function(percent) +{ + // should trigger from here the overlay + Display.status("Buffering:" + percent + "%"); + + Player.bufferState = percent; + Display.bufferUpdate(); + Display.showProgress(); +}; + +Player.onBufferingComplete = function() { + Display.status("Play"); + Display.hideStatus(); + + Main.log("Buffer Completed - Buffering Duration= " + (new Date().getTime() - Player.bufferStartTime) + " ms"); + + Player.bufferState = 100; + Display.bufferUpdate(); + Display.showProgress(); + + Player.setFullscreen(); + Display.hide(); + + Main.log("onBufferingComplete "); +/* Player.pauseVideo(); + window.setTimeout(Player.resumeVideo, 1000); */ +}; + + +Player.OnCurrentPlayTime = function(time) { + Player.curPlayTime = parseInt(time) + parseInt(Player.cptOffset); + +// alert ("OnCurrentPlayTime time= " + time + " this.curPlayTime= " +this.curPlayTime ); +// Display.setTime(Player.curPlayTime); + + // Update the Current Play Progress Bar + Display.updateProgressBar(); + + if (Player.isRecording == true) { + Display.updateRecBar(Player.startTime, Player.duration); + } + Player.curPlayTimeStr = Display.getHumanTimeRepresentation(Player.curPlayTime); + Display.updatePlayTime(); +}; + + +Player.OnStreamInfoReady = function() { + alert("*** OnStreamInfoReady ***"); +// Display.showPopup("*** OnStreamInfoReady *** "); +// Display.showPopup("GetCurrentBitrates= " + Player.plugin.GetCurrentBitrates()); + Main.log("GetCurrentBitrates= " + Player.plugin.GetCurrentBitrates()); + if ((Player.isLive == false) && (Player.isRecording == false)) { + Player.totalTime = Player.plugin.GetDuration(); + } +// Display.updateTotalTime (Player.totalTime); + Player.totalTimeStr = Display.getHumanTimeRepresentation(Player.totalTime); + + +// alert("totalTime= " + Player.totalTime + " totalTimeStr= " +Player.totalTimeStr); +// Display.setTotalTime(totalTime); + + +/* var height = Player.plugin.GetVideoHeight(); + var width = Player.GetVideoWidth(); + Display.showPopup("Resolution= " + height + " x " +width); + alert("Resolution= " + height + " x " +width); +*/ +}; + +Player.OnRenderingComplete = function() { + // Do Something + Player.stopVideo(); +}; + +Player.OnConnectionFailed = function() { + // fails to connect to the streaming server + alert ("ERROR: Failed to connect to the streaming server"); +// widgetAPI.putInnerHTML(document.getElementById("popup"), "Failed to connect to the streaming server"); + Display.showPopup("Failed to connect to the streaming server"); +}; + +Player.OnStreamNotFound = function() { +// 404 file not found + alert ("ERROR: Stream Not Found"); +// widgetAPI.putInnerHTML(document.getElementById("popup"), "Stream Not Found"); + Display.showPopup("Stream Not Found"); + +}; + +Player.OnNetworkDisconnected = function() { +// when the ethernet is disconnected or the streaming server stops supporting the content in the middle of streaming. + alert ("ERROR: Lost Stream (Unavailable?)"); + +// widgetAPI.putInnerHTML(document.getElementById("popup"), "Lost Stream (Unavailable?)"); + Display.showPopup("Lost Stream (Unavailable?)"); +}; + diff --git a/smarttv-client/Javascript/Server.js b/smarttv-client/Javascript/Server.js new file mode 100755 index 0000000..588906b --- /dev/null +++ b/smarttv-client/Javascript/Server.js @@ -0,0 +1,141 @@ +var Server = +{ + dataReceivedCallback : null, + errorCallback : null, + doSort : false, + retries : 0, + + XHRObj : null +}; + +Server.init = function() +{ + var success = true; + + var splashElement = document.getElementById("splashStatus"); + widgetAPI.putInnerHTML(splashElement, "Starting Up"); + + if (this.XHRObj) { + this.XHRObj.destroy(); + this.XHRObj = null; + } + + return success; +}; + +Server.setSort = function (val) { + this.doSort = val; +}; + +Server.fetchVideoList = function(url) { + alert("fetching Videos url= " + url); + if (this.XHRObj == null) { + this.XHRObj = new XMLHttpRequest(); + } + + if (this.XHRObj) { + this.XHRObj.onreadystatechange = function() + { + var splashElement = document.getElementById("splashStatus"); + widgetAPI.putInnerHTML(splashElement, "State" + Server.XHRObj.readyState); + + if (Server.XHRObj.readyState == 4) { + Server.createVideoList(); + } + }; + + this.XHRObj.open("GET", url, true); + this.XHRObj.send(null); + } + else { + var splashElement = document.getElementById("splashStatus"); + widgetAPI.putInnerHTML(splashElement, "Failed !!!" ); + Display.showPopup("Failed to create XHR"); + + if (this.errorCallback != null) { + this.errorCallback("ServerError"); + } + } +}; + +Server.createVideoList = function() { + alert ("creating Video list now"); + var splashElement = document.getElementById("splashStatus"); + widgetAPI.putInnerHTML(splashElement, "Creating Video list now" ); + + if (this.XHRObj.status != 200) { + widgetAPI.putInnerHTML(splashElement, "XML Server Error " + this.XHRObj.status); + Display.status("XML Server Error " + this.XHRObj.status); + Display.showPopup("XML Server Error " + this.XHRObj.status); + if (this.errorCallback != null) { + this.errorCallback("ServerError"); + } + } + else + { + var xmlResponse = this.XHRObj.responseXML; + if (xmlResponse == null) { + Display.status("xmlResponse == null" ); + widgetAPI.putInnerHTML(splashElement, "Error in XML File "); + Display.showPopup("Error in XML File"); + if (this.errorCallback != null) { + this.errorCallback("XmlError"); + } + return; + } + var xmlElement = xmlResponse.documentElement; +// var xmlElement = this.XHRObj.responseXML.documentElement; + + if (!xmlElement) { + widgetAPI.putInnerHTML(splashElement, "Failed to get valid XML!!!"); + Display.status("Failed to get valid XML"); + Display.showPopup("Failed to get valid XML"); + return; + } + else + { + widgetAPI.putInnerHTML(splashElement, "Parsing ..."); + var items = xmlElement.getElementsByTagName("item"); + + 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 startstrVal = ""; + var startVal =0; + var durVal =0; + try { +// startstrVal = items[index].getElementsByTagName("startstr")[0].firstChild.data; + startVal = parseInt(items[index].getElementsByTagName("start")[0].firstChild.data); + durVal = parseInt(items[index].getElementsByTagName("duration")[0].firstChild.data); + } + catch (e) { + + alert("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, +// startstr: startstrVal, + start: startVal, + dur: durVal}); + } + + } + Data.completed(this.doSort); + widgetAPI.putInnerHTML(splashElement, "Done..."); + + if (this.dataReceivedCallback) + { + this.dataReceivedCallback(); /* Notify all data is received and stored */ + } + } + } +}; diff --git a/smarttv-client/config.xml b/smarttv-client/config.xml new file mode 100755 index 0000000..e586334 --- /dev/null +++ b/smarttv-client/config.xml @@ -0,0 +1,33 @@ + + + + user + + + + Images/icon/SmartTvWeb_106.png + Images/icon/SmartTvWeb_115.png + Images/icon/SmartTvWeb_85.png + Images/icon/SmartTvWeb_95.png + 0.8 + + y + y + y + n + n + n + n + y + VDR on TV + + 960 + 540 + + T. Lohmar + + + Private + + Config + diff --git a/smarttv-client/index.html b/smarttv-client/index.html new file mode 100755 index 0000000..757468c --- /dev/null +++ b/smarttv-client/index.html @@ -0,0 +1,125 @@ + + + + + Vdr on SmartTV + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+
1: Live
+
2: Recordings
+
3: Media
+
4: Options
+
5: DLNA
+
+
+
+
+ Server Configurations

+ Address (host:port) http:// +
+
+ +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + -- cgit v1.2.3 From e13c617a3b97b3707038c31cd18ef49686c36d3d Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 26 Dec 2012 21:58:29 +0100 Subject: Fixes and improvements of initial widget configuration. --- smarttv-client/Javascript/Config.js | 35 ++++++++++++++++++++++------------- smarttv-client/Javascript/Main.js | 10 ++++++---- smarttv-client/Javascript/Options.js | 8 +++++--- smarttv-client/Javascript/Server.js | 7 +++++++ 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index b0742a4..ad0196f 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -3,7 +3,8 @@ var Config = { XHRObj : null, xmlDocument : null, serverUrl : "", // Will become the main URL for contacting the server - serverAddr : "192.168.1.122:8000", + serverAddr : "", + serverAddrDefault: "192.168.1.122:8000", format :"has", tgtBufferBitrate : 6000, // kbps totalBufferDuration : 30, // sec @@ -26,25 +27,27 @@ var Config = { Config.init = function () { var fileSystemObj = new FileSystem(); - + if (fileSystemObj.isValidCommonPath(curWidget.id) == 0){ - Display.showPopup ("First Launch of the Widget --> Launching Config Menu"); +// Display.showPopup ("First Launch of the Widget --> Launching Config Menu"); alert("First Launch of the Widget"); // should switch to the config screen here var res = fileSystemObj.createCommonDir(curWidget.id); if (res == true) { Config.firstLaunch = true; - -// Config.writeContext("192.168.1.122:8000"); + + Main.init(); + Main.changeState(4); return; } else { + Main.init(); Display.showPopup ("ERROR: Cannot create widget folder"); + Main.log("ERROR: Cannot create widget folder curWidget.id= " +curWidget.id); } return; } - else { Config.readContext(); } @@ -94,8 +97,6 @@ Config.updateContext = function (addr) { Config.readContext = function () { var fileSystemObj = new FileSystem(); -// Display.showPopup ("Reading Config file for curWidget.id= " + curWidget.id); - try { var fd = fileSystemObj.openCommonFile(Config.cfgFileName, "r"); @@ -109,23 +110,28 @@ Config.readContext = function () { Config.serverUrl = "http://" + Config.serverAddr; } else { - Display.showPopup ("WARNING: Error in Config File"); + Display.showPopup ("WARNING: Error in Config File. Try widget restart."); + // TODO: I should re-write the config file } } - fileSystemObj.closeCommonFile(fd); + fileSystemObj.closeCommonFile(fd); + } catch (e) { + Main.log("Config.readContext: Error while reading: e= " +e); var res = fileSystemObj.createCommonDir(curWidget.id); if (res == true) { - Display.showPopup ("*** Read Error and Widget Folder successfully created ***"); - alert("-------------- Error: res = true ------------------------"); + Main.log("Config.readContext: Widget Folder created"); + Display.showPopup ("Config Read Error: Try widget restart"); } else { - Display.showPopup ("*** Read Error and Widget Folder creation failed ***"); + Main.log("Config.readContext: Widget Folder creation failed"); + Display.showPopup ("Config Read Error: Try re-installing the widget"); alert("-------------- Error: res = false ------------------------"); } Config.firstLaunch = true; + Main.init(); Main.changeState(4); } }; @@ -171,16 +177,19 @@ Config.processConfig = function () { if (xmlResponse == null) { alert ("xml error"); Display.showPopup("Error in XML Config File"); + Main.log("Error in XML Config File"); return; } this.xmlDocument = xmlResponse.documentElement; if (!this.xmlDocument ) { alert("Failed to get valid Config XML"); Display.showPopup("Failed to get valid Config XML"); + Main.log("Failed to get valid Config XML"); return; } else { alert ("Paring config XML now"); + Main.log("Paring config XML now"); this.format = Config.getXmlString("format"); var res = Config.getXmlValue("tgtBufferBitrate"); if (res != 0) diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index ef92172..fa3c863 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -38,8 +38,6 @@ var Main = { }; Main.onLoad = function() { - -// Config.init("http://192.168.1.122:8000/widget.conf"); Network.init(); Display.init(); @@ -55,6 +53,7 @@ Main.onLoad = function() { }; // Called by Config, when done +// TODO: Send sendReadyEvent early and show a splash screen during startup Main.init = function () { alert("Main.init()"); if ( Player.init() && Audio.init() && Server.init() ) { @@ -77,7 +76,7 @@ Main.init = function () { document.getElementById("splashScreen").style.display="none"; - widgetAPI.sendReadyEvent(); + widgetAPI.sendReadyEvent(); } else { alert("Failed to initialise"); @@ -86,7 +85,10 @@ Main.init = function () { }; Main.log = function (msg) { - var XHRObj = new XMLHttpRequest(); + if (Config.serverUrl == "" ) + return; + + var XHRObj = new XMLHttpRequest(); XHRObj.open("POST", Config.serverUrl + "/log", true); XHRObj.send("CLOG: " + msg); }; diff --git a/smarttv-client/Javascript/Options.js b/smarttv-client/Javascript/Options.js index d60a9ff..11095c6 100755 --- a/smarttv-client/Javascript/Options.js +++ b/smarttv-client/Javascript/Options.js @@ -7,15 +7,15 @@ Options.init = function() { _g_ime.keySet = '12key'; this.imeBox = new IMEShell("widgetServerAddr", Options.onImeCreated, 'de'); - alert ("Options Initiated"); }; Options.onComplete = function () { - alert("Colpleted"); + alert("Completed"); }; Options.onEnter = function () { alert("Enter: " + document.getElementById("widgetServerAddr").value ); + Config.updateContext(document.getElementById("widgetServerAddr").value); document.getElementById('widgetServerAddr').blur(); @@ -38,6 +38,7 @@ Options.onBlue = function () { Options.onImeCreated = function(obj) { // _g_ime.keySet ("12key"); // obj.setKeySetFunc('12key'); + Main.log ("Options.onImeCreated()"); obj.setKeyFunc(tvKey.KEY_RETURN, function(keyCode) { widgetAPI.sendReturnEvent(); return false; } ); obj.setKeyFunc(tvKey.KEY_EXIT, function(keyCode) { widgetAPI.sendExitEvent(); return false; } ); @@ -46,7 +47,7 @@ Options.onImeCreated = function(obj) { obj.setWordBoxPos(18, 6); obj.setKeyFunc(tvKey.KEY_BLUE, Options.onBlue); - obj.setString(Config.serverAddr); + obj.setString(Config.serverAddrDefault); obj.setEnterFunc(Options.onEnter); if (obj.setMode("_num") == false) { @@ -60,6 +61,7 @@ Options.onImeCreated = function(obj) { Options.onReady = function () { document.getElementById('widgetServerAddr').focus(); + Main.log ("Options.onReady()"); alert ("KeySet= " + this.imeBox.getKeySet()); }; diff --git a/smarttv-client/Javascript/Server.js b/smarttv-client/Javascript/Server.js index 588906b..c64d320 100755 --- a/smarttv-client/Javascript/Server.js +++ b/smarttv-client/Javascript/Server.js @@ -60,6 +60,8 @@ Server.fetchVideoList = function(url) { Server.createVideoList = function() { alert ("creating Video list now"); + Main.log("creating Video list now"); + var splashElement = document.getElementById("splashStatus"); widgetAPI.putInnerHTML(splashElement, "Creating Video list now" ); @@ -96,6 +98,11 @@ Server.createVideoList = function() { { widgetAPI.putInnerHTML(splashElement, "Parsing ..."); var items = xmlElement.getElementsByTagName("item"); + if (items.length == 0) { + Display.showPopup("Something wrong. Response does not contain any item"); + Main.log("Something wrong. Response does not contain any item"); + + }; for (var index = 0; index < items.length; index++) { -- cgit v1.2.3 From 8f0bf44fd3f08c837a8409378afd09dfb5b12f0f Mon Sep 17 00:00:00 2001 From: thlo Date: Thu, 27 Dec 2012 11:17:44 +0100 Subject: Limit HTTP server to Media Folder. Reduction of log output --- vdr-smarttvweb/httpresource.c | 83 ++++++++++++++++++++++++++++------------- vdr-smarttvweb/smarttvfactory.c | 9 +++-- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 8ca689a..bd83d80 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -310,7 +310,7 @@ int cHttpResource::processRequest() { if (strcasecmp(mMethod.c_str(), "GET") != 0){ sendError(501, "Not supported", NULL, "Method is not supported."); - return ERROR; + return OKAY; } #ifndef STANDALONE @@ -323,8 +323,8 @@ int cHttpResource::processRequest() { ret = sendRecordingsHtml( &statbuf); return OKAY; } - if (mPath.compare("/recordings.xml") == 0) { + if (mPath.compare("/recordings.xml") == 0) { ret = sendRecordingsXml( &statbuf); return OKAY; } @@ -339,67 +339,89 @@ int cHttpResource::processRequest() { return OKAY; } #endif + if (mPath.compare("/media.xml") == 0) { ret = sendMediaXml( &statbuf); return OKAY; } - if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { - ret = sendManifest( &statbuf, false); - return OKAY; + if (mPath.compare("/widget.conf") == 0) { + mPath = mFactory->getConfigDir() + "/widget.conf"; } - if (mPath.compare(mPath.size() -9, 9, "-seg.m3u8") == 0) { - ret = sendManifest( &statbuf, true); - return OKAY; + if (mPath.size() > 8) { + if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { + ret = sendManifest( &statbuf, false); + return OKAY; + } } - if (mPath.compare(mPath.size() -7, 7, "-seg.ts") == 0) { - ret = sendMediaSegment( &statbuf); - return OKAY; + if (mPath.size() > 9) { + if (mPath.compare(mPath.size() -9, 9, "-seg.m3u8") == 0) { + ret = sendManifest( &statbuf, true); + return OKAY; + } } - if (mPath.compare("/widget.conf") == 0) { - mPath = mFactory->getConfigDir() + "/widget.conf"; + if (mPath.size() > 7) { + if (mPath.compare(mPath.size() -7, 7, "-seg.ts") == 0) { + ret = sendMediaSegment( &statbuf); + return OKAY; + } } if (stat(mPath.c_str(), &statbuf) < 0) { + // checking, whether the file or directory exists sendError(404, "Not Found", NULL, "File not found."); - return ERROR; + return OKAY; } if (S_ISDIR(statbuf.st_mode)) { - + // Do Folder specific checkings #ifndef DEBUG *(mLog->log())<< DEBUGPREFIX << " processRequest: isDir - mPath: " << mPath.c_str() << endl; #endif - if (mPath.size() >4) { if (mPath.compare(mPath.size() - 4, 4, ".rec") == 0) { + // Handle any recording directory specifically mContentType = VDRDIR; return sendVdrDir( &statbuf); } } - // else { + + if (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) != 0) { + // No directory access outside of MediaFolder + *(mLog->log())<< DEBUGPREFIX + << " Directory request is not for MediaFolde (" + << mFactory->getConfig()->getMediaFolder() << ")" + << endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + sendDir( &statbuf); mContentType = MEMBLOCK; - return OKAY; - // } + return OKAY; } else { + // mPath is not a folder, thus it is a file #ifndef DEBUG *(mLog->log())<< DEBUGPREFIX << " processRequest: file send\n"; #endif + // Check, if requested file is in Media Directory + if (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) != 0) { + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } mFileSize = statbuf.st_size; - mContentType = SINGLEFILE; - return sendFile(&statbuf); } + #ifndef DEBUG *(mLog->log())<< DEBUGPREFIX << " processRequest: Not Handled SHOULD not be here\n"; @@ -523,8 +545,10 @@ int cHttpResource::fillDataBlk() { case MEMBLOCK: int rem_len = mResponseMessage->size() - mResponseMessagePos; if (rem_len == 0) { - *(mLog->log())<< DEBUGPREFIX - << " fillDataBlock: MEMBLOCK done" << endl; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " fillDataBlock: MEMBLOCK done" << endl; +#endif delete mResponseMessage; mResponseMessagePos = 0; mConnState = TOCLOSE; @@ -857,12 +881,14 @@ int cHttpResource::parseFiles(vector *entries, string prefix, string string dir_comp; dir_comp = dir_base + dir_name + "/"; +#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX << " parseFiles: Prefix= " << prefix << " base= " << dir_base << " dir= " << dir_name << " comp= " << dir_comp << endl; +#endif dir = opendir(dir_comp.c_str()); while ((de = readdir(dir)) != NULL) { @@ -870,7 +896,6 @@ int cHttpResource::parseFiles(vector *entries, string prefix, string continue; } - // *(mLog->log()) << DEBUGPREFIX << " " << de->d_name << endl; strcpy(pathbuf, dir_comp.c_str()); strcat(pathbuf, de->d_name); @@ -890,8 +915,10 @@ int cHttpResource::parseFiles(vector *entries, string prefix, string t.tm_sec = 0; start = mktime(&t); +#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX << " Vdr Folder Found: " << pathbuf << " start= " << start << endl; +#endif entries->push_back(sFileEntry(dir_name, pathbuf, start)); } @@ -1237,8 +1264,10 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) { mConnState = SERVING; - *(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl; - +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl; +#endif + vector entries; parseFiles(&entries, "", media_folder, "", statbuf); @@ -1586,10 +1615,12 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { } } +#ifndef DEBUG *(mLog->log())<< DEBUGPREFIX << " Found " << act_rec.size() << " running timers" << endl; +#endif int rec_dur = 0; for (cRecording *recording = recordings->First(); recording; recording = recordings->Next(recording)) { diff --git a/vdr-smarttvweb/smarttvfactory.c b/vdr-smarttvweb/smarttvfactory.c index 12a24bc..a57310a 100644 --- a/vdr-smarttvweb/smarttvfactory.c +++ b/vdr-smarttvweb/smarttvfactory.c @@ -144,7 +144,8 @@ void SmartTvServer::loop() { write_set = mWriteState; if (ret != handeled_fds) { - *(mLog.log()) << "ERROR: Select-ret= " << ret + // Only ok, when the server has closed a handing HTTP connection + *(mLog.log()) << "WARNING: Select-ret= " << ret << " != handeled_fds= " << handeled_fds << endl; /* FD_ZERO(&mReadState); FD_ZERO(&mWriteState); @@ -168,10 +169,10 @@ void SmartTvServer::loop() { timeout.tv_sec = 5; timeout.tv_usec = 0; - ret = select(maxfd + 1, &read_set, &write_set, NULL, &timeout); if (ret == 0) { + // timeout: Check for dead TCP connections for (uint idx= 0; idx < clientList.size(); idx++) { if (clientList[idx] != NULL) if (clientList[idx]->checkStatus() == ERROR) { @@ -194,9 +195,9 @@ void SmartTvServer::loop() { // new accept if (FD_ISSET(mServerFd, &read_set)) { + handeled_fds ++; if((rfd = accept(mServerFd, (sockaddr*)&sadr, &addr_size))!= -1){ req_id ++; - handeled_fds ++; #ifndef DEBUG *(mLog.log()) << "fd= " << rfd @@ -268,7 +269,9 @@ void SmartTvServer::loop() { continue; } if ( clientList[rfd]->handleWrite() < 0){ +#ifndef DEBUG *(mLog.log()) << "fd= " << rfd << " --------------------- Check Write: Closing ---------------------" << endl; +#endif close(rfd); delete clientList[rfd]; clientList[rfd] = NULL; -- cgit v1.2.3 From 3249a09d4239dbc7b5cbce53f611014d8c89b0d4 Mon Sep 17 00:00:00 2001 From: thlo Date: Thu, 27 Dec 2012 17:13:59 +0100 Subject: Allow fetching of widget.conf --- vdr-smarttvweb/httpresource.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index bd83d80..7755ff0 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -347,6 +347,14 @@ int cHttpResource::processRequest() { if (mPath.compare("/widget.conf") == 0) { mPath = mFactory->getConfigDir() + "/widget.conf"; + + if (stat(mPath.c_str(), &statbuf) < 0) { + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + mFileSize = statbuf.st_size; + mContentType = SINGLEFILE; + return sendFile(&statbuf); } if (mPath.size() > 8) { -- cgit v1.2.3 From 19e18001a906b75fd7383291307e72eca14d70b2 Mon Sep 17 00:00:00 2001 From: thlo Date: Thu, 27 Dec 2012 22:24:51 +0100 Subject: Increase robustness (main part of code runs in chrome now). Bug fixes. --- smarttv-client/Javascript/Audio.js | 25 ++-- smarttv-client/Javascript/Config.js | 123 +++++++++------- smarttv-client/Javascript/Data.js | 14 +- smarttv-client/Javascript/Display.js | 224 +++++++++++++---------------- smarttv-client/Javascript/Main.js | 270 +++++++++++++++++++++++------------ smarttv-client/Javascript/Network.js | 21 ++- smarttv-client/Javascript/Options.js | 12 +- smarttv-client/Javascript/Player.js | 85 +++++------ smarttv-client/Javascript/Server.js | 28 ++-- 9 files changed, 449 insertions(+), 353 deletions(-) diff --git a/smarttv-client/Javascript/Audio.js b/smarttv-client/Javascript/Audio.js index 6046c37..f5c548e 100755 --- a/smarttv-client/Javascript/Audio.js +++ b/smarttv-client/Javascript/Audio.js @@ -9,23 +9,30 @@ Audio.init = function() this.plugin = document.getElementById("pluginAudio"); - if (!this.plugin) - { + if (!this.plugin) { success = false; } return success; }; -Audio.setRelativeVolume = function(delta) -{ - this.plugin.SetVolumeWithKey(delta); +Audio.setRelativeVolume = function(delta) { + if (Config.deviceType == 0) + this.plugin.SetVolumeWithKey(delta); + else + Main.log("Un-supported Audio Device"); Display.setVolume( this.getVolume() ); }; -Audio.getVolume = function() -{ - alert("Volume : " + this.plugin.GetVolume()); - return this.plugin.GetVolume(); +Audio.getVolume = function() { + var res = 0; + if (Config.deviceType == 0) { + res = this.plugin.GetVolume(); + } + else { + Main.log("Un-supported Audio Device"); + } + + return res; }; diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index ad0196f..9e111d9 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -1,8 +1,8 @@ var Config = { - cfgFileName : curWidget.id + "/config.dta", + cfgFileName : "", XHRObj : null, xmlDocument : null, - serverUrl : "", // Will become the main URL for contacting the server + serverUrl : "http://192.168.1.122:8000", // Will become the main URL for contacting the server. Form "http://:port" serverAddr : "", serverAddrDefault: "192.168.1.122:8000", format :"has", @@ -13,7 +13,11 @@ var Config = { initialTimeOut: 3, // sec skipDuration : 30, // sec noLiveChannels : 30, - firstLaunch : false + firstLaunch : false, + + deviceType : 0 // Used to differentiate between browsers and platforms + // 0: Samsung + }; @@ -24,32 +28,53 @@ var Config = { * Come back here, read the file and continue */ - Config.init = function () { - var fileSystemObj = new FileSystem(); - - if (fileSystemObj.isValidCommonPath(curWidget.id) == 0){ -// Display.showPopup ("First Launch of the Widget --> Launching Config Menu"); - alert("First Launch of the Widget"); - // should switch to the config screen here - var res = fileSystemObj.createCommonDir(curWidget.id); - if (res == true) { - Config.firstLaunch = true; - Main.init(); - Main.changeState(4); +/* Main.logToServer ("navigator.appCodeName= " + navigator.appCodeName); + Main.logToServer ("navigator.appName= " + navigator.appName); + Main.logToServer ("navigator.platform= " + navigator.platform); + + Main.logToServer ("modelName= " + deviceapis.tv.info.getModel()); + Main.logToServer ("productType= " + deviceapis.tv.info.getProduct()); +*/ + + if (this.deviceType == 0) { + // This is a Samsung Smart TV + + try { + this.cfgFileName = curWidget.id + "/config.dta"; + } + catch (e) { + Main.log("curWidget.id does not exists. Is that a Samsung?"); + return; + }; + + var fileSystemObj = new FileSystem(); + + if (fileSystemObj.isValidCommonPath(curWidget.id) == 0){ +// Display.showPopup ("First Launch of the Widget --> Launching Config Menu"); + Main.log("First Launch of the Widget"); + // should switch to the config screen here + var res = fileSystemObj.createCommonDir(curWidget.id); + if (res == true) { + Config.firstLaunch = true; + + Main.init(); // Obsolete? + + Main.changeState(4); + return; + } + else { + Main.init(); + Display.showPopup ("ERROR: Cannot create widget folder"); + Main.logToServer("ERROR: Cannot create widget folder curWidget.id= " +curWidget.id); + } return; } else { - Main.init(); - Display.showPopup ("ERROR: Cannot create widget folder"); - Main.log("ERROR: Cannot create widget folder curWidget.id= " +curWidget.id); - } - return; - } - else { - Config.readContext(); + Config.readContext(); + } } Config.fetchConfig(); @@ -118,16 +143,16 @@ Config.readContext = function () { } catch (e) { - Main.log("Config.readContext: Error while reading: e= " +e); + Main.logToServer("Config.readContext: Error while reading: e= " +e); var res = fileSystemObj.createCommonDir(curWidget.id); if (res == true) { - Main.log("Config.readContext: Widget Folder created"); + Main.logToServer("Config.readContext: Widget Folder created"); Display.showPopup ("Config Read Error: Try widget restart"); } else { - Main.log("Config.readContext: Widget Folder creation failed"); + Main.logToServer("Config.readContext: Widget Folder creation failed"); Display.showPopup ("Config Read Error: Try re-installing the widget"); - alert("-------------- Error: res = false ------------------------"); + Main.log("-------------- Error: res = false ------------------------"); } Config.firstLaunch = true; @@ -144,8 +169,8 @@ Config.getXmlValue = function (itm) { res = val[0].firstChild.data; } catch (e) { - Main.log("parsing widget.conf: Item= " + itm + " not found" + e); - alert ("parsing widget.conf: Item= " + itm + " not found e= " + e); + Main.logToServer("parsing widget.conf: Item= " + itm + " not found" + e); + Main.log ("parsing widget.conf: Item= " + itm + " not found e= " + e); } return res; @@ -159,8 +184,8 @@ Config.getXmlString = function (itm) { res = val[0].firstChild.data; } catch (e) { - Main.log("parsing widget.conf: Item= " + itm + " not found" + e); - alert ("parsing widget.conf: Item= " + itm + " not found e= " + e); + Main.logToServer("parsing widget.conf: Item= " + itm + " not found" + e); + Main.log ("parsing widget.conf: Item= " + itm + " not found e= " + e); }; return res; @@ -168,28 +193,28 @@ Config.getXmlString = function (itm) { Config.processConfig = function () { if (this.XHRObj.status != 200) { - alert ("Config Server Error"); + Main.log ("Config Server Error"); Display.showPopup("Config Server Error " + this.XHRObj.status); - Main.log("Config Server Error " + this.XHRObj.status); + Main.logToServer("Config Server Error " + this.XHRObj.status); } else { var xmlResponse = this.XHRObj.responseXML; if (xmlResponse == null) { - alert ("xml error"); + Main.log ("xml error"); Display.showPopup("Error in XML Config File"); - Main.log("Error in XML Config File"); + Main.logToServer("Error in XML Config File"); return; } this.xmlDocument = xmlResponse.documentElement; if (!this.xmlDocument ) { - alert("Failed to get valid Config XML"); + Main.log("Failed to get valid Config XML"); Display.showPopup("Failed to get valid Config XML"); - Main.log("Failed to get valid Config XML"); + Main.logToServer("Failed to get valid Config XML"); return; } else { - alert ("Paring config XML now"); - Main.log("Paring config XML now"); + Main.log ("Parsing config XML now"); + Main.logToServer("Parsing config XML now"); this.format = Config.getXmlString("format"); var res = Config.getXmlValue("tgtBufferBitrate"); if (res != 0) @@ -212,16 +237,16 @@ Config.processConfig = function () { res = Config.getXmlValue("liveChannels"); if (res != 0) noLiveChannels = res; - alert("**** Config ****"); - alert("serverUrl= " + Config.serverUrl); - alert("format= " + Config.format); - alert("tgtBufferBitrate= " + Config.tgtBufferBitrate); - alert("totalBufferDuration= " + Config.totalBufferDuration); - alert("initialBuffer= " + Config.initialBuffer); - alert("pendingBuffer= " + Config.pendingBuffer); - alert("skipDuration= " + Config.skipDuration); - alert("initialTimeOut= " + Config.initialTimeOut); - alert("**** /Config ****"); + Main.log("**** Config ****"); + Main.log("serverUrl= " + Config.serverUrl); + Main.log("format= " + Config.format); + Main.log("tgtBufferBitrate= " + Config.tgtBufferBitrate); + Main.log("totalBufferDuration= " + Config.totalBufferDuration); + Main.log("initialBuffer= " + Config.initialBuffer); + Main.log("pendingBuffer= " + Config.pendingBuffer); + Main.log("skipDuration= " + Config.skipDuration); + Main.log("initialTimeOut= " + Config.initialTimeOut); + Main.log("**** /Config ****"); }; }; diff --git a/smarttv-client/Javascript/Data.js b/smarttv-client/Javascript/Data.js index a2de13f..e7049ee 100755 --- a/smarttv-client/Javascript/Data.js +++ b/smarttv-client/Javascript/Data.js @@ -18,7 +18,7 @@ Data.completed= function(sort) { this.assets.sortPayload(); this.folderList.push({item : this.assets, id: 0}); - alert ("Data.completed()= " +this.folderList.length); + Main.log ("Data.completed()= " +this.folderList.length); }; @@ -43,9 +43,9 @@ Data.addItem = function(t_list, pyld) { }; Data.dumpFolderStruct = function(){ - alert("---------- dumpFolderStruct ------------"); + Main.log("---------- dumpFolderStruct ------------"); this.assets.print(0); - alert("---------- dumpFolderStruct Done -------"); + Main.log("---------- dumpFolderStruct Done -------"); }; Data.getCurrentItem = function () { @@ -76,7 +76,7 @@ Item.prototype.getTitle = function () { Item.prototype.getPayload = function () { if (this.isFolder == true) { - alert("WARNING: getting payload on a folder title=" +this.title); + Main.log("WARNING: getting payload on a folder title=" +this.title); } return this.payload; }; @@ -102,7 +102,7 @@ Item.prototype.addChild = function (key, pyld, level) { } else { if (level > 10) { - alert(" too many levels"); + Main.log(" too many levels"); return; } var t = key.shift(); @@ -129,9 +129,9 @@ Item.prototype.print = function(level) { prefix += " "; for (var i = 0; i < this.childs.length; i++) { - alert(prefix + this.childs[i].title); + Main.log(prefix + this.childs[i].title); if (this.childs[i].isFolder == true) { - alert(prefix+"Childs:"); + Main.log(prefix+"Childs:"); this.childs[i].print(level +1); } } diff --git a/smarttv-client/Javascript/Display.js b/smarttv-client/Javascript/Display.js index fa90887..5c68b53 100755 --- a/smarttv-client/Javascript/Display.js +++ b/smarttv-client/Javascript/Display.js @@ -25,14 +25,16 @@ var Display = Display.init = function() { var success = true; - + + // Samsung specific object + this.pluginTime = document.getElementById("pluginTime"); + this.statusDiv = document.getElementById("status"); this.statusPopup = document.getElementById("statusPopup"); - this.pluginTime = document.getElementById("pluginTime"); this.bufferingElm = document.getElementById("bufferingBar"); - alert("Display.init now=" + this.pluginTime.GetEpochTime()); +// Main.log("Display.init now=" + this.pluginTime.GetEpochTime()); this.progOlHandler = new OverlayHandler("ProgHndl"); this.volOlHandler = new OverlayHandler("VolHndl"); @@ -41,7 +43,6 @@ Display.init = function() this.volOlHandler.init(Display.handlerShowVolume, Display.handlerHideVolume); this.popupOlHandler.init(Display.handlerShowPopup, Display.handlerHidePopup); - Display.status("Stop"); if (!this.statusDiv) { success = false; @@ -60,7 +61,7 @@ Display.init = function() var elm = document.getElementById("selectItem"+i); if (elm == null) { done = true; - alert( " only found to selectItem"+ (i-1)); + Main.log( " only found to selectItem"+ (i-1)); break; } elm.style.paddingBottom = "3px"; @@ -69,10 +70,40 @@ Display.init = function() elm.style.textAlign = "center"; } - alert("Display initialized" ); + Main.log("Display initialized" ); return success; }; +Display.putInnerHTML = function (elm, val) { + + switch (Config.deviceType) { + case 0: + // Samsung specific handling of innerHtml + widgetAPI.putInnerHTML(elm, val); + break; + default: + elm.innerHTML = val; + break; + } + +}; + +Display.GetEpochTime = function() { + var res = 0; + switch (Config.deviceType) { + case 0: + // Samsung specific UTC time function + res = Display.pluginTime.GetEpochTime(); + break; + default: + var now_millis = ((new Date).getTime()); + res = (now_millis /1000.0); + break; + } + + return res; +}; + Display.resetSelectItems = function (itm) { var done = false; var i = 0; @@ -81,7 +112,7 @@ Display.resetSelectItems = function (itm) { var elm = document.getElementById("selectItem"+i); if (elm == null) { done = true; - alert( " only found to selectItem"+ (i-1)); + Main.log( " only found to selectItem"+ (i-1)); break; } Display.unselectItem(elm); @@ -100,7 +131,7 @@ Display.resetVideoList = function () { break; } Display.unselectItem(elm); - widgetAPI.putInnerHTML(elm, ""); + Display.putInnerHTML(elm, ""); } }; @@ -108,7 +139,7 @@ Display.resetVideoList = function () { Display.setOlTitle = function (title) { this.olTitle = title; var elm = document.getElementById("olTitle"); - widgetAPI.putInnerHTML(elm, Display.olTitle); + Display.putInnerHTML(elm, Display.olTitle); }; Display.setStartStop = function(start, stop) { @@ -129,73 +160,10 @@ Display.setStartStop = function(start, stop) { this.olStartStop = this.olStartStop + hours + ":" + minutes; var elm = document.getElementById("olStartStop"); - widgetAPI.putInnerHTML(elm, Display.olStartStop); + Display.putInnerHTML(elm, Display.olStartStop); }; /* -//obsolete? -Display.setTotalTime = function(total) { - this.totalTime = total; -}; -*/ -/* - -// Player.OnCurrentPlayTime -Display.setTime = function(time) { - var timePercent = (100 * time) / this.totalTime; - var timeHTML = ""; - var timeHour = 0; var timeMinute = 0; var timeSecond = 0; - var totalTimeHour = 0; var totalTimeMinute = 0; - var totalTimesecond = 0; - - if(Player.state == Player.PLAYING) - { - totalTimeHour = Math.floor(this.totalTime/3600000); - timeHour = Math.floor(time/3600000); - - totalTimeMinute = Math.floor((this.totalTime%3600000)/60000); - timeMinute = Math.floor((time%3600000)/60000); - - totalTimeSecond = Math.floor((this.totalTime%60000)/1000); - timeSecond = Math.floor((time%60000)/1000); - - timeHTML = timeHour + ":"; - - if(timeMinute == 0) - timeHTML += "00:"; - else if(timeMinute <10) - timeHTML += "0" + timeMinute + ":"; - else - timeHTML += timeMinute + ":"; - - if(timeSecond == 0) - timeHTML += "00/"; - else if(timeSecond <10) - timeHTML += "0" + timeSecond + "/"; - else - timeHTML += timeSecond + "/"; - - timeHTML += totalTimeHour + ":"; - - if(totalTimeMinute == 0) - timeHTML += "00:"; - else if(totalTimeMinute <10) - timeHTML += "0" + totalTimeMinute; - else - timeHTML += totalTimeMinute; - - if(totalTimeSecond == 0) - timeHTML += "00"; - else if(totalTimeSecond <10) - timeHTML += "0" + totalTimeSecond; - else - timeHTML += totalTimeSecond; - } - else - timeHTML = "0:00:00/0:00:00"; -}; -*/ - Display.getHumanTimeRepresentation = function(time) { var totalTimeHour = 0; var totalTimeMinute = 0; @@ -223,7 +191,7 @@ Display.getHumanTimeRepresentation = function(time) { return totalTimeStr; }; - +*/ /* // called by Player.OnStreamInfoReady Display.updateTotalTime = function(time) { @@ -235,7 +203,7 @@ Display.updateTotalTime = function(time) { Display.updatePlayTime = function() { // Player.curPlayTimeStr = Display.getHumanTimeRepresentation(Player.curPlayTime); var timeElement = document.getElementById("olTimeInfo"); - widgetAPI.putInnerHTML(timeElement, Player.curPlayTimeStr + " / " + Player.totalTimeStr); + Display.putInnerHTML(timeElement, Player.curPlayTimeStr + " / " + Player.totalTimeStr); }; Display.updateProgressBar = function () { @@ -244,10 +212,10 @@ Display.updateProgressBar = function () { }; Display.updateRecBar = function (start_time, duration){ - var now = Display.pluginTime.GetEpochTime(); + var now = Display.GetEpochTime(); var remaining = Math.round(((start_time + duration) - now) * 100/ duration); -// alert (" remaining= " + remaining + " start= " + start_time + " dur= " + duration); +// Main.log (" remaining= " + remaining + " start= " + start_time + " dur= " + duration); var elm = document.getElementById("olRecProgressBar"); elm.style.display="block"; elm.style.width = remaining + "%"; @@ -256,9 +224,9 @@ Display.updateRecBar = function (start_time, duration){ Display.status = function(status) { - alert(status); - widgetAPI.putInnerHTML(this.statusDiv, status); - widgetAPI.putInnerHTML(this.statusPopup, status); + Main.log(status); + Display.putInnerHTML(this.statusDiv, status); + Display.putInnerHTML(this.statusPopup, status); }; Display.showStatus = function() { @@ -271,7 +239,7 @@ Display.hideStatus = function() { Display.progress = function(status) { - widgetAPI.putInnerHTML(this.statusDiv, status); + Display.putInnerHTML(this.statusDiv, status); }; @@ -308,6 +276,7 @@ Display.durationString = function(time) { Display.handleDescription =function (selected) { + Main.log("Display.handleDescription "); if (Data.getCurrentItem().childs[selected].isFolder == true) { Display.setDescription( "Dir: " +Data.getCurrentItem().childs[selected].title ); @@ -326,11 +295,11 @@ Display.handleDescription =function (selected) { var min = Display.getNumString (digi.getMinutes(), 2); var d_str =""; -// alert("handleDescription: " +Data.getCurrentItem().childs[selected].payload.desc); +// Main.log("handleDescription: " +Data.getCurrentItem().childs[selected].payload.desc); var msg = ""; if (Main.state == 1) { // Live - var now = pluginTime.GetEpochTime(); + var now = Display.GetEpochTime(); d_str = hour + ":" + min; @@ -338,7 +307,10 @@ Display.handleDescription =function (selected) { msg += ""+ prog + "
"; msg += "
Start: " + d_str + "
"; msg += "Duration: " + Display.durationString(length) + "h
"; + Main.log("itm.payload.start= " + itm.payload.start + " length= " + length + " now= " +now); msg += "Remaining: " + Display.durationString((itm.payload.start + length - now)); + + } else { // on-demand @@ -399,7 +371,7 @@ Display.getDisplayTitle = function(item) { } break; default: - alert("ERROR: Shall be in state 1 or 2. State= " + Main.state); + Main.log("ERROR: Shall be in state 1 or 2. State= " + Main.state); break; } return res; @@ -409,25 +381,22 @@ Display.setVideoList = function(selected, first) { // var listHTML = ""; var first_item = selected; - if(typeof first !='undefined') { - first_item = first; - alert("Display.setVideoList first_item= " + first_item); - } - + var i=0; - alert("Display.setVideoList title= " +Data.getCurrentItem().childs[selected].title + " selected= " + selected + " first_item= " + first_item); + Main.log("Display.setVideoList title= " +Data.getCurrentItem().childs[selected].title + " selected= " + selected + " first_item= " + first_item); this.handleDescription(selected); - + for (i = 0; i <= this.LASTIDX; i++) { if ((first_item+i) >= Data.getVideoCount()) { listHTML = ""; } else { -// alert(" - title[first_item+i]= " +Data.getCurrentItem().childs[(first_item +i)].title + " i= " + i); listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[first_item+i]); +// Main.log(" - title[first_item+i]= " +Data.getCurrentItem().childs[(first_item +i)].title + " i= " + i + " listHTML= " + listHTML); +// Main.log(" - listHTML= " + listHTML); } this.videoList[i] = document.getElementById("video"+i); - widgetAPI.putInnerHTML(this.videoList[i], listHTML); + Display.putInnerHTML(this.videoList[i], listHTML); this.unselectItem(this.videoList[i]); } @@ -436,7 +405,7 @@ Display.setVideoList = function(selected, first) { listHTML = (selected +1) + " / " + Data.getVideoCount(); - widgetAPI.putInnerHTML(document.getElementById("videoCount"), listHTML); + Display.putInnerHTML(document.getElementById("videoCount"), listHTML); }; Display.selectItem = function (item) { @@ -463,12 +432,12 @@ Display.unselectItem = function (item) { Display.setVideoListPosition = function(position, move) { var listHTML = ""; - alert ("Display.setVideoListPosition title= " +Data.getCurrentItem().childs[position].title + " move= " +move); + Main.log ("Display.setVideoListPosition title= " +Data.getCurrentItem().childs[position].title + " move= " +move); this.handleDescription(position); listHTML = (position + 1) + " / " + Data.getVideoCount(); - widgetAPI.putInnerHTML(document.getElementById("videoCount"), listHTML); + Display.putInnerHTML(document.getElementById("videoCount"), listHTML); if(Data.getVideoCount() < this.LASTIDX) { @@ -505,7 +474,7 @@ Display.setVideoListPosition = function(position, move) for(i = 0; i <= this.LASTIDX; i++) { listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i]); // listHTML = Data.getCurrentItem().childs[i].title; - widgetAPI.putInnerHTML(this.videoList[i], listHTML); + Display.putInnerHTML(this.videoList[i], listHTML); if(i == this.currentWindow) this.selectItem(this.videoList[i]); @@ -517,7 +486,7 @@ Display.setVideoListPosition = function(position, move) for(i = 0; i <= this.LASTIDX; i++) { listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position - this.currentWindow]); // listHTML = Data.getCurrentItem().childs[i + position - this.currentWindow].title; - widgetAPI.putInnerHTML(this.videoList[i], listHTML); + Display.putInnerHTML(this.videoList[i], listHTML); } } } @@ -530,7 +499,7 @@ Display.setVideoListPosition = function(position, move) for(i = 0; i <= this.LASTIDX; i++) { listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position - this.currentWindow]); // listHTML = Data.getCurrentItem().childs[i + position - this.currentWindow].title; - widgetAPI.putInnerHTML(this.videoList[i], listHTML); + Display.putInnerHTML(this.videoList[i], listHTML); if(i == this.currentWindow) this.selectItem(this.videoList[i]); @@ -542,7 +511,7 @@ Display.setVideoListPosition = function(position, move) for(i = 0; i <= this.LASTIDX; i++) { listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position]); // listHTML = Data.getCurrentItem().childs[i + position].title; - widgetAPI.putInnerHTML(this.videoList[i], listHTML); + Display.putInnerHTML(this.videoList[i], listHTML); } } } @@ -551,7 +520,7 @@ Display.setVideoListPosition = function(position, move) Display.setDescription = function(description) { var descriptionElement = document.getElementById("description"); - widgetAPI.putInnerHTML(descriptionElement, description); + Display.putInnerHTML(descriptionElement, description); }; //-------------------------------------------------------- @@ -583,7 +552,7 @@ Display.setVolume = function(level) var volumeElement = document.getElementById("volumeInfo"); - widgetAPI.putInnerHTML(volumeElement, "     " + Audio.getVolume()); + Display.putInnerHTML(volumeElement, "     " + Audio.getVolume()); }; // Called by main @@ -606,7 +575,7 @@ Display.handlerHideVolume = function() { //--------------------------------------------------------- Display.showPopup = function(text) { var oldHTML = document.getElementById("popup").innerHTML; - widgetAPI.putInnerHTML(document.getElementById("popup"), oldHTML + "
" + text); + Display.putInnerHTML(document.getElementById("popup"), oldHTML + "
" + text); this.popupOlHandler.show(); }; @@ -617,7 +586,7 @@ Display.handlerShowPopup = function() { Display.handlerHidePopup = function() { document.getElementById("popup").style.display="none"; - widgetAPI.putInnerHTML(document.getElementById("popup"), ""); + Display.putInnerHTML(document.getElementById("popup"), ""); }; //--------------------------------------------------------- @@ -635,9 +604,9 @@ Display.handlerShowProgress = function() { document.getElementById("overlay").style.display="block"; if (Player.isRecording == true) { document.getElementById("olRecProgressBar").style.display="block"; - var now = pluginTime.GetEpochTime(); + var now = Display.GetEpochTime(); var remaining = Math.round(((Player.startTime + Player.duration) - now) * 100/ Player.duration); - alert (" remaining= " + remaining); + Main.log (" remaining= " + remaining); var elm = document.getElementById("olRecProgressBar"); elm.style.display="block"; elm.style.width = remaining + "%"; @@ -648,12 +617,12 @@ Display.handlerShowProgress = function() { var timePercent = (Player.curPlayTime *100)/ Player.totalTime; - alert("show OL Progress timePercent= " + timePercent); + Main.log("show OL Progress timePercent= " + timePercent); document.getElementById("olProgressBar").style.width = timePercent + "%"; var timeElement = document.getElementById("olTimeInfo"); - widgetAPI.putInnerHTML(timeElement, Player.curPlayTimeStr + " / " + Player.totalTimeStr); + Display.putInnerHTML(timeElement, Player.curPlayTimeStr + " / " + Player.totalTimeStr); var nowElement = document.getElementById("olNow"); var Digital=new Date(); @@ -661,14 +630,14 @@ Display.handlerShowProgress = function() { var minutes=Digital.getMinutes(); if (minutes<=9) minutes='0'+minutes; - widgetAPI.putInnerHTML(nowElement, hours + ':' + minutes); + Display.putInnerHTML(nowElement, hours + ':' + minutes); }; //---------------------------------------- function OverlayHandler (n) { - this.pluginTime = null; +// this.pluginTime = null; this.active = false; this.startTime = 0; this.hideTime = 0; @@ -684,34 +653,34 @@ OverlayHandler.prototype.init = function(showcb, hidecb) { var success = true; this.showCallback = showcb; this.hideCallback = hidecb; - this.pluginTime = document.getElementById("pluginTime"); +/* this.pluginTime = document.getElementById("pluginTime"); if (!this.pluginTime) { - alert(this.handlerName + " cannot aquire time plugin : " + success); + Main.log(this.handlerName + " cannot aquire time plugin : " + success); success = false; } -// alert(this.handlerName + " is initialized"); +*/ + // Main.log(this.handlerName + " is initialized"); return success; }; OverlayHandler.prototype.checkHideCallback = function () { - var pluginTime = document.getElementById("pluginTime"); - var now = pluginTime.GetEpochTime(); -// alert(that.handlerName + "checkHideCallback: now= " + now + " hideTime= " + that.hideTime + " delta= " + (now - that.hideTime)); + var now = Display.GetEpochTime(); + // Main.log(that.handlerName + "checkHideCallback: now= " + now + " hideTime= " + that.hideTime + " delta= " + (now - that.hideTime)); if (now >= this.hideTime) { -// alert(this.handlerName + "hiding " + this.handlerName + " howDur: act= " + (now - this.startTime) + " int= " + (this.hideTime-this.startTime)); +// Main.log(this.handlerName + "hiding " + this.handlerName + " howDur: act= " + (now - this.startTime) + " int= " + (this.hideTime-this.startTime)); this.olDelay = 3000; if (this.hideCallback) { this.hideCallback(); } else - alert(this.handlerName + ": No hideCallback defined - ignoring " ); + Main.log(this.handlerName + ": No hideCallback defined - ignoring " ); this.active = false; return; } var delay = (this.hideTime - now) * 1000; -// alert(this.handlerName + "checkHideCallback: new timeout= " +delay); +// Main.log(this.handlerName + "checkHideCallback: new timeout= " +delay); // pass an anonymous function var self = this; @@ -720,10 +689,12 @@ OverlayHandler.prototype.checkHideCallback = function () { OverlayHandler.prototype.show = function() { if (!this.active ) { - this.startTime = this.pluginTime.GetEpochTime(); +// this.startTime = this.pluginTime.GetEpochTime(); + this.startTime = Display.GetEpochTime(); + this.hideTime = this.startTime + (this.olDelay / 1000); -// alert(this.handlerName + " showing " + this.handlerName + " from= " + this.startTime + " to at least= " + this.hideTime); +// Main.log(this.handlerName + " showing " + this.handlerName + " from= " + this.startTime + " to at least= " + this.hideTime); if (this.showCallback) { this.showCallback(); @@ -732,11 +703,12 @@ OverlayHandler.prototype.show = function() { this.active = true; } else - alert(this.handlerName + ": No showCallback defined - ignoring " ); + Main.log(this.handlerName + ": No showCallback defined - ignoring " ); } else { -// alert(this.handlerName + " extending showtime for " + this.handlerName + " for another " + (this.olDelay /1000)+ "sec"); - this.hideTime = this.pluginTime.GetEpochTime() + (this.olDelay /1000); +// Main.log(this.handlerName + " extending showtime for " + this.handlerName + " for another " + (this.olDelay /1000)+ "sec"); +// this.hideTime = this.pluginTime.GetEpochTime() + (this.olDelay /1000); + this.hideTime = Display.GetEpochTime() + (this.olDelay /1000); } }; @@ -745,12 +717,12 @@ OverlayHandler.prototype.cancel = function () { if (!this.active) return; -// alert("cancel for handler " + this.handlerName); +// Main.log("cancel for handler " + this.handlerName); if (this.hideCallback) { this.hideCallback(); } else - alert(this.handlerName + ": No hideCallback defined - ignoring " ); + Main.log(this.handlerName + ": No hideCallback defined - ignoring " ); this.active = false; window.clearTimeout(this.timeoutObj); diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index fa3c863..a122557 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -1,7 +1,13 @@ -var widgetAPI = new Common.API.Widget(); -var tvKey = new Common.API.TVKeyValue(); +var widgetAPI = null; +var tvKey = null; -var custom = window.deviceapis.customdevice || {}; +try { + widgetAPI = new Common.API.Widget(); + tvKey = new Common.API.TVKeyValue(); +} +catch (e) { +} +//var custom = window.deviceapis.customdevice || {}; /* @@ -39,6 +45,17 @@ var Main = { Main.onLoad = function() { Network.init(); + try { + widgetAPI.sendReadyEvent(); + } + catch (e) { + Config.deviceType = 1; + tvKey = Main.tvKeys; + + Main.log("Not a Samsung Smart TV" ); +// Display.showPopup("Not a Samsung Smart TV. Lets see, how far we come"); + } + Display.init(); this.defKeyHndl = new cDefaulKeyHndl; @@ -47,23 +64,22 @@ Main.onLoad = function() { this.menuKeyHndl = new cMenuKeyHndl(this.defKeyHndl); this.selectMenuKeyHndl = new cSelectMenuKeyHndl(this.defKeyHndl); - alert (" created KeyHandlers"); - + Main.log (" created KeyHandlers"); + Config.init(); }; // Called by Config, when done // TODO: Send sendReadyEvent early and show a splash screen during startup Main.init = function () { - alert("Main.init()"); + Main.log("Main.init()"); if ( Player.init() && Audio.init() && Server.init() ) { Display.setVolume( Audio.getVolume() ); // Start retrieving data from server - Server.dataReceivedCallback = function() - { + Server.dataReceivedCallback = function() { /* Use video information when it has arrived */ - alert("Server.dataReceivedCallback"); +// Main.log("Server.dataReceivedCallback"); Display.setVideoList(Main.selectedVideo); Display.show(); @@ -76,20 +92,32 @@ Main.init = function () { document.getElementById("splashScreen").style.display="none"; - widgetAPI.sendReadyEvent(); } else { - alert("Failed to initialise"); + Main.log("Failed to initialise"); } }; Main.log = function (msg) { - if (Config.serverUrl == "" ) - return; + // alert redirect + if (Config.deviceType == 0) { + alert (msg); + } + else { + console.log(msg); + } + +}; + +Main.logToServer = function (msg) { +// if (Config.serverUrl == "" ) +// return; var XHRObj = new XMLHttpRequest(); - XHRObj.open("POST", Config.serverUrl + "/log", true); +// XHRObj.open("POST", Config.serverUrl + "/log", true); + XHRObj.open("POST", "http://192.168.1.122:8000/log", true); + XHRObj.send("CLOG: " + msg); }; @@ -99,14 +127,14 @@ Main.onUnload = function() }; Main.changeState = function (state) { - alert("change state: new state= " + state); + Main.log("change state: new state= " + state); var old_state = this.state; this.state = state; switch (this.state) { case 0: Main.selectMenuKeyHndl.select = old_state; - alert ("old Select= " + Main.selectMenuKeyHndl.select); + Main.log ("old Select= " + Main.selectMenuKeyHndl.select); Display.resetSelectItems(Main.selectMenuKeyHndl.select); document.getElementById("selectScreen").style.display="block"; @@ -160,10 +188,10 @@ Main.recordingsSelected = function() { Server.setSort(true); if (Config.format == "") { Server.fetchVideoList(Config.serverUrl + "/recordings.xml?model=samsung"); /* Request video information from server */ - alert("fetchVideoList from: " + Config.serverUrl + "/recordings.xml?model=samsung"); + Main.log("fetchVideoList from: " + Config.serverUrl + "/recordings.xml?model=samsung"); } else { - Main.log("Using format " + Config.format); + Main.logToServer("Using format " + Config.format); if (Config.format == "") Server.fetchVideoList(Config.serverUrl + "/recordings.xml?model=samsung&has4hd=false"); /* Request video information from server */ else @@ -202,11 +230,10 @@ Main.mediaSelected = function() { }; Main.optionsSelected = function() { - alert ("Main.optionsSelected"); + Main.log ("Main.optionsSelected"); }; -Main.enableKeys = function() -{ +Main.enableKeys = function() { document.getElementById("anchor").focus(); }; @@ -218,7 +245,7 @@ Main.keyDown = function() { break; case 1: // Live - alert("Live - Main.keyDown PlayerState= " + Player.getState()); + Main.log("Live - Main.keyDown PlayerState= " + Player.getState()); if(Player.getState() == Player.STOPPED) { // Menu Key this.menuKeyHndl.handleKeyDown(); @@ -232,7 +259,7 @@ Main.keyDown = function() { case 2: case 3: // recordings - alert("Recordings - Main.keyDown PlayerState= " + Player.getState()); + Main.log("Recordings - Main.keyDown PlayerState= " + Player.getState()); if(Player.getState() == Player.STOPPED) { // Menu Key this.menuKeyHndl.handleKeyDown(); @@ -244,16 +271,17 @@ Main.keyDown = function() { break; case 4: - alert ("ERROR: Wrong State"); + Main.log ("ERROR: Wrong State"); break; }; }; Main.playItem = function (url) { - alert(Main.state + " playItem for " +Data.getCurrentItem().childs[Main.selectedVideo].payload.link); + Main.log(Main.state + " playItem for " +Data.getCurrentItem().childs[Main.selectedVideo].payload.link); var start_time = Data.getCurrentItem().childs[Main.selectedVideo].payload.start; var duration = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur; - var now = Display.pluginTime.GetEpochTime(); + var now = Display.GetEpochTime(); + document.getElementById("olRecProgressBar").style.display="none"; switch (this.state) { @@ -267,12 +295,12 @@ Main.playItem = function (url) { Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; // Display.updateTotalTime(Player.totalTime); var digi = new Date((Data.getCurrentItem().childs[Main.selectedVideo].payload.start*1000)); - alert (" Date(): StartTime= " + digi.getHours() + ":" + digi.getMinutes() + ":" + digi.getSeconds()); + Main.log (" Date(): StartTime= " + digi.getHours() + ":" + digi.getMinutes() + ":" + digi.getSeconds()); // Player.cptOffset = (now - Data.getCurrentItem().childs[Main.selectedVideo].payload.start) * 1000; Player.setCurrentPlayTimeOffset((now - Data.getCurrentItem().childs[Main.selectedVideo].payload.start) * 1000); Player.OnCurrentPlayTime(0); - alert ("Live now= " + now + " StartTime= " + Data.getCurrentItem().childs[Main.selectedVideo].payload.start + " offset= " +Player.cptOffset ); - alert("Live Content= " + Data.getCurrentItem().childs[Main.selectedVideo].title + " dur= " + Data.getCurrentItem().childs[Main.selectedVideo].payload.dur); + Main.log ("Live now= " + now + " StartTime= " + Data.getCurrentItem().childs[Main.selectedVideo].payload.start + " offset= " +Player.cptOffset ); + Main.log("Live Content= " + Data.getCurrentItem().childs[Main.selectedVideo].title + " dur= " + Data.getCurrentItem().childs[Main.selectedVideo].payload.dur); break; case 2: case 3: @@ -280,10 +308,10 @@ Main.playItem = function (url) { // Player.cptOffset = 0; Player.isLive = false; Player.isRecording = false; - alert(" playItem: now= " + now + " start_time= " + start_time + " dur= " + duration + " (Start + Dur - now)= " + ((start_time + duration) -now)); + Main.log(" playItem: now= " + now + " start_time= " + start_time + " dur= " + duration + " (Start + Dur - now)= " + ((start_time + duration) -now)); if ((now - (start_time + duration)) < 0) { // still recording - alert("*** Still Recording! ***"); + Main.log("*** Still Recording! ***"); Player.isRecording = true; Player.startTime = start_time; Player.duration = duration; @@ -318,7 +346,7 @@ Main.selectPageUp = function(up) { var first_item = this.selectedVideo - Display.currentWindow; if (first_item < 0 ) first_item = 0; - alert("selectPageUp: this.selectedVideo= " + this.selectedVideo + " first_item= " + first_item); + Main.log("selectPageUp: this.selectedVideo= " + this.selectedVideo + " first_item= " + first_item); Display.setVideoList(this.selectedVideo, first_item); }; @@ -331,13 +359,13 @@ Main.selectPageDown = function(down) { } var first_item = this.selectedVideo - Display.currentWindow; - alert("selectPageDown: this.selectedVideo= " + this.selectedVideo + " first_item= " + first_item); + Main.log("selectPageDown: this.selectedVideo= " + this.selectedVideo + " first_item= " + first_item); Display.setVideoList(this.selectedVideo, first_item); }; Main.nextVideo = function(no) { this.selectedVideo = (this.selectedVideo + no) % Data.getVideoCount(); - alert("nextVideo= " + this.selectedVideo); + Main.log("nextVideo= " + this.selectedVideo); }; Main.previousVideo = function(no) { @@ -346,7 +374,7 @@ Main.previousVideo = function(no) { if (this.selectedVideo < 0) { this.selectedVideo += Data.getVideoCount(); } - alert("previousVideo= " + this.selectedVideo); + Main.log("previousVideo= " + this.selectedVideo); }; @@ -409,7 +437,7 @@ Main.muteMode = function() break; default: - alert("ERROR: unexpected mode in muteMode"); + Main.log("ERROR: unexpected mode in muteMode"); break; } }; @@ -418,6 +446,12 @@ Main.muteMode = function() Main.getKeyCode = function(code) { var res = ""; + + if (Config.deviceType != 0) { + // Not a Samsung + res = "Unknown Key (" + code + ")"; + return res; + } switch(code) { case tvKey.KEY_1: res = "KEY_1"; @@ -646,7 +680,7 @@ Main.getKeyCode = function(code) { function cPlayStateKeyHndl(def_hndl) { this.defaultKeyHandler = def_hndl; this.handlerName = "PlayStateKeyHanlder"; - alert(this.handlerName + " created"); + Main.log(this.handlerName + " created"); }; @@ -655,70 +689,70 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { var keyCode = event.keyCode; if(Player.getState() == Player.STOPPED) { - alert("ERROR: Wrong state - STOPPED"); + Main.log("ERROR: Wrong state - STOPPED"); return; } - alert(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); + Main.log(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); switch(keyCode) { case tvKey.KEY_1: - alert("KEY_1 pressed"); + Main.log("KEY_1 pressed"); Display.showProgress(); Player.jumpToVideo(10); break; case tvKey.KEY_2: - alert("KEY_2 pressed"); + Main.log("KEY_2 pressed"); Display.showProgress(); Player.jumpToVideo(20); break; case tvKey.KEY_3: - alert("KEY_3 pressed"); + Main.log("KEY_3 pressed"); Display.showProgress(); Player.jumpToVideo(30); break; case tvKey.KEY_4: - alert("KEY_4 pressed"); + Main.log("KEY_4 pressed"); Display.showProgress(); Player.jumpToVideo(40); break; case tvKey.KEY_5: - alert("KEY_5 pressed"); + Main.log("KEY_5 pressed"); Display.showProgress(); Player.jumpToVideo(50); break; case tvKey.KEY_6: - alert("KEY_6 pressed"); + Main.log("KEY_6 pressed"); Display.showProgress(); Player.jumpToVideo(60); break; case tvKey.KEY_7: - alert("KEY_7 pressed"); + Main.log("KEY_7 pressed"); Display.showProgress(); Player.jumpToVideo(70); break; case tvKey.KEY_8: - alert("KEY_8 pressed"); + Main.log("KEY_8 pressed"); Display.showProgress(); Player.jumpToVideo(80); break; case tvKey.KEY_9: - alert("KEY_9 pressed"); + Main.log("KEY_9 pressed"); Display.showProgress(); Player.jumpToVideo(90); break; case tvKey.KEY_RIGHT: case tvKey.KEY_FF: - alert("FF"); + Main.log("FF"); Display.showProgress(); Player.skipForwardVideo(); break; case tvKey.KEY_LEFT: case tvKey.KEY_RW: - alert("RW"); + Main.log("RW"); Display.showProgress(); Player.skipBackwardVideo(); break; @@ -726,7 +760,7 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { case tvKey.KEY_ENTER: case tvKey.KEY_PLAY: case tvKey.KEY_PANEL_ENTER: - alert("ENTER"); + Main.log("ENTER"); if(Player.getState() == Player.PAUSED) { Player.resumeVideo(); } @@ -735,19 +769,19 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { case tvKey.KEY_RETURN: case tvKey.KEY_PANEL_RETURN: case tvKey.KEY_STOP: - alert("STOP"); + Main.log("STOP"); // Player.setWindow(); Player.stopVideo(); break; case tvKey.KEY_PAUSE: - alert("PAUSE"); + Main.log("PAUSE"); Player.pauseVideo(); break; case tvKey.KEY_UP: case tvKey.KEY_PANEL_VOL_UP: case tvKey.KEY_VOL_UP: - alert("VOL_UP"); + Main.log("VOL_UP"); Display.showVolume(); if(Main.mute == 0) Audio.setRelativeVolume(0); @@ -756,19 +790,77 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { case tvKey.KEY_DOWN: case tvKey.KEY_PANEL_VOL_DOWN: case tvKey.KEY_VOL_DOWN: - alert("VOL_DOWN"); + Main.log("VOL_DOWN"); Display.showVolume(); if(Main.mute == 0) Audio.setRelativeVolume(1); break; default: - alert("Calling Default Key Hanlder"); + Main.log("Calling Default Key Hanlder"); this.defaultKeyHandler.handleDefKeyDown(keyCode); break; } }; +Main.tvKeys = { + KEY_UP :38, + KEY_DOWN :40, + KEY_LEFT :37, + KEY_RIGHT :39, + KEY_ENTER :13, + + KEY_STOP :27, // ESC +// KEY_MUTE :27, + + // Un-used keycodes + KEY_RETURN :88, + KEY_EXIT :45, + KEY_RED :108, + KEY_GREEN :20, + KEY_YELLOW :21, + KEY_BLUE :22, + KEY_RW :69, + KEY_PAUSE :74, + KEY_FF :72, + KEY_PLAY :71, + KEY_STOP :70, + KEY_1 :101, + KEY_2 :98, + KEY_3 :6, + KEY_4 :8, + KEY_5 :9, + KEY_6 :10, + KEY_7 :12, + KEY_8 :13, + KEY_9 :14, + KEY_0 :17, + + KEY_PANEL_CH_UP :104, + KEY_PANEL_CH_DOWN :106, + KEY_PANEL_VOL_UP :203, + KEY_PANEL_VOL_DOWN :204, + KEY_PANEL_ENTER :309, + KEY_PANEL_SOURCE :612, + KEY_PANEL_MENU :613, + KEY_PANEL_POWER :614, + + KEY_POWER :76, + KEY_VOL_UP :7, + KEY_VOL_DOWN :11, + KEY_CH_UP :68, + KEY_CH_DOWN :65, + KEY_MTS :655, + KEY_12 :1057, + KEY_AD :1039, + KEY_FF_ :1078, + KEY_REWIND_ :1080, + KEY_SLEEP :1097, + KEY_STEP :1023, + KEY_HOME :1118 + +}; + //--------------------------------------------------- // Live Play State Key Handler @@ -777,7 +869,7 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { function cLivePlayStateKeyHndl(def_hndl) { this.defaultKeyHandler = def_hndl; this.handlerName = "LivePlayStateKeyHanlder"; - alert(this.handlerName + " created"); + Main.log(this.handlerName + " created"); }; @@ -786,16 +878,16 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { var keyCode = event.keyCode; if(Player.getState() == Player.STOPPED) { - alert("ERROR: Wrong state - STOPPED"); + Main.log("ERROR: Wrong state - STOPPED"); return; } - alert(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); + Main.log(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); switch(keyCode) { case tvKey.KEY_1: case tvKey.KEY_CH_UP: - alert("Prog Up"); + Main.log("Prog Up"); Display.showProgress(); Player.stopVideo(); Main.previousVideo(1); @@ -805,7 +897,7 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { case tvKey.KEY_4: case tvKey.KEY_CH_DOWN: - alert("Prog Down"); + Main.log("Prog Down"); Display.showProgress(); Player.stopVideo(); Main.nextVideo(1); @@ -816,7 +908,7 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { case tvKey.KEY_ENTER: case tvKey.KEY_PLAY: case tvKey.KEY_PANEL_ENTER: - alert("ENTER"); + Main.log("ENTER"); Display.hide(); Display.showProgress(); break; @@ -824,19 +916,19 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { case tvKey.KEY_RETURN: case tvKey.KEY_PANEL_RETURN: case tvKey.KEY_STOP: - alert("STOP"); + Main.log("STOP"); Player.stopVideo(); Display.setVideoList(Main.selectedVideo); Display.show(); break; case tvKey.KEY_PAUSE: - alert("PAUSE"); + Main.log("PAUSE"); break; case tvKey.KEY_UP: case tvKey.KEY_PANEL_VOL_UP: case tvKey.KEY_VOL_UP: - alert("VOL_UP"); + Main.log("VOL_UP"); Display.showVolume(); if(Main.mute == 0) Audio.setRelativeVolume(0); @@ -845,7 +937,7 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { case tvKey.KEY_DOWN: case tvKey.KEY_PANEL_VOL_DOWN: case tvKey.KEY_VOL_DOWN: - alert("VOL_DOWN"); + Main.log("VOL_DOWN"); Display.showVolume(); if(Main.mute == 0) Audio.setRelativeVolume(1); @@ -863,34 +955,34 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { function cMenuKeyHndl (def_hndl) { this.defaultKeyHandler = def_hndl; this.handlerName = "MenuKeyHandler"; - alert(this.handlerName + " created"); + Main.log(this.handlerName + " created"); }; cMenuKeyHndl.prototype.handleKeyDown = function () { var keyCode = event.keyCode; - alert(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); + Main.log(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); switch(keyCode) { case tvKey.KEY_RIGHT: - alert("Right"); + Main.log("Right"); Main.selectPageDown(Main.DOWN); break; case tvKey.KEY_LEFT: - alert("Left"); + Main.log("Left"); Main.selectPageUp(Main.UP); break; case tvKey.KEY_ENTER: case tvKey.KEY_PLAY: case tvKey.KEY_PANEL_ENTER: - alert("ENTER"); + Main.log("ENTER"); if (Data.getCurrentItem().childs[Main.selectedVideo].isFolder == true) { - alert ("selectFolder= " +Main.selectedVideo); + Main.log ("selectFolder= " +Main.selectedVideo); Data.selectFolder(Main.selectedVideo); Main.selectedVideo= 0; Display.setVideoList(Main.selectedVideo); @@ -907,24 +999,24 @@ cMenuKeyHndl.prototype.handleKeyDown = function () { case tvKey.KEY_RETURN: case tvKey.KEY_PANEL_RETURN: if (Data.isRootFolder() == true) { - alert ("root reached"); + Main.log ("root reached"); Main.changeState(0); } else { Main.selectedVideo = Data.folderUp(); - alert("folderUp selectedVideo= " + Main.selectedVideo); + Main.log("folderUp selectedVideo= " + Main.selectedVideo); Display.setVideoList(Main.selectedVideo); } break; case tvKey.KEY_DOWN: - alert("DOWN"); + Main.log("DOWN"); Main.selectNextVideo(Main.DOWN); break; case tvKey.KEY_UP: - alert("UP"); + Main.log("UP"); Main.selectPreviousVideo(Main.UP); break; @@ -941,7 +1033,7 @@ cMenuKeyHndl.prototype.handleKeyDown = function () { function cSelectMenuKeyHndl (def_hndl) { this.defaultKeyHandler = def_hndl; this.handlerName = "SelectMenuKeyHandler"; - alert(this.handlerName + " created"); + Main.log(this.handlerName + " created"); this.select = 1; this.selectMax = 4; // Highest Select Entry @@ -949,15 +1041,15 @@ function cSelectMenuKeyHndl (def_hndl) { cSelectMenuKeyHndl.prototype.handleKeyDown = function () { var keyCode = event.keyCode; - alert(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); + Main.log(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); switch(keyCode) { case tvKey.KEY_ENTER: case tvKey.KEY_PLAY: case tvKey.KEY_PANEL_ENTER: - alert("ENTER"); - alert ("CurSelect= " + this.select); + Main.log("ENTER"); + Main.log ("CurSelect= " + this.select); Main.changeState (this.select); @@ -966,7 +1058,7 @@ cSelectMenuKeyHndl.prototype.handleKeyDown = function () { if (++this.select > this.selectMax) this.select = 1; Display.selectItem(document.getElementById("selectItem"+this.select)); - alert("DOWN " +this.select); + Main.log("DOWN " +this.select); break; case tvKey.KEY_UP: @@ -976,7 +1068,7 @@ cSelectMenuKeyHndl.prototype.handleKeyDown = function () { this.select = this.selectMax; Display.selectItem(document.getElementById("selectItem"+this.select)); - alert("UP "+ this.select); + Main.log("UP "+ this.select); break; default: this.defaultKeyHandler.handleDefKeyDown(keyCode); @@ -991,38 +1083,38 @@ cSelectMenuKeyHndl.prototype.handleKeyDown = function () { function cDefaulKeyHndl() { this.handlerName = "DefaultKeyHanlder"; - alert(this.handlerName + " created"); + Main.log(this.handlerName + " created"); }; cDefaulKeyHndl.prototype.handleDefKeyDown = function (keyCode) { - alert("cDefaulKeyHndl::handleKeyDown: " + Main.getKeyCode(keyCode)); + Main.log("cDefaulKeyHndl::handleKeyDown: " + Main.getKeyCode(keyCode)); switch(keyCode) { case tvKey.KEY_EXIT: - alert(this.handlerName +"Exit"); + Main.log(this.handlerName +"Exit"); Player.stopVideo(); widgetAPI.sendReturnEvent(); break; case tvKey.KEY_VOL_UP: - alert(this.handlerName + "VOL_UP"); + Main.log(this.handlerName + "VOL_UP"); Display.showVolume(); if(Main.mute == 0) Audio.setRelativeVolume(0); break; case tvKey.KEY_VOL_DOWN: - alert(this.handlerName + "VOL_DOWN"); + Main.log(this.handlerName + "VOL_DOWN"); Display.showVolume(); if(Main.mute == 0) Audio.setRelativeVolume(1); break; case tvKey.KEY_MUTE: - alert(this.handlerName + "MUTE"); + Main.log(this.handlerName + "MUTE"); Main.muteMode(); break; default: - alert(this.handlerName + "Unhandled key"); + Main.log(this.handlerName + "Unhandled key"); break; } }; diff --git a/smarttv-client/Javascript/Network.js b/smarttv-client/Javascript/Network.js index 1dfcafc..b4c0772 100755 --- a/smarttv-client/Javascript/Network.js +++ b/smarttv-client/Javascript/Network.js @@ -1,18 +1,25 @@ var Network = { plugin : null, ownMac : "", - ownGw : "" + ownGw : "", + isInited: false }; Network.init = function () { this.plugin = document.getElementById("pluginNetwork"); - var nw_type = this.plugin.GetActiveType(); - if ((nw_type == 0) || (nw_type == 1)) { - this.ownMac = this.plugin.GetMAC(nw_type); - this.ownGw = this.plugin.GetGateway(nw_type); + try { + var nw_type = this.plugin.GetActiveType(); + if ((nw_type == 0) || (nw_type == 1)) { + this.ownMac = this.plugin.GetMAC(nw_type); + this.ownGw = this.plugin.GetGateway(nw_type); + } + Main.log( "ownMac= " + this.ownMac); + Main.log ("ownGw= " + this.ownGw); + this.isInited = true; + } + catch (e) { + // Code for Non Samsung SmartTV here } - alert( "ownMac= " + this.ownMac); - alert ("ownGw= " + this.ownGw); } ; diff --git a/smarttv-client/Javascript/Options.js b/smarttv-client/Javascript/Options.js index 11095c6..31e9d50 100755 --- a/smarttv-client/Javascript/Options.js +++ b/smarttv-client/Javascript/Options.js @@ -10,11 +10,11 @@ Options.init = function() { }; Options.onComplete = function () { - alert("Completed"); + Main.log("Completed"); }; Options.onEnter = function () { - alert("Enter: " + document.getElementById("widgetServerAddr").value ); + Main.log("Enter: " + document.getElementById("widgetServerAddr").value ); Config.updateContext(document.getElementById("widgetServerAddr").value); @@ -38,7 +38,7 @@ Options.onBlue = function () { Options.onImeCreated = function(obj) { // _g_ime.keySet ("12key"); // obj.setKeySetFunc('12key'); - Main.log ("Options.onImeCreated()"); + Main.logToServer ("Options.onImeCreated()"); obj.setKeyFunc(tvKey.KEY_RETURN, function(keyCode) { widgetAPI.sendReturnEvent(); return false; } ); obj.setKeyFunc(tvKey.KEY_EXIT, function(keyCode) { widgetAPI.sendExitEvent(); return false; } ); @@ -51,7 +51,7 @@ Options.onImeCreated = function(obj) { obj.setEnterFunc(Options.onEnter); if (obj.setMode("_num") == false) { - Main.log("obj.setMode(\"_num\") returns false"); + Main.logToServer("obj.setMode(\"_num\") returns false"); } Options.imeBox.setOnCompleteFunc(Options.onComplete); @@ -61,8 +61,8 @@ Options.onImeCreated = function(obj) { Options.onReady = function () { document.getElementById('widgetServerAddr').focus(); - Main.log ("Options.onReady()"); - alert ("KeySet= " + this.imeBox.getKeySet()); + Main.logToServer ("Options.onReady()"); + Main.log ("KeySet= " + this.imeBox.getKeySet()); }; diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index a04c26f..716f36c 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -1,3 +1,7 @@ +/* + * This module only works with the Samsung Media Players. For other player objects, the code need to be adjusted + */ + var Player = { plugin : null, @@ -29,19 +33,19 @@ var Player = Player.init = function() { var success = true; - alert("success vale : " + success); + Main.log("success vale : " + success); this.state = this.STOPPED; this.plugin = document.getElementById("pluginPlayer"); if (!this.plugin) { - alert("success vale this.plugin : " + success); + Main.log("success vale this.plugin : " + success); success = false; } - var vermsg = this.plugin.GetPlayerVersion(); - alert ("player plugin version: " +vermsg); +// var vermsg = this.plugin.GetPlayerVersion(); +// Main.log ("player plugin version: " +vermsg); this.plugin.OnCurrentPlayTime = 'Player.OnCurrentPlayTime'; this.plugin.OnStreamInfoReady = 'Player.OnStreamInfoReady'; @@ -53,13 +57,13 @@ Player.init = function() { this.plugin.OnNetworkDisconnected = 'Player.OnNetworkDisconnected'; // when the ethernet is disconnected or the streaming server stops supporting the content in the middle of streaming. this.plugin.OnRenderingComplete = 'Player.OnRenderingComplete'; - alert("success= " + success); + Main.log("success= " + success); return success; }; Player.deinit = function() { - alert("Player deinit !!! " ); + Main.log("Player deinit !!! " ); if (this.plugin) { @@ -77,47 +81,42 @@ Player.setFullscreen = function() { Player.setBuffer = function (btr){ var res = true; -// var m_bitrate = Config.tgtBufferBitrate; -// var buffer_sec = Config.totalBufferDuration; -// var init_buffer_perc = Config.initialBuffer; -// var pend_buffer_perc = Config.pendingBuffer; - var buffer_byte = (Config.totalBufferDuration * Config.tgtBufferBitrate) / 8.0; - Main.log("Seting TotalBufferSize to " + Math.round(buffer_byte) +"Byte dur= " +Config.totalBufferDuration + "sec init_buffer_perc= " +Config.initialBuffer +"% pend_buffer_perc= " +Config.pendingBuffer + "% initialTimeOut= " +Config.initialTimeOut + "sec"); + Main.logToServer("Seting TotalBufferSize to " + Math.round(buffer_byte) +"Byte dur= " +Config.totalBufferDuration + "sec init_buffer_perc= " +Config.initialBuffer +"% pend_buffer_perc= " +Config.pendingBuffer + "% initialTimeOut= " +Config.initialTimeOut + "sec"); //The SetTotalBufferSize function sets the streaming buffer size of media player. res = this.plugin.SetTotalBufferSize(Math.round(buffer_byte)); if (res == false) { Display.showPopup("SetTotalBufferSize(" + Math.round(buffer_byte) +") returns error"); - Main.log("SetTotalBufferSize(" + Math.round(buffer_byte) +") returns error"); + Main.logToServer("SetTotalBufferSize(" + Math.round(buffer_byte) +") returns error"); } // The SetInitialBuffer function sets the first buffering size as percentage of buffer size before starting playback. res = this.plugin.SetInitialBuffer(Math.round( buffer_byte * Config.initialBuffer/ 100.0)); if (res == false) { Display.showPopup("SetInitialBuffer(" + Math.round(buffer_byte * Config.initialBuffer/ 100.0) +") returns error"); - Main.log("SetInitialBuffer(" + Math.round(buffer_byte * Config.initialBuffer/ 100.0) +") returns error"); + Main.logToServer("SetInitialBuffer(" + Math.round(buffer_byte * Config.initialBuffer/ 100.0) +") returns error"); } //he SetInitialTimeOut function sets the maximum time out value for initial buffering before starting playback. res = this.plugin.SetInitialTimeOut(Config.initialTimeOut); if (res == false) { Display.showPopup("SetInitialTimeOut(" + 2 +") returns error"); - Main.log("SetInitialTimeOut(" + 2 +") returns error"); + Main.logToServer("SetInitialTimeOut(" + 2 +") returns error"); } // The SetPendingBuffer function sets the size of a buffer as percentage of total buffer size that media player goes out from buffering status. res = this.plugin.SetPendingBuffer(Math.round(buffer_byte * Config.pendingBuffer /100.0)); if (res == false) { Display.showPopup("SetPendingBuffer(" + Math.round(buffer_byte * Config.pendingBuffer /100.0) +") returns error"); - Main.log("SetPendingBuffer(" + Math.round(buffer_byte * Config.pendingBuffer /100.0) +") returns error"); + Main.logToServer("SetPendingBuffer(" + Math.round(buffer_byte * Config.pendingBuffer /100.0) +") returns error"); } }; Player.setVideoURL = function(url) { this.url = url; - alert("URL = " + this.url); + Main.log("URL = " + this.url); }; Player.setCurrentPlayTimeOffset = function(val) { @@ -127,8 +126,12 @@ Player.setCurrentPlayTimeOffset = function(val) { }; Player.playVideo = function() { + if (Config.deviceType != 0) { + Display.showPopup ("Only supported for TVs"); + return; + } if (this.url == null) { - alert("No videos to play"); + Main.log("No videos to play"); } else { @@ -146,7 +149,7 @@ Player.playVideo = function() { Player.setBuffer(15000000.0); - alert ("StartPlayback for " + this.url); + Main.log ("StartPlayback for " + this.url); // if (this.plugin.StartPlayback() == false) // Display.showPopup("StartPlayback returns false"); @@ -157,7 +160,7 @@ Player.playVideo = function() { Player.pauseVideo = function() { Display.showProgress(); - Main.log("pauseVideo"); + Main.logToServer("pauseVideo"); this.state = this.PAUSED; Display.status("Pause"); @@ -179,12 +182,12 @@ Player.stopVideo = function() { } } else { - alert("Ignoring stop request, not in correct state"); + Main.log("Ignoring stop request, not in correct state"); } }; Player.resumeVideo = function() { - Main.log("resumeVideo"); + Main.logToServer("resumeVideo"); Display.showProgress(); this.state = this.PLAYING; Display.status("Play"); @@ -201,14 +204,14 @@ Player.jumpToVideo = function(percent) { Player.bufferState = 0; Display.showProgress(); if (this.state != this.PLAYING) { - alert ("Player not Playing"); + Main.log ("Player not Playing"); return; } if (this.totalTime == -1 && this.isLive == false) this.totalTime = this.plugin.GetDuration(); var tgt = Math.round(((percent-2)/100.0) * this.totalTime/ 1000.0); - alert("jumpToVideo= " + percent + "% of " + (this.totalTime/1000) + "sec tgt = " + tgt + "sec curPTime= " + (this.curPlayTime/1000) +"sec"); + Main.log("jumpToVideo= " + percent + "% of " + (this.totalTime/1000) + "sec tgt = " + tgt + "sec curPTime= " + (this.curPlayTime/1000) +"sec"); // Display.showPopup("jumpToVideo= " + percent + "% of " + (this.totalTime/1000) + "sec
--> tgt = " + tgt + "sec curPTime= " + (this.curPlayTime/1000)+"sec"); this.plugin.Stop(); @@ -240,7 +243,7 @@ Player.getState = function() { //------------------------------------------------ Player.onBufferingStart = function() { - Main.log("Buffer Start: " + Player.curPlayTime); + Main.logToServer("Buffer Start: " + Player.curPlayTime); Player.bufferStartTime = new Date().getTime(); Player.bufferState = 0; @@ -265,7 +268,7 @@ Player.onBufferingComplete = function() { Display.status("Play"); Display.hideStatus(); - Main.log("Buffer Completed - Buffering Duration= " + (new Date().getTime() - Player.bufferStartTime) + " ms"); + Main.logToServer("Buffer Completed - Buffering Duration= " + (new Date().getTime() - Player.bufferStartTime) + " ms"); Player.bufferState = 100; Display.bufferUpdate(); @@ -274,7 +277,7 @@ Player.onBufferingComplete = function() { Player.setFullscreen(); Display.hide(); - Main.log("onBufferingComplete "); + Main.logToServer("onBufferingComplete "); /* Player.pauseVideo(); window.setTimeout(Player.resumeVideo, 1000); */ }; @@ -283,40 +286,30 @@ Player.onBufferingComplete = function() { Player.OnCurrentPlayTime = function(time) { Player.curPlayTime = parseInt(time) + parseInt(Player.cptOffset); -// alert ("OnCurrentPlayTime time= " + time + " this.curPlayTime= " +this.curPlayTime ); -// Display.setTime(Player.curPlayTime); - // Update the Current Play Progress Bar Display.updateProgressBar(); if (Player.isRecording == true) { Display.updateRecBar(Player.startTime, Player.duration); } - Player.curPlayTimeStr = Display.getHumanTimeRepresentation(Player.curPlayTime); + Player.curPlayTimeStr = Display.durationString(Player.curPlayTime / 1000.0); + Display.updatePlayTime(); }; Player.OnStreamInfoReady = function() { - alert("*** OnStreamInfoReady ***"); -// Display.showPopup("*** OnStreamInfoReady *** "); -// Display.showPopup("GetCurrentBitrates= " + Player.plugin.GetCurrentBitrates()); - Main.log("GetCurrentBitrates= " + Player.plugin.GetCurrentBitrates()); + Main.log("*** OnStreamInfoReady ***"); + Main.logToServer("GetCurrentBitrates= " + Player.plugin.GetCurrentBitrates()); if ((Player.isLive == false) && (Player.isRecording == false)) { Player.totalTime = Player.plugin.GetDuration(); } -// Display.updateTotalTime (Player.totalTime); - Player.totalTimeStr = Display.getHumanTimeRepresentation(Player.totalTime); - - -// alert("totalTime= " + Player.totalTime + " totalTimeStr= " +Player.totalTimeStr); -// Display.setTotalTime(totalTime); - + Player.curPlayTimeStr = Display.durationString(Player.totalTime / 1000.0); /* var height = Player.plugin.GetVideoHeight(); var width = Player.GetVideoWidth(); Display.showPopup("Resolution= " + height + " x " +width); - alert("Resolution= " + height + " x " +width); + Main.log("Resolution= " + height + " x " +width); */ }; @@ -327,14 +320,14 @@ Player.OnRenderingComplete = function() { Player.OnConnectionFailed = function() { // fails to connect to the streaming server - alert ("ERROR: Failed to connect to the streaming server"); + Main.log ("ERROR: Failed to connect to the streaming server"); // widgetAPI.putInnerHTML(document.getElementById("popup"), "Failed to connect to the streaming server"); Display.showPopup("Failed to connect to the streaming server"); }; Player.OnStreamNotFound = function() { // 404 file not found - alert ("ERROR: Stream Not Found"); + Main.log ("ERROR: Stream Not Found"); // widgetAPI.putInnerHTML(document.getElementById("popup"), "Stream Not Found"); Display.showPopup("Stream Not Found"); @@ -342,7 +335,7 @@ Player.OnStreamNotFound = function() { Player.OnNetworkDisconnected = function() { // when the ethernet is disconnected or the streaming server stops supporting the content in the middle of streaming. - alert ("ERROR: Lost Stream (Unavailable?)"); + Main.log ("ERROR: Lost Stream (Unavailable?)"); // widgetAPI.putInnerHTML(document.getElementById("popup"), "Lost Stream (Unavailable?)"); Display.showPopup("Lost Stream (Unavailable?)"); diff --git a/smarttv-client/Javascript/Server.js b/smarttv-client/Javascript/Server.js index c64d320..ee23505 100755 --- a/smarttv-client/Javascript/Server.js +++ b/smarttv-client/Javascript/Server.js @@ -13,7 +13,7 @@ Server.init = function() var success = true; var splashElement = document.getElementById("splashStatus"); - widgetAPI.putInnerHTML(splashElement, "Starting Up"); + Display.putInnerHTML(splashElement, "Starting Up"); if (this.XHRObj) { this.XHRObj.destroy(); @@ -28,7 +28,7 @@ Server.setSort = function (val) { }; Server.fetchVideoList = function(url) { - alert("fetching Videos url= " + url); + Main.log("fetching Videos url= " + url); if (this.XHRObj == null) { this.XHRObj = new XMLHttpRequest(); } @@ -37,7 +37,7 @@ Server.fetchVideoList = function(url) { this.XHRObj.onreadystatechange = function() { var splashElement = document.getElementById("splashStatus"); - widgetAPI.putInnerHTML(splashElement, "State" + Server.XHRObj.readyState); + Display.putInnerHTML(splashElement, "State" + Server.XHRObj.readyState); if (Server.XHRObj.readyState == 4) { Server.createVideoList(); @@ -49,7 +49,7 @@ Server.fetchVideoList = function(url) { } else { var splashElement = document.getElementById("splashStatus"); - widgetAPI.putInnerHTML(splashElement, "Failed !!!" ); + Display.putInnerHTML(splashElement, "Failed !!!" ); Display.showPopup("Failed to create XHR"); if (this.errorCallback != null) { @@ -59,14 +59,14 @@ Server.fetchVideoList = function(url) { }; Server.createVideoList = function() { - alert ("creating Video list now"); - Main.log("creating Video list now"); + Main.log ("creating Video list now"); + Main.logToServer("creating Video list now"); var splashElement = document.getElementById("splashStatus"); - widgetAPI.putInnerHTML(splashElement, "Creating Video list now" ); + Display.putInnerHTML(splashElement, "Creating Video list now" ); if (this.XHRObj.status != 200) { - widgetAPI.putInnerHTML(splashElement, "XML Server Error " + this.XHRObj.status); + Display.putInnerHTML(splashElement, "XML Server Error " + this.XHRObj.status); Display.status("XML Server Error " + this.XHRObj.status); Display.showPopup("XML Server Error " + this.XHRObj.status); if (this.errorCallback != null) { @@ -78,7 +78,7 @@ Server.createVideoList = function() { var xmlResponse = this.XHRObj.responseXML; if (xmlResponse == null) { Display.status("xmlResponse == null" ); - widgetAPI.putInnerHTML(splashElement, "Error in XML File "); + Display.putInnerHTML(splashElement, "Error in XML File "); Display.showPopup("Error in XML File"); if (this.errorCallback != null) { this.errorCallback("XmlError"); @@ -89,18 +89,18 @@ Server.createVideoList = function() { // var xmlElement = this.XHRObj.responseXML.documentElement; if (!xmlElement) { - widgetAPI.putInnerHTML(splashElement, "Failed to get valid XML!!!"); + Display.putInnerHTML(splashElement, "Failed to get valid XML!!!"); Display.status("Failed to get valid XML"); Display.showPopup("Failed to get valid XML"); return; } else { - widgetAPI.putInnerHTML(splashElement, "Parsing ..."); + Display.putInnerHTML(splashElement, "Parsing ..."); var items = xmlElement.getElementsByTagName("item"); if (items.length == 0) { Display.showPopup("Something wrong. Response does not contain any item"); - Main.log("Something wrong. Response does not contain any item"); + Main.logToServer("Something wrong. Response does not contain any item"); }; @@ -120,7 +120,7 @@ Server.createVideoList = function() { } catch (e) { - alert("ERROR: "+e); + Main.log("ERROR: "+e); } var desc = descriptionElement.firstChild.data; @@ -137,7 +137,7 @@ Server.createVideoList = function() { } Data.completed(this.doSort); - widgetAPI.putInnerHTML(splashElement, "Done..."); + Display.putInnerHTML(splashElement, "Done..."); if (this.dataReceivedCallback) { -- cgit v1.2.3 From 00ebc89c5ed4abb7234a2ac40604839fb6399307 Mon Sep 17 00:00:00 2001 From: thlo Date: Thu, 27 Dec 2012 23:10:17 +0100 Subject: Correction of TotalPlaytime string. --- smarttv-client/Javascript/Config.js | 5 ++--- smarttv-client/Javascript/Main.js | 29 +++++++++++++++++++++++++++-- smarttv-client/Javascript/Player.js | 5 +++-- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index 9e111d9..518c6e6 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -2,7 +2,7 @@ var Config = { cfgFileName : "", XHRObj : null, xmlDocument : null, - serverUrl : "http://192.168.1.122:8000", // Will become the main URL for contacting the server. Form "http://:port" + serverUrl : "", // Will become the main URL for contacting the server. Form "http://:port" serverAddr : "", serverAddrDefault: "192.168.1.122:8000", format :"has", @@ -46,14 +46,13 @@ Config.init = function () { this.cfgFileName = curWidget.id + "/config.dta"; } catch (e) { - Main.log("curWidget.id does not exists. Is that a Samsung?"); + Main.log("curWidget.id does not exists. Is that really a Samsung?"); return; }; var fileSystemObj = new FileSystem(); if (fileSystemObj.isValidCommonPath(curWidget.id) == 0){ -// Display.showPopup ("First Launch of the Widget --> Launching Config Menu"); Main.log("First Launch of the Widget"); // should switch to the config screen here var res = fileSystemObj.createCommonDir(curWidget.id); diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index a122557..ca82fe4 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -7,6 +7,26 @@ try { } catch (e) { } + +/* + * Config.deviceType is used to differentiate main devices. + * Config.deviceType == 0 is a Samsung ES Series Device (2012) + * Config.deviceType != 0 is currently a Chrome Browser (Only GUI, no Video Playback) + * + * In order to adjust to other devices: + * Config.js: realization of persistent storage for variable "Config.serverUrl" (URL of server plugin) + * + * Handle KeyCodes: global variable tvKey holds an enum + * event.keyCode: is used to get the key pressed + * + * Display.putInnerHTML: Samsung specific way to hanle innerHTML + * Display.GetEpochTime: returns the current time (UTC) in seconds + * + * Audio: Get and Set Volume + * Player: All operations to get the video playing + * + */ + //var custom = window.deviceapis.customdevice || {}; @@ -293,6 +313,8 @@ Main.playItem = function (url) { Player.bufferState = 0; Player.isRecording = false; Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; + Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); + // Display.updateTotalTime(Player.totalTime); var digi = new Date((Data.getCurrentItem().childs[Main.selectedVideo].payload.start*1000)); Main.log (" Date(): StartTime= " + digi.getHours() + ":" + digi.getMinutes() + ":" + digi.getSeconds()); @@ -309,13 +331,16 @@ Main.playItem = function (url) { Player.isLive = false; Player.isRecording = false; Main.log(" playItem: now= " + now + " start_time= " + start_time + " dur= " + duration + " (Start + Dur - now)= " + ((start_time + duration) -now)); - if ((now - (start_time + duration)) < 0) { + + Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; + Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); + + if ((now - (start_time + duration)) < 0) { // still recording Main.log("*** Still Recording! ***"); Player.isRecording = true; Player.startTime = start_time; Player.duration = duration; - Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; document.getElementById("olRecProgressBar").style.display="block"; Display.updateRecBar(start_time, duration); diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index 716f36c..404cfda 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -304,8 +304,9 @@ Player.OnStreamInfoReady = function() { if ((Player.isLive == false) && (Player.isRecording == false)) { Player.totalTime = Player.plugin.GetDuration(); } - Player.curPlayTimeStr = Display.durationString(Player.totalTime / 1000.0); - +// Player.curPlayTimeStr = Display.durationString(Player.totalTime / 1000.0); + Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); + /* var height = Player.plugin.GetVideoHeight(); var width = Player.GetVideoWidth(); Display.showPopup("Resolution= " + height + " x " +width); -- cgit v1.2.3 From c542bd58a0821cfae77e07abea50c4c2b304a3d6 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 29 Dec 2012 00:02:32 +0100 Subject: use public method cRecording::Start() --- vdr-smarttvweb/httpresource.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 7755ff0..175c258 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -1574,7 +1574,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { *mResponseMessage += hdr; - /* + if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", "-", 0, 0) == ERROR) return ERROR; @@ -1582,7 +1582,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", "-", 0, 0) == ERROR) return ERROR; -*/ + //-------------------- cRecordings* recordings = &Recordings; @@ -1668,7 +1668,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { } if (writeXmlItem(cUrlEncode::doXmlSaveEncode(recording->Name()), link, "NA", desc, "-", - recording->start, rec_dur) == ERROR) + recording->Start(), rec_dur) == ERROR) return ERROR; } -- cgit v1.2.3 From 2370c4c3c3f5bd7e330f7da934773ad0289bdcc0 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 29 Dec 2012 00:39:18 +0100 Subject: another change of start to Start() --- vdr-smarttvweb/httpresource.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 175c258..1a5bd3b 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -1707,7 +1707,8 @@ int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { char f[400]; for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { hdr = ""; - strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&recording->start)); + time_t start_time = recording->Start(); + strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&start_time)); snprintf(f, sizeof(f), "%s - %d %s\r\n", buff, recording->HierarchyLevels(), recording->FileName(), recording->Name()); hdr += f; *mResponseMessage += hdr; -- cgit v1.2.3 From c822638483774188f436ff7019112d2502c5a141 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 29 Dec 2012 08:06:31 +0100 Subject: changed from off_t to uint64_t --- vdr-smarttvweb/httpresource.c | 43 +++++++++++++++++++++++-------------------- vdr-smarttvweb/httpresource.h | 2 +- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 1a5bd3b..653a2fa 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -69,12 +69,12 @@ using namespace std; struct sVdrFileEntry { - unsigned long long sSize; - unsigned long long sFirstOffset; + uint64_t sSize; + uint64_t sFirstOffset; int sIdx; sVdrFileEntry () {}; - sVdrFileEntry (off_t s, off_t t, int i) + sVdrFileEntry (uint64_t s, uint64_t t, int i) : sSize(s), sFirstOffset(t), sIdx(i) {}; }; @@ -88,7 +88,7 @@ sTimerEntry(string t, time_t s, int d) : name(t), startTime(s), duration(d) {}; // 8 Byte per entry struct tIndexTs { - uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!) + uint64_t offset:40; // up to 1TB per file (not using long long int here - must definitely be exactly 64 bit!) int reserved:7; // reserved for future use int independent:1; // marks frames that can be displayed by themselves (for trick modes) uint16_t number:16; // up to 64K files per recording @@ -142,6 +142,7 @@ int cHttpResource::checkStatus() { time_t now = time(NULL); switch (mConnState) { + case SERVING: case WAITING: case READHDR: case READPAYLOAD: @@ -194,7 +195,7 @@ int cHttpResource::handleRead() { int buflen = sizeof(buf); int line_count = 0; - bool is_req = true; + // bool is_req = true; string rem_hdr = ""; buflen = read(mFd, buf, sizeof(buf)); @@ -283,7 +284,7 @@ int cHttpResource::handleRead() { #ifndef DEBUG *(mLog->log())<< " parsing Request Line= " << line << endl; #endif - is_req = false; + // is_req = false; // Parse the request line if (parseHttpRequestLine(line) != OKAY) { return ERROR; @@ -302,7 +303,7 @@ int cHttpResource::processRequest() { *(mLog->log())<< DEBUGPREFIX << " processRequest for mPath= " << mPath << DEBUGHDR << endl; #endif struct stat statbuf; - int ret = OKAY; + // int ret = OKAY; if (mMethod.compare("POST")==0) { return handlePost(); @@ -320,28 +321,29 @@ int cHttpResource::processRequest() { << " generating /recordings.html" << DEBUGHDR << endl; #endif - ret = sendRecordingsHtml( &statbuf); + // ret = sendRecordingsHtml( &statbuf); + sendRecordingsHtml( &statbuf); return OKAY; } if (mPath.compare("/recordings.xml") == 0) { - ret = sendRecordingsXml( &statbuf); + sendRecordingsXml( &statbuf); return OKAY; } if (mPath.compare("/channels.xml") == 0) { - ret = sendChannelsXml( &statbuf); + sendChannelsXml( &statbuf); return OKAY; } if (mPath.compare("/epg.xml") == 0) { - ret = sendEpgXml( &statbuf); + sendEpgXml( &statbuf); return OKAY; } #endif if (mPath.compare("/media.xml") == 0) { - ret = sendMediaXml( &statbuf); + sendMediaXml( &statbuf); return OKAY; } @@ -359,21 +361,21 @@ int cHttpResource::processRequest() { if (mPath.size() > 8) { if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { - ret = sendManifest( &statbuf, false); + sendManifest( &statbuf, false); return OKAY; } } if (mPath.size() > 9) { if (mPath.compare(mPath.size() -9, 9, "-seg.m3u8") == 0) { - ret = sendManifest( &statbuf, true); + sendManifest( &statbuf, true); return OKAY; } } if (mPath.size() > 7) { if (mPath.compare(mPath.size() -7, 7, "-seg.ts") == 0) { - ret = sendMediaSegment( &statbuf); + sendMediaSegment( &statbuf); return OKAY; } } @@ -917,7 +919,8 @@ int cHttpResource::parseFiles(vector *entries, string prefix, string t.tm_isdst = -1; // char [20] rest; int start = -1; - sscanf(de->d_name, "%4d-%02d-%02d.%02d%.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); + sscanf(de->d_name, "%4d-%02d-%02d.%02d.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); + // sscanf(de->d_name, "%4d-%02d-%02d.%02d%.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); t.tm_year -= 1900; t.tm_mon--; t.tm_sec = 0; @@ -1574,7 +1577,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { *mResponseMessage += hdr; - + /* if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", "-", 0, 0) == ERROR) return ERROR; @@ -1582,7 +1585,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", "-", 0, 0) == ERROR) return ERROR; - +*/ //-------------------- cRecordings* recordings = &Recordings; @@ -1726,7 +1729,7 @@ int cHttpResource::sendVdrDir(struct stat *statbuf) { char pathbuf[4096]; char f[400]; int vdr_idx = 0; - off_t total_file_size = 0; + uint64_t total_file_size = 0; // int ret = OKAY; string vdr_dir = mPath; vector file_sizes; @@ -1880,7 +1883,7 @@ int cHttpResource::sendVdrDir(struct stat *statbuf) { } void cHttpResource::sendHeaders(int status, const char *title, const char *extra, const char *mime, - off_t length, time_t date) { + long long int length, time_t date) { time_t now; char timebuf[128]; diff --git a/vdr-smarttvweb/httpresource.h b/vdr-smarttvweb/httpresource.h index 22e44c4..51c59ef 100644 --- a/vdr-smarttvweb/httpresource.h +++ b/vdr-smarttvweb/httpresource.h @@ -161,7 +161,7 @@ class cHttpResource { int sendMediaSegment (struct stat *statbuf); void sendHeaders(int status, const char *title, const char *extra, const char *mime, - off_t length, time_t date); + long long int length, time_t date); int sendFile(struct stat *statbuf); -- cgit v1.2.3 From 1dddd64ad226c64efb8574047c61b7e124f60524 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 29 Dec 2012 14:16:45 +0100 Subject: Increase JavaScript robustness. --- smarttv-client/Javascript/Config.js | 7 +++++++ smarttv-client/Javascript/Main.js | 2 +- smarttv-client/Javascript/Player.js | 11 ++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index 518c6e6..c260be1 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -42,6 +42,13 @@ Config.init = function () { if (this.deviceType == 0) { // This is a Samsung Smart TV + if (this.serverUrl != "") { + // Hardcoded server URL. Done with config + Main.log ("Hardcoded server URL. Done with config"); + Config.fetchConfig(); + return; + } + try { this.cfgFileName = curWidget.id + "/config.dta"; } diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index ca82fe4..7103068 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -474,7 +474,7 @@ Main.getKeyCode = function(code) { if (Config.deviceType != 0) { // Not a Samsung - res = "Unknown Key (" + code + ")"; + res = "Unknown Key (KeyCode= " + code + ")"; return res; } switch(code) { diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index 404cfda..77f124c 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -37,7 +37,16 @@ Player.init = function() { this.state = this.STOPPED; this.plugin = document.getElementById("pluginPlayer"); - + +/* var pl_version = ""; + try { + pl_version = this.plugin.GetPlayerVersion(); + } + catch (e) { + Main.logToServer("Error while getting player version: " +e); + } + Main.logToServer("PlayerVersion= " + pl_version); +*/ if (!this.plugin) { Main.log("success vale this.plugin : " + success); -- cgit v1.2.3 From 39e526255696a07159d25a7bcc4ce01e90e6b21b Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 29 Dec 2012 14:21:07 +0100 Subject: Zipped Widget --- release/Widget-v08.zip | Bin 0 -> 51804 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 release/Widget-v08.zip diff --git a/release/Widget-v08.zip b/release/Widget-v08.zip new file mode 100755 index 0000000..1f8ac3d Binary files /dev/null and b/release/Widget-v08.zip differ -- cgit v1.2.3 From d7c09a665d2b5637e137f9dab3bf7ba372771845 Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 30 Dec 2012 22:13:32 +0100 Subject: Bug Fix. --- vdr-smarttvweb/httpresource.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 653a2fa..629659d 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -142,7 +142,6 @@ int cHttpResource::checkStatus() { time_t now = time(NULL); switch (mConnState) { - case SERVING: case WAITING: case READHDR: case READPAYLOAD: @@ -157,6 +156,8 @@ int cHttpResource::checkStatus() { case TOCLOSE: return ERROR; break; + case SERVING: + break; } // check for how much time the -- cgit v1.2.3 From 768d14d416c11e741f127c6b6a14b69fb66f5609 Mon Sep 17 00:00:00 2001 From: thlo Date: Mon, 31 Dec 2012 14:06:22 +0100 Subject: Several small improvements and bug fixes. --- smarttv-client/CSS/Main.css | 21 ++- smarttv-client/Images/description-bg.png | Bin 0 -> 94116 bytes smarttv-client/Images/leftHalf-bg.png | Bin 0 -> 138277 bytes smarttv-client/Images/main-bg.png | Bin 0 -> 2522 bytes smarttv-client/Images/rightHalf-bg.png | Bin 0 -> 124624 bytes smarttv-client/Images/selectScreen-bg.png | Bin 0 -> 306185 bytes smarttv-client/Images/selectView-bg.png | Bin 0 -> 74541 bytes smarttv-client/Images/videoList-bg.png | Bin 0 -> 106449 bytes smarttv-client/Javascript/Main.js | 232 ++++++++++++++++++++---------- smarttv-client/Javascript/Player.js | 81 ++++++++++- smarttv-client/config.xml | 2 +- smarttv-client/index.html | 11 +- 12 files changed, 253 insertions(+), 94 deletions(-) create mode 100644 smarttv-client/Images/description-bg.png create mode 100644 smarttv-client/Images/leftHalf-bg.png create mode 100644 smarttv-client/Images/main-bg.png create mode 100644 smarttv-client/Images/rightHalf-bg.png create mode 100644 smarttv-client/Images/selectScreen-bg.png create mode 100644 smarttv-client/Images/selectView-bg.png create mode 100644 smarttv-client/Images/videoList-bg.png diff --git a/smarttv-client/CSS/Main.css b/smarttv-client/CSS/Main.css index da91d1f..ae43a9e 100755 --- a/smarttv-client/CSS/Main.css +++ b/smarttv-client/CSS/Main.css @@ -41,10 +41,9 @@ body { position: absolute; left: 0px; top: 0px; width: 960px; height: 540px; + background: url("../Images/selectScreen-bg.png"); background: -webkit-radial-gradient(80% 80%, ellipse cover, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); } -/* background: -webkit-linear-gradient(top, rgba(64,150,238,1) 0%,rgba(64,150,238,1) 100%);*/ - #selectView { position: absolute; @@ -57,6 +56,8 @@ body { padding-top:20px; padding-left:20px; padding-right:20px; + background: url("../Images/selectView-bg.png"); + background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); } @@ -66,6 +67,7 @@ body { left: 0px; top: 0px; width: 960px; height: 540px; display: none; + background: url("../Images/selectScreen-bg.png"); background: -webkit-radial-gradient(80% 80%, ellipse cover, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); } @@ -80,6 +82,8 @@ body { padding-top:20px; padding-left:20px; padding-right:20px; + background: url("../Images/selectView-bg.png"); + background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); } @@ -124,6 +128,8 @@ body { position: absolute; left: 0px; top: 0px; width: 960px; height: 540px; + background: rgba(0, 0, 139, 0.5); + background: url("../Images/main-bg.png"); background: -webkit-linear-gradient(top, rgba(64,150,238,1) 0%,rgba(64,150,238,1) 100%); } @@ -150,10 +156,11 @@ body { -webkit-border-radius: 7px; border-width:1px; + background: url("../Images/rightHalf-bg.png"); + background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); } -/* background: -webkit-linear-gradient(top, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); -*/ + #status { position:absolute; @@ -182,6 +189,8 @@ body { text-overflow : ellipsis; -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); + background: url("../Images/description-bg.png"); + background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); } @@ -190,6 +199,8 @@ body { position:absolute; left: 10px; top:40px; width:50%; height:90%; + background: url("../Images/leftHalf-bg.png"); + background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); border-style:solid; border-width:1px; @@ -212,6 +223,8 @@ body { -webkit-border-radius: 7px; -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); + background: url("../Images/videoList-bg.png"); + background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); } diff --git a/smarttv-client/Images/description-bg.png b/smarttv-client/Images/description-bg.png new file mode 100644 index 0000000..f1021d4 Binary files /dev/null and b/smarttv-client/Images/description-bg.png differ diff --git a/smarttv-client/Images/leftHalf-bg.png b/smarttv-client/Images/leftHalf-bg.png new file mode 100644 index 0000000..0be1542 Binary files /dev/null and b/smarttv-client/Images/leftHalf-bg.png differ diff --git a/smarttv-client/Images/main-bg.png b/smarttv-client/Images/main-bg.png new file mode 100644 index 0000000..d9743ce Binary files /dev/null and b/smarttv-client/Images/main-bg.png differ diff --git a/smarttv-client/Images/rightHalf-bg.png b/smarttv-client/Images/rightHalf-bg.png new file mode 100644 index 0000000..824f3a8 Binary files /dev/null and b/smarttv-client/Images/rightHalf-bg.png differ diff --git a/smarttv-client/Images/selectScreen-bg.png b/smarttv-client/Images/selectScreen-bg.png new file mode 100644 index 0000000..6d595cc Binary files /dev/null and b/smarttv-client/Images/selectScreen-bg.png differ diff --git a/smarttv-client/Images/selectView-bg.png b/smarttv-client/Images/selectView-bg.png new file mode 100644 index 0000000..084f7d7 Binary files /dev/null and b/smarttv-client/Images/selectView-bg.png differ diff --git a/smarttv-client/Images/videoList-bg.png b/smarttv-client/Images/videoList-bg.png new file mode 100644 index 0000000..d6b7900 Binary files /dev/null and b/smarttv-client/Images/videoList-bg.png differ diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index 7103068..7d71e4d 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -1,9 +1,11 @@ var widgetAPI = null; var tvKey = null; +var pluginObj = null; try { widgetAPI = new Common.API.Widget(); tvKey = new Common.API.TVKeyValue(); + pluginObj = new Common.API.Plugin(); } catch (e) { } @@ -64,9 +66,12 @@ var Main = { }; Main.onLoad = function() { + window.onShow = showHandler; + Network.init(); try { - widgetAPI.sendReadyEvent(); + widgetAPI.sendReadyEvent(); + } catch (e) { Config.deviceType = 1; @@ -77,7 +82,7 @@ Main.onLoad = function() { } Display.init(); - + this.defKeyHndl = new cDefaulKeyHndl; this.playStateKeyHndl = new cPlayStateKeyHndl(this.defKeyHndl); this.livePlayStateKeyHndl = new cLivePlayStateKeyHndl(this.defKeyHndl); @@ -89,11 +94,23 @@ Main.onLoad = function() { Config.init(); }; +showHandler = function() { + NNaviPlugin = document.getElementById("pluginObjectNNavi"); + NNaviPlugin.SetBannerState(2); + + pluginObj.unregistKey(tvKey.KEY_VOL_UP); + pluginObj.unregistKey(tvKey.KEY_VOL_DOWN); + pluginObj.unregistKey(tvKey.KEY_MUTE); + pluginObj.unregistKey(tvKey.KEY_PANEL_VOL_UP); + pluginObj.unregistKey(tvKey.KEY_PANEL_VOL_DOWN); +}; + + // Called by Config, when done // TODO: Send sendReadyEvent early and show a splash screen during startup Main.init = function () { Main.log("Main.init()"); - if ( Player.init() && Audio.init() && Server.init() ) { + if ( Player.init() && Server.init() && Audio.init()) { Display.setVolume( Audio.getVolume() ); // Start retrieving data from server @@ -116,7 +133,16 @@ Main.init = function () { else { Main.log("Failed to initialise"); } - + /* + * + * Fetch JS file + xhttp=new XMLHttpRequest(); + xhttp.open("GET","$MANAGER_WIDGET/Common/API/Plugin.js",false); + xhttp.send(""); + xmlDoc=xhttp.responseText; + Main.logToServer (xmlDoc); +*/ + }; Main.log = function (msg) { @@ -146,6 +172,7 @@ Main.onUnload = function() Player.deinit(); }; + Main.changeState = function (state) { Main.log("change state: new state= " + state); var old_state = this.state; @@ -698,6 +725,65 @@ Main.getKeyCode = function(code) { return res; }; +Main.tvKeys = { + KEY_UP :38, + KEY_DOWN :40, + KEY_LEFT :37, + KEY_RIGHT :39, + KEY_ENTER :13, + + KEY_STOP :27, // ESC +// KEY_MUTE :27, + + // Un-used keycodes + KEY_RETURN :88, + KEY_EXIT :45, + KEY_RED :108, + KEY_GREEN :20, + KEY_YELLOW :21, + KEY_BLUE :22, + KEY_RW :69, + KEY_PAUSE :74, + KEY_FF :72, + KEY_PLAY :71, + KEY_STOP :70, + KEY_1 :101, + KEY_2 :98, + KEY_3 :6, + KEY_4 :8, + KEY_5 :9, + KEY_6 :10, + KEY_7 :12, + KEY_8 :13, + KEY_9 :14, + KEY_0 :17, + + KEY_PANEL_CH_UP :104, + KEY_PANEL_CH_DOWN :106, + KEY_PANEL_VOL_UP :203, + KEY_PANEL_VOL_DOWN :204, + KEY_PANEL_ENTER :309, + KEY_PANEL_SOURCE :612, + KEY_PANEL_MENU :613, + KEY_PANEL_POWER :614, + + KEY_POWER :76, + KEY_VOL_UP :7, + KEY_VOL_DOWN :11, + KEY_CH_UP :68, + KEY_CH_DOWN :65, + KEY_MTS :655, + KEY_12 :1057, + KEY_AD :1039, + KEY_FF_ :1078, + KEY_REWIND_ :1080, + KEY_SLEEP :1097, + KEY_STEP :1023, + KEY_HOME :1118 + +}; + + //--------------------------------------------------- // PlayState Key Handler //--------------------------------------------------- @@ -768,20 +854,33 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { Player.jumpToVideo(90); break; - case tvKey.KEY_RIGHT: case tvKey.KEY_FF: - Main.log("FF"); + case tvKey.KEY_RIGHT: + Main.log("Right: Skip Forward"); Display.showProgress(); Player.skipForwardVideo(); break; - case tvKey.KEY_LEFT: case tvKey.KEY_RW: - Main.log("RW"); + case tvKey.KEY_LEFT: + Main.log("Left: Skip Backward"); Display.showProgress(); Player.skipBackwardVideo(); break; +/* Works only for progressive streams, not Adaptive HTTP */ +/* case tvKey.KEY_FF: + Main.log("FF"); + Display.showProgress(); + Player.fastForwardVideo(); + + break; + case tvKey.KEY_RW: + Main.log("RW"); + Display.showProgress(); + Player.RewindVideo(); + break; +*/ case tvKey.KEY_ENTER: case tvKey.KEY_PLAY: case tvKey.KEY_PANEL_ENTER: @@ -789,6 +888,7 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { if(Player.getState() == Player.PAUSED) { Player.resumeVideo(); } + Player.ResetTrickPlay(); Display.showProgress(); break; case tvKey.KEY_RETURN: @@ -797,13 +897,15 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { Main.log("STOP"); // Player.setWindow(); Player.stopVideo(); + widgetAPI.blockNavigation(event); + break; case tvKey.KEY_PAUSE: Main.log("PAUSE"); Player.pauseVideo(); break; - case tvKey.KEY_UP: +/* case tvKey.KEY_UP: case tvKey.KEY_PANEL_VOL_UP: case tvKey.KEY_VOL_UP: Main.log("VOL_UP"); @@ -820,7 +922,7 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { if(Main.mute == 0) Audio.setRelativeVolume(1); break; - +*/ default: Main.log("Calling Default Key Hanlder"); this.defaultKeyHandler.handleDefKeyDown(keyCode); @@ -828,64 +930,6 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { } }; -Main.tvKeys = { - KEY_UP :38, - KEY_DOWN :40, - KEY_LEFT :37, - KEY_RIGHT :39, - KEY_ENTER :13, - - KEY_STOP :27, // ESC -// KEY_MUTE :27, - - // Un-used keycodes - KEY_RETURN :88, - KEY_EXIT :45, - KEY_RED :108, - KEY_GREEN :20, - KEY_YELLOW :21, - KEY_BLUE :22, - KEY_RW :69, - KEY_PAUSE :74, - KEY_FF :72, - KEY_PLAY :71, - KEY_STOP :70, - KEY_1 :101, - KEY_2 :98, - KEY_3 :6, - KEY_4 :8, - KEY_5 :9, - KEY_6 :10, - KEY_7 :12, - KEY_8 :13, - KEY_9 :14, - KEY_0 :17, - - KEY_PANEL_CH_UP :104, - KEY_PANEL_CH_DOWN :106, - KEY_PANEL_VOL_UP :203, - KEY_PANEL_VOL_DOWN :204, - KEY_PANEL_ENTER :309, - KEY_PANEL_SOURCE :612, - KEY_PANEL_MENU :613, - KEY_PANEL_POWER :614, - - KEY_POWER :76, - KEY_VOL_UP :7, - KEY_VOL_DOWN :11, - KEY_CH_UP :68, - KEY_CH_DOWN :65, - KEY_MTS :655, - KEY_12 :1057, - KEY_AD :1039, - KEY_FF_ :1078, - KEY_REWIND_ :1080, - KEY_SLEEP :1097, - KEY_STEP :1023, - KEY_HOME :1118 - -}; - //--------------------------------------------------- // Live Play State Key Handler @@ -945,12 +989,14 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { Player.stopVideo(); Display.setVideoList(Main.selectedVideo); Display.show(); + widgetAPI.blockNavigation(event); + break; case tvKey.KEY_PAUSE: Main.log("PAUSE"); break; - case tvKey.KEY_UP: +/* case tvKey.KEY_UP: case tvKey.KEY_PANEL_VOL_UP: case tvKey.KEY_VOL_UP: Main.log("VOL_UP"); @@ -967,7 +1013,7 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function () { if(Main.mute == 0) Audio.setRelativeVolume(1); break; - +*/ default: this.defaultKeyHandler.handleDefKeyDown(keyCode); break; @@ -1020,7 +1066,7 @@ cMenuKeyHndl.prototype.handleKeyDown = function () { } break; - case tvKey.KEY_EXIT: +// case tvKey.KEY_EXIT: case tvKey.KEY_RETURN: case tvKey.KEY_PANEL_RETURN: if (Data.isRootFolder() == true) { @@ -1032,9 +1078,9 @@ cMenuKeyHndl.prototype.handleKeyDown = function () { Main.log("folderUp selectedVideo= " + Main.selectedVideo); Display.setVideoList(Main.selectedVideo); } - break; - + widgetAPI.blockNavigation(event); + break; case tvKey.KEY_DOWN: Main.log("DOWN"); Main.selectNextVideo(Main.DOWN); @@ -1069,7 +1115,29 @@ cSelectMenuKeyHndl.prototype.handleKeyDown = function () { Main.log(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); switch(keyCode) { - + case tvKey.KEY_1: + Main.log("KEY_1 pressed"); + this.select = 1; + Main.changeState (this.select); + break; + case tvKey.KEY_2: + Main.log("KEY_2 pressed"); + this.select = 2; + Main.changeState (this.select); + + break; + case tvKey.KEY_3: + Main.log("KEY_3 pressed"); + this.select = 3; + Main.changeState (this.select); + + break; + case tvKey.KEY_4: + Main.log("KEY_4 pressed"); + this.select = 4; + Main.changeState (this.select); + break; + case tvKey.KEY_ENTER: case tvKey.KEY_PLAY: case tvKey.KEY_PANEL_ENTER: @@ -1116,12 +1184,19 @@ cDefaulKeyHndl.prototype.handleDefKeyDown = function (keyCode) { switch(keyCode) { case tvKey.KEY_EXIT: - Main.log(this.handlerName +"Exit"); - Player.stopVideo(); - widgetAPI.sendReturnEvent(); + Main.log(this.handlerName +"Exit"); + if (Main.state != 0) { + Player.stopVideo(); + Main.changeState(0); + widgetAPI.blockNavigation(event); + } + else { + widgetAPI.sendReturnEvent(); + + } break; - case tvKey.KEY_VOL_UP: +/* case tvKey.KEY_VOL_UP: Main.log(this.handlerName + "VOL_UP"); Display.showVolume(); if(Main.mute == 0) @@ -1138,6 +1213,7 @@ cDefaulKeyHndl.prototype.handleDefKeyDown = function (keyCode) { Main.log(this.handlerName + "MUTE"); Main.muteMode(); break; +*/ default: Main.log(this.handlerName + "Unhandled key"); break; diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index 77f124c..ffdbf03 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -24,6 +24,9 @@ var Player = bufferState : 0, // buffer state in % + trickPlaySpeed : 1, // Multiple of 2 only. + trickPlayDirection : 1, + STOPPED : 0, PLAYING : 1, PAUSED : 2, @@ -153,16 +156,17 @@ Player.playVideo = function() { Display.showProgress(); this.state = this.PLAYING; -// if (this.plugin.InitPlayer(this.url) == false) -// Display.showPopup("InitPlayer returns false"); + if (this.plugin.InitPlayer(this.url) == false) + Display.showPopup("InitPlayer returns false"); Player.setBuffer(15000000.0); - + Player.ResetTrickPlay(); + Main.log ("StartPlayback for " + this.url); -// if (this.plugin.StartPlayback() == false) -// Display.showPopup("StartPlayback returns false"); + if (this.plugin.StartPlayback() == false) + Display.showPopup("StartPlayback returns false"); - this.plugin.Play( this.url ); +// this.plugin.Play( this.url ); Audio.plugin.SetSystemMute(false); } }; @@ -243,6 +247,71 @@ Player.skipBackwardVideo = function() { Display.showPopup("Jump Backward ret= " + ((res == true) ? "True" : "False")); }; +Player.fastForwardVideo = function() { + if (this.trickPlayDirection == 1) + this.trickPlaySpeed = this.trickPlaySpeed * 2; + else { + this.trickPlaySpeed = this.trickPlaySpeed / 2; + + if (this.trickPlaySpeed < 1) { + this.trickPlaySpeed = 1; + this.trickPlayDirection = -1; + } + + } + + Main.log("FastForward: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); + Main.logToServer("FastForward: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); + if (this.plugin.SetPlaybackSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { + Display.showPopup("trick play returns false. Reset Trick-Play" ); + this.trickPlaySpeed = 1; + this.trickPlayDirection = 1; + } + +}; + +Player.RewindVideo = function() { + if (this.trickPlayDirection == 1) { + this.trickPlaySpeed = this.trickPlaySpeed / 2; + if (this.trickPlaySpeed < 1) { + this.trickPlaySpeed = 1; + this.trickPlayDirection = 1; + } + + } + else + this.trickPlaySpeed = this.trickPlaySpeed * 2; + + if (this.plugin.SetPlaybackSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { + Display.showPopup("trick play returns false. Reset Trick-Play" ); + this.trickPlaySpeed = 1; + this.trickPlayDirection = 1; + } + + Main.log("Rewind: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); + Main.logToServer("Rewind: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); + if (this.plugin.SetPlaybackSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { + Display.showPopup("trick play returns false. Reset Trick-Play" ); + this.trickPlaySpeed = 1; + this.trickPlayDirection = 1; + } + +}; + +Player.ResetTrickPlay = function() { + if (this.trickPlaySpeed != 1) { + this.trickPlaySpeed = 1; + this.trickPlayDirection = 1; + Main.log("Reset Trickplay " ); + if (this.plugin.SetPlaybackSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { + Display.showPopup("trick play returns false. Reset Trick-Play" ); + this.trickPlaySpeed = 1; + this.trickPlayDirection = 1; + } + + } +}; + Player.getState = function() { return this.state; }; diff --git a/smarttv-client/config.xml b/smarttv-client/config.xml index e586334..3a5bc44 100755 --- a/smarttv-client/config.xml +++ b/smarttv-client/config.xml @@ -9,7 +9,7 @@ Images/icon/SmartTvWeb_115.png Images/icon/SmartTvWeb_85.png Images/icon/SmartTvWeb_95.png - 0.8 + 0.81 y y diff --git a/smarttv-client/index.html b/smarttv-client/index.html index 757468c..b4fea24 100755 --- a/smarttv-client/index.html +++ b/smarttv-client/index.html @@ -7,9 +7,11 @@ + + - + @@ -27,14 +29,14 @@ - + + - - + @@ -57,7 +59,6 @@
2: Recordings
3: Media
4: Options
-
5: DLNA
-- cgit v1.2.3 From 06ce7556e61e65c52cd222c8c401bcbbda5ceb92 Mon Sep 17 00:00:00 2001 From: thlo Date: Mon, 31 Dec 2012 14:09:43 +0100 Subject: zipped Widget-0.81 --- release/Widget-v081.zip | Bin 0 -> 898634 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 release/Widget-v081.zip diff --git a/release/Widget-v081.zip b/release/Widget-v081.zip new file mode 100755 index 0000000..b5b3823 Binary files /dev/null and b/release/Widget-v081.zip differ -- cgit v1.2.3 From 6875170a503df081979d3b786b2b186334b31aab Mon Sep 17 00:00:00 2001 From: thlo Date: Tue, 1 Jan 2013 22:07:29 +0100 Subject: Native Volume OSD also visible on 2011 Models. Seeking duration (Jump Forward and Backward) now adjustable during playback. Minor Bug Fixes. --- smarttv-client/CSS/Main.css | 12 ++++---- smarttv-client/Javascript/Config.js | 1 + smarttv-client/Javascript/Display.js | 53 ++++++++++++++---------------------- smarttv-client/Javascript/Main.js | 22 +++++++++++++-- smarttv-client/Javascript/Options.js | 23 +++++++++++++--- smarttv-client/Javascript/Player.js | 49 ++++++++++++++++++++++++++++----- smarttv-client/config.xml | 2 +- smarttv-client/index.html | 2 ++ 8 files changed, 109 insertions(+), 55 deletions(-) diff --git a/smarttv-client/CSS/Main.css b/smarttv-client/CSS/Main.css index ae43a9e..e3ebaca 100755 --- a/smarttv-client/CSS/Main.css +++ b/smarttv-client/CSS/Main.css @@ -56,12 +56,12 @@ body { padding-top:20px; padding-left:20px; padding-right:20px; - background: url("../Images/selectView-bg.png"); - background-size: 100%; + background: rgba(6,109,171,1) 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); } - +/* background-size: 100%; +*/ #optionsScreen { position: absolute; left: 0px; top: 0px; @@ -82,8 +82,7 @@ body { padding-top:20px; padding-left:20px; padding-right:20px; - background: url("../Images/selectView-bg.png"); - background-size: 100%; + background: rgba(6,109,171,1) 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); } @@ -157,7 +156,6 @@ body { border-width:1px; background: url("../Images/rightHalf-bg.png"); - background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); } @@ -189,6 +187,7 @@ body { text-overflow : ellipsis; -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); + background: url("../Images/description-bg.png"); background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); @@ -200,7 +199,6 @@ body { left: 10px; top:40px; width:50%; height:90%; background: url("../Images/leftHalf-bg.png"); - background-size: 100%; background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); border-style:solid; border-width:1px; diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index c260be1..8718fb5 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -243,6 +243,7 @@ Config.processConfig = function () { res = Config.getXmlValue("liveChannels"); if (res != 0) noLiveChannels = res; + Player.skipDuration = Config.skipDuration; Main.log("**** Config ****"); Main.log("serverUrl= " + Config.serverUrl); Main.log("format= " + Config.format); diff --git a/smarttv-client/Javascript/Display.js b/smarttv-client/Javascript/Display.js index 5c68b53..71b4c71 100755 --- a/smarttv-client/Javascript/Display.js +++ b/smarttv-client/Javascript/Display.js @@ -142,6 +142,12 @@ Display.setOlTitle = function (title) { Display.putInnerHTML(elm, Display.olTitle); }; +Display.resetStartStop = function () { + Display.olStartStop = ""; + var elm = document.getElementById("olStartStop"); + Display.putInnerHTML(elm, Display.olStartStop); + +}; Display.setStartStop = function(start, stop) { this.olStartStop = ""; @@ -163,41 +169,14 @@ Display.setStartStop = function(start, stop) { Display.putInnerHTML(elm, Display.olStartStop); }; -/* -Display.getHumanTimeRepresentation = function(time) { - var totalTimeHour = 0; - var totalTimeMinute = 0; - var totalTimeSecond = 0; - - totalTimeHour = Math.floor(time/3600000); - totalTimeMinute = Math.floor((time%3600000)/60000); - totalTimeSecond = Math.floor((time%60000)/1000); - - var totalTimeStr = totalTimeHour + ":"; +Display.setSkipDuration = function(duration) { + this.olStartStop = ""; - if(totalTimeMinute == 0) - totalTimeStr += "00:"; - else if(totalTimeMinute <10) - totalTimeStr += "0" + totalTimeMinute + ":"; - else - totalTimeStr += totalTimeMinute + ":"; - - if(totalTimeSecond == 0) - totalTimeStr += "00"; - else if(totalTimeSecond <10) - totalTimeStr += "0" + totalTimeSecond; - else - totalTimeStr += totalTimeSecond; + this.olStartStop = duration; - return totalTimeStr; -}; -*/ -/* -// called by Player.OnStreamInfoReady -Display.updateTotalTime = function(time) { - Player.totalTimeStr = Display.getHumanTimeRepresentation(time); + var elm = document.getElementById("olStartStop"); + Display.putInnerHTML(elm, "Next Skip: " + Display.olStartStop+"sec"); }; -*/ // Player.OnCurrentPlayTime Display.updatePlayTime = function() { @@ -411,6 +390,7 @@ Display.setVideoList = function(selected, first) { Display.selectItem = function (item) { item.style.color = "black"; + item.style.background = "white"; item.style.background = "-webkit-linear-gradient(top, rgba(246,248,249,1) 0%,rgba(229,235,238,1) 50%,rgba(215,222,227,1) 51%,rgba(245,247,249,1) 100%)"; item.style.borderRadius= "3px"; item.style["-webkit-box-shadow"] = "2px 2px 1px 2px rgba(0,0,0, 0.5)"; @@ -589,7 +569,14 @@ Display.handlerHidePopup = function() { Display.putInnerHTML(document.getElementById("popup"), ""); }; -//--------------------------------------------------------- +/* --------------------------------------------------------- + * Progress Bar Handlers + * --------------------------------------------------------- + */ + +Display.isProgressOlShown = function () { + return this.progOlHandler.active; +}; Display.showProgress = function() { this.progOlHandler.show(); diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index 7d71e4d..da302d8 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -66,7 +66,12 @@ var Main = { }; Main.onLoad = function() { - window.onShow = showHandler; + if (typeof(window.onShow) == "function" ) { + window.onShow = showHandler; + } + if (typeof(window.onshow) == "function" ) { + window.onshow = showHandler; + } Network.init(); try { @@ -376,7 +381,7 @@ Main.playItem = function (url) { document.getElementById("olRecProgressBar").display="none"; } Display.setOlTitle(Data.getCurrentItem().childs[Main.selectedVideo].title); - Display.olStartStop = ""; + Display.resetStartStop(); break; }; @@ -889,6 +894,10 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { Player.resumeVideo(); } Player.ResetTrickPlay(); + if (Display.isProgressOlShown()) { + Player.adjustSkipDuration(0); // reset skip duration to default + Display.resetStartStop(); + } Display.showProgress(); break; case tvKey.KEY_RETURN: @@ -904,7 +913,14 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function () { Main.log("PAUSE"); Player.pauseVideo(); break; - + case tvKey.KEY_UP: + Player.adjustSkipDuration(1); + Display.showProgress(); + break; + case tvKey.KEY_DOWN: + Player.adjustSkipDuration(2); + Display.showProgress(); + break; /* case tvKey.KEY_UP: case tvKey.KEY_PANEL_VOL_UP: case tvKey.KEY_VOL_UP: diff --git a/smarttv-client/Javascript/Options.js b/smarttv-client/Javascript/Options.js index 31e9d50..d066c00 100755 --- a/smarttv-client/Javascript/Options.js +++ b/smarttv-client/Javascript/Options.js @@ -18,6 +18,7 @@ Options.onEnter = function () { Config.updateContext(document.getElementById("widgetServerAddr").value); + document.getElementById('widgetServerAddr').blur(); document.getElementById("optionsScreen").style.display="none"; @@ -27,7 +28,11 @@ Options.onEnter = function () { Main.changeState(0); Config.fetchConfig(); -// Main.enableKeys(); + if(_g_ime.pluginMouse_use_YN){ + this.imeBox._blur(); + }; + + // Main.enableKeys(); }; Options.onBlue = function () { @@ -40,6 +45,11 @@ Options.onImeCreated = function(obj) { // obj.setKeySetFunc('12key'); Main.logToServer ("Options.onImeCreated()"); + Main.logToServer ("[IME] ============================================ imeobj.IsSupportXT9:"+obj.IsSupportXT9); + var strKeySet = obj.getKeySet(); + Main.logToServer ("[IME] ============================================ strKeySet:"+strKeySet); + + obj.setKeyFunc(tvKey.KEY_RETURN, function(keyCode) { widgetAPI.sendReturnEvent(); return false; } ); obj.setKeyFunc(tvKey.KEY_EXIT, function(keyCode) { widgetAPI.sendExitEvent(); return false; } ); @@ -56,13 +66,18 @@ Options.onImeCreated = function(obj) { Options.imeBox.setOnCompleteFunc(Options.onComplete); + if(_g_ime.pluginMouse_use_YN){ + obj._focus(); + }; + document.getElementById('widgetServerAddr').focus(); + Main.logToServer ("Options.onReady()"); + Main.logToServer ("KeySet= " + Options.imeBox.getKeySet()); + Options.onReady (); }; Options.onReady = function () { - document.getElementById('widgetServerAddr').focus(); - Main.logToServer ("Options.onReady()"); - Main.log ("KeySet= " + this.imeBox.getKeySet()); + }; diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index ffdbf03..d54a761 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -16,6 +16,8 @@ var Player = curPlayTime : 0, // millis totalTime : -1, // millis + skipDuration : 30, + curPlayTimeStr : "00:00:00", // millis totalTimeStr : "", stopCallback : null, /* Callback function to be set by client */ @@ -55,7 +57,8 @@ Player.init = function() { Main.log("success vale this.plugin : " + success); success = false; } - + this.skipDuration = Config.skipDuration; // Use Config default also here + // var vermsg = this.plugin.GetPlayerVersion(); // Main.log ("player plugin version: " +vermsg); @@ -161,7 +164,8 @@ Player.playVideo = function() { Player.setBuffer(15000000.0); Player.ResetTrickPlay(); - + Player.skipDuration = Config.skipDuration; // reset + Main.log ("StartPlayback for " + this.url); if (this.plugin.StartPlayback() == false) Display.showPopup("StartPlayback returns false"); @@ -236,17 +240,48 @@ Player.jumpToVideo = function(percent) { }; Player.skipForwardVideo = function() { - var res = this.plugin.JumpForward(Config.skipDuration); - if (res == false) - Display.showPopup("Jump Forward ret= " + ((res == true) ? "True" : "False")); + var res = this.plugin.JumpForward(Player.skipDuration); + if (res == false) { + Display.showPopup("Jump Forward ret= " + ((res == true) ? "True" : "False")); + } + }; Player.skipBackwardVideo = function() { - var res = this.plugin.JumpBackward(Config.skipDuration); - if (res == false) + var res = this.plugin.JumpBackward(Player.skipDuration); + if (res == false) { Display.showPopup("Jump Backward ret= " + ((res == true) ? "True" : "False")); + + } + }; +Player.adjustSkipDuration = function (dir) { + if (Player.isLive == true) { + return; + } + switch (dir) { + case 0: + // Reset + Player.skipDuration = Config.skipDuration; + Display.setSkipDuration(Player.skipDuration ); + break; + case 1: + // Increase + Player.skipDuration += Config.skipDuration; + Display.setSkipDuration(Player.skipDuration ); + break; + case 2: + // Decrease + Player.skipDuration -= Config.skipDuration; + if (Player.skipDuration < Config.skipDuration) + Player.skipDuration = Config.skipDuration; + Display.setSkipDuration(Player.skipDuration ); + break; + }; +}; + + Player.fastForwardVideo = function() { if (this.trickPlayDirection == 1) this.trickPlaySpeed = this.trickPlaySpeed * 2; diff --git a/smarttv-client/config.xml b/smarttv-client/config.xml index 3a5bc44..5f9c48f 100755 --- a/smarttv-client/config.xml +++ b/smarttv-client/config.xml @@ -9,7 +9,7 @@ Images/icon/SmartTvWeb_115.png Images/icon/SmartTvWeb_85.png Images/icon/SmartTvWeb_95.png - 0.81 + 0.82 y y diff --git a/smarttv-client/index.html b/smarttv-client/index.html index b4fea24..3f4660a 100755 --- a/smarttv-client/index.html +++ b/smarttv-client/index.html @@ -11,6 +11,8 @@ + + -- cgit v1.2.3 From 61def43d29e6f143f583ce4db598b716596702e8 Mon Sep 17 00:00:00 2001 From: thlo Date: Tue, 1 Jan 2013 22:11:00 +0100 Subject: zipped Widget-0.82 --- release/Widget-v082.zip | Bin 0 -> 899000 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 release/Widget-v082.zip diff --git a/release/Widget-v082.zip b/release/Widget-v082.zip new file mode 100755 index 0000000..bf07988 Binary files /dev/null and b/release/Widget-v082.zip differ -- cgit v1.2.3 From a857c8fd7546029ddc666f835a0930f19a33be94 Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 2 Jan 2013 14:24:55 +0100 Subject: HTTP HEAD request support added --- vdr-smarttvweb/httpresource.c | 85 +++++++++++++++++++++++++++++++++++++++++-- vdr-smarttvweb/httpresource.h | 3 +- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 629659d..5f51197 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -1,7 +1,7 @@ /* * httpresource.h: VDR on Smart TV plugin * - * Copyright (C) 2012 Thorsten Lohmar + * Copyright (C) 2012 T. Lohmar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -310,7 +310,7 @@ int cHttpResource::processRequest() { return handlePost(); } - if (strcasecmp(mMethod.c_str(), "GET") != 0){ + if (!((strcasecmp(mMethod.c_str(), "GET") == 0) or (strcasecmp(mMethod.c_str(), "HEAD") == 0))) { sendError(501, "Not supported", NULL, "Method is not supported."); return OKAY; } @@ -323,27 +323,41 @@ int cHttpResource::processRequest() { << DEBUGHDR << endl; #endif // ret = sendRecordingsHtml( &statbuf); + if (handleHeadRequest() != 0) + return OKAY; + sendRecordingsHtml( &statbuf); return OKAY; } if (mPath.compare("/recordings.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + sendRecordingsXml( &statbuf); return OKAY; } if (mPath.compare("/channels.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + sendChannelsXml( &statbuf); return OKAY; } if (mPath.compare("/epg.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + sendEpgXml( &statbuf); return OKAY; } #endif if (mPath.compare("/media.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; sendMediaXml( &statbuf); return OKAY; } @@ -355,6 +369,9 @@ int cHttpResource::processRequest() { sendError(404, "Not Found", NULL, "File not found."); return OKAY; } + if (handleHeadRequest() != 0) + return OKAY; + mFileSize = statbuf.st_size; mContentType = SINGLEFILE; return sendFile(&statbuf); @@ -362,6 +379,8 @@ int cHttpResource::processRequest() { if (mPath.size() > 8) { if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { + if (handleHeadRequest() != 0) + return OKAY; sendManifest( &statbuf, false); return OKAY; } @@ -369,6 +388,8 @@ int cHttpResource::processRequest() { if (mPath.size() > 9) { if (mPath.compare(mPath.size() -9, 9, "-seg.m3u8") == 0) { + if (handleHeadRequest() != 0) + return OKAY; sendManifest( &statbuf, true); return OKAY; } @@ -376,6 +397,9 @@ int cHttpResource::processRequest() { if (mPath.size() > 7) { if (mPath.compare(mPath.size() -7, 7, "-seg.ts") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + sendMediaSegment( &statbuf); return OKAY; } @@ -397,6 +421,9 @@ int cHttpResource::processRequest() { if (mPath.size() >4) { if (mPath.compare(mPath.size() - 4, 4, ".rec") == 0) { // Handle any recording directory specifically + if (handleHeadRequest() != 0) + return OKAY; + mContentType = VDRDIR; return sendVdrDir( &statbuf); } @@ -411,6 +438,8 @@ int cHttpResource::processRequest() { sendError(404, "Not Found", NULL, "File not found."); return OKAY; } + if (handleHeadRequest() != 0) + return OKAY; sendDir( &statbuf); mContentType = MEMBLOCK; @@ -428,6 +457,8 @@ int cHttpResource::processRequest() { sendError(404, "Not Found", NULL, "File not found."); return OKAY; } + if (handleHeadRequest() != 0) + return OKAY; mFileSize = statbuf.st_size; mContentType = SINGLEFILE; return sendFile(&statbuf); @@ -630,6 +661,24 @@ int cHttpResource::parseResume(cResumeEntry &entry, string &id) { return ERROR; } +int cHttpResource::handleHeadRequest() { + if (mMethod.compare("HEAD") != 0) { + return 0; + } + *(mLog->log())<< DEBUGPREFIX + << " Handle HEAD Request for Url " << mPath << endl; + mConnState = SERVING; + mContentType = MEMBLOCK; + + // sent an empty response message with just the OK header + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + sendHeaders(200, "OK", NULL, NULL, -1, -1); + return 1; +} + int cHttpResource::handlePost() { mConnState = SERVING; mContentType = MEMBLOCK; @@ -1313,6 +1362,7 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) { int cHttpResource::sendEpgXml (struct stat *statbuf) { #ifndef STANDALONE + char f[400]; mResponseMessage = new string(); *mResponseMessage = ""; @@ -1400,6 +1450,7 @@ int cHttpResource::sendEpgXml (struct stat *statbuf) { int cHttpResource::sendChannelsXml (struct stat *statbuf) { #ifndef STANDALONE + char f[400]; mResponseMessage = new string(); *mResponseMessage = ""; @@ -1514,6 +1565,7 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { int cHttpResource::sendRecordingsXml(struct stat *statbuf) { #ifndef STANDALONE + mResponseMessage = new string(); *mResponseMessage = ""; mResponseMessagePos = 0; @@ -1689,6 +1741,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { #ifndef STANDALONE + mResponseMessage = new string(); mResponseMessagePos = 0; *mResponseMessage = ""; @@ -1696,6 +1749,24 @@ int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { mConnState = SERVING; + vector avps; + parseQueryLine(&avps); + string link_ext = ""; + string type = ""; + + if (getQueryAttributeValue(&avps, "type", type) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Type Parameter: " << type + << endl; + if (type == "hls") { + link_ext = "/manifest-seg.m3u8"; + } + if (type == "has") { + link_ext = "/manifest-seg.mpd"; + } + + } + sendHeaders(200, "OK", NULL, "text/html", -1, statbuf->st_mtime); string hdr = ""; @@ -1713,7 +1784,14 @@ int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { hdr = ""; time_t start_time = recording->Start(); strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&start_time)); - snprintf(f, sizeof(f), "%s - %d %s\r\n", buff, recording->HierarchyLevels(), recording->FileName(), recording->Name()); + + if (recording->IsPesRecording()) + snprintf(f, sizeof(f), "%s %s\r\n", buff, cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), + recording->Name()); + else + snprintf(f, sizeof(f), "%s %s\r\n", buff, cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), + link_ext.c_str(), recording->Name()); + hdr += f; *mResponseMessage += hdr; // start is time_t @@ -1724,6 +1802,7 @@ int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { int cHttpResource::sendVdrDir(struct stat *statbuf) { + #ifndef DEBUG *(mLog->log())<< DEBUGPREFIX << " *** sendVdrDir mPath= " << mPath << endl; #endif diff --git a/vdr-smarttvweb/httpresource.h b/vdr-smarttvweb/httpresource.h index 51c59ef..31148e2 100644 --- a/vdr-smarttvweb/httpresource.h +++ b/vdr-smarttvweb/httpresource.h @@ -1,7 +1,7 @@ /* * httpresource.h: VDR on Smart TV plugin * - * Copyright (C) 2012 Thorsten Lohmar + * Copyright (C) 2012 T. Lohmar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -138,6 +138,7 @@ class cHttpResource { int fillDataBlk(); int handlePost(); + int handleHeadRequest(); int processRequest(); int processHttpHeaderNew(); -- cgit v1.2.3 From a84fb47821356729f6bbcf5813aec5f7c6faafed Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 5 Jan 2013 16:37:48 +0100 Subject: New Web Front End. Various bug fixes. --- README.txt | 9 ++ vdr-smarttvweb/httpresource.c | 214 ++++++++++++++-------------------- vdr-smarttvweb/httpresource.h | 15 +-- vdr-smarttvweb/smarttvfactory.c | 19 +++- vdr-smarttvweb/smarttvweb.c | 4 +- vdr-smarttvweb/smarttvweb.conf | 4 +- vdr-smarttvweb/stvw_cfg.c | 3 +- vdr-smarttvweb/web/Data.js | 247 ++++++++++++++++++++++++++++++++++++++++ vdr-smarttvweb/web/Server.js | 120 +++++++++++++++++++ vdr-smarttvweb/web/favicon.ico | Bin 0 -> 1502 bytes vdr-smarttvweb/web/index.html | 100 ++++++++++++++++ 11 files changed, 584 insertions(+), 151 deletions(-) create mode 100644 README.txt create mode 100755 vdr-smarttvweb/web/Data.js create mode 100755 vdr-smarttvweb/web/Server.js create mode 100755 vdr-smarttvweb/web/favicon.ico create mode 100755 vdr-smarttvweb/web/index.html diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..25cc16f --- /dev/null +++ b/README.txt @@ -0,0 +1,9 @@ +This VDR Project contains a server plugin and a Smart TV Widget. + +Folders: + +vdr-smarttvweb: Contains the vdr server plugin, incl. the web front end +smarttv-client: Contains the source code of the Smart TV widget +release: Contains Smart TV Widget files ready for deplyoment. + +Check http://projects.vdr-developer.org/projects/plg-smarttvweb/wiki for a description, installation instructions and configuration instructions. diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 5f51197..1f4805a 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -304,7 +304,7 @@ int cHttpResource::processRequest() { *(mLog->log())<< DEBUGPREFIX << " processRequest for mPath= " << mPath << DEBUGHDR << endl; #endif struct stat statbuf; - // int ret = OKAY; + bool ok_to_serve = false; if (mMethod.compare("POST")==0) { return handlePost(); @@ -316,20 +316,6 @@ int cHttpResource::processRequest() { } #ifndef STANDALONE - if (mPath.compare("/recordings.html") == 0) { -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " generating /recordings.html" - << DEBUGHDR << endl; -#endif - // ret = sendRecordingsHtml( &statbuf); - if (handleHeadRequest() != 0) - return OKAY; - - sendRecordingsHtml( &statbuf); - return OKAY; - } - if (mPath.compare("/recordings.xml") == 0) { if (handleHeadRequest() != 0) return OKAY; @@ -377,6 +363,22 @@ int cHttpResource::processRequest() { return sendFile(&statbuf); } + if (mPath.compare("/favicon.ico") == 0) { + mPath = mFactory->getConfigDir() + "/web/favicon.ico"; + + if (stat(mPath.c_str(), &statbuf) < 0) { + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + if (handleHeadRequest() != 0) + return OKAY; + + mFileSize = statbuf.st_size; + mContentType = SINGLEFILE; + return sendFile(&statbuf); + } + + if (mPath.size() > 8) { if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { if (handleHeadRequest() != 0) @@ -405,9 +407,20 @@ int cHttpResource::processRequest() { } } + if (mPath.find("/web/") == 0) { + mPath = mFactory->getConfigDir() + mPath; + *(mLog->log())<< DEBUGPREFIX + << " Found web request. serving " << mPath << endl; + ok_to_serve = true; + } + + if (stat(mPath.c_str(), &statbuf) < 0) { // checking, whether the file or directory exists + *(mLog->log())<< DEBUGPREFIX + << " File Not found " << mPath << endl; sendError(404, "Not Found", NULL, "File not found."); + return OKAY; } @@ -429,12 +442,8 @@ int cHttpResource::processRequest() { } } - if (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) != 0) { - // No directory access outside of MediaFolder - *(mLog->log())<< DEBUGPREFIX - << " Directory request is not for MediaFolde (" - << mFactory->getConfig()->getMediaFolder() << ")" - << endl; + if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { + // No directory access outside of MediaFolder (and also VDRCONG/web) sendError(404, "Not Found", NULL, "File not found."); return OKAY; } @@ -453,7 +462,7 @@ int cHttpResource::processRequest() { #endif // Check, if requested file is in Media Directory - if (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) != 0) { + if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { sendError(404, "Not Found", NULL, "File not found."); return OKAY; } @@ -529,8 +538,10 @@ int cHttpResource::fillDataBlk() { return ERROR; } if (mRemLength == 0) { +#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX << " mRemLength is zero " << "--> Done " << endl; +#endif fclose(mFile); mFile = NULL; return ERROR; @@ -541,8 +552,10 @@ int cHttpResource::fillDataBlk() { mRemLength -= mBlkLen; if (mRemLength == 0) { +#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX << " last Block read " - << "--> Almost Done " << endl; + << "--> Almost Done " << endl; +#endif return OKAY; } @@ -553,6 +566,7 @@ int cHttpResource::fillDataBlk() { snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); mPath = pathbuf; + if (openFile(pathbuf) != OKAY) { *(mLog->log())<< DEBUGPREFIX << " Failed to open file= " << pathbuf << " mRemLength= " << mRemLength<< endl; mFile = NULL; @@ -563,13 +577,13 @@ int cHttpResource::fillDataBlk() { else *(mLog->log()) << DEBUGPREFIX << " Still data to send mBlkLen= " << mBlkLen <<" --> continue " << endl; return OKAY; - } + } // Error: Open next file failed if (mBlkLen == 0) { to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); mBlkLen = fread(mBlkData, 1, to_read, mFile); + mRemLength -= mBlkLen; } - mRemLength -= mBlkLen; } break; case SINGLEFILE: @@ -933,9 +947,6 @@ int cHttpResource::getQueryAttributeValue(vector *avps, string attr, int cHttpResource::parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf) { char pathbuf[4096]; string link; - // char f[400]; - // int len; - DIR *dir; struct dirent *de; string dir_comp; @@ -963,6 +974,7 @@ int cHttpResource::parseFiles(vector *entries, string prefix, string if (S_ISDIR(statbuf->st_mode)) { if (strcmp(&(pathbuf[strlen(pathbuf)-4]), ".rec") == 0) { + // vdr folder time_t now = time(NULL); struct tm tm_r; struct tm t = *localtime_r(&now, &tm_r); @@ -984,6 +996,7 @@ int cHttpResource::parseFiles(vector *entries, string prefix, string entries->push_back(sFileEntry(dir_name, pathbuf, start)); } else { + // regular file parseFiles(entries, prefix + de->d_name + "~", dir_comp, de->d_name, statbuf); } } @@ -1004,12 +1017,11 @@ int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) { string mpd_name = mPath.substr(pos+1); float seg_dur = mFactory->getSegmentDuration() *1.0; - // float seg_dur = SEGMENT_DURATION *1.0; - cRecordings* recordings = &Recordings; cRecording* rec = recordings->GetByName(mDir.c_str()); - float duration = rec->NumFrames() / rec->FramesPerSecond(); + double duration = rec->NumFrames() / rec->FramesPerSecond(); + time_t now = time(NULL); if (rec->Info() != NULL){ @@ -1032,6 +1044,7 @@ int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) { // duration is now either the actual duration of the asset or the target duration of the asset int end_seg = int (duration / seg_dur) +1; + // FIXME: Test Only /* if (rec->FramesPerSecond() > 40) end_seg = int (duration *2 / seg_dur) +1; @@ -1056,7 +1069,7 @@ int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) { return OKAY; } -void cHttpResource::writeM3U8(float duration, float seg_dur, int end_seg) { +void cHttpResource::writeM3U8(double duration, float seg_dur, int end_seg) { mResponseMessage = new string(); mResponseMessagePos = 0; *mResponseMessage = ""; @@ -1067,32 +1080,32 @@ void cHttpResource::writeM3U8(float duration, float seg_dur, int end_seg) { string hdr = ""; - sendHeaders(200, "OK", NULL, "application/x-mpegURL", -1, -1); - *mResponseMessage += "#EXTM3U\n"; - // snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", (seg_dur-1)); - snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", int(seg_dur)); + *mResponseMessage += "#EXTM3U\n"; + // snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", (seg_dur-1)); + snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", int(seg_dur)); + hdr = buf; + *mResponseMessage += hdr; + + *mResponseMessage += "#EXT-X-MEDIA-SEQUENCE:1\n"; + *mResponseMessage += "#EXT-X-KEY:METHOD=NONE\n"; + + for (int i = 1; i < end_seg; i++){ + // snprintf(buf, sizeof(buf), "#EXTINF:%.1f,\n", (seg_dur-0.5)); + snprintf(buf, sizeof(buf), "#EXTINF:%.2f,\n", seg_dur); hdr = buf; *mResponseMessage += hdr; - - *mResponseMessage += "#EXT-X-MEDIA-SEQUENCE:1\n"; - *mResponseMessage += "#EXT-X-KEY:METHOD=NONE\n"; - for (int i = 1; i < end_seg; i++){ - // snprintf(buf, sizeof(buf), "#EXTINF:%.1f,\n", (seg_dur-0.5)); - snprintf(buf, sizeof(buf), "#EXTINF:%.2f,\n", seg_dur); - hdr = buf; - *mResponseMessage += hdr; - - snprintf(buf, sizeof(buf), "%d-seg.ts\n", i); - hdr = buf; - *mResponseMessage += hdr; - } - *mResponseMessage += "#EXT-X-ENDLIST\n"; + snprintf(buf, sizeof(buf), "%d-seg.ts\n", i); + hdr = buf; + *mResponseMessage += hdr; + } + *mResponseMessage += "#EXT-X-ENDLIST\n"; + sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); } -void cHttpResource::writeMPD(float duration, float seg_dur, int end_seg) { +void cHttpResource::writeMPD(double duration, float seg_dur, int end_seg) { mResponseMessage = new string(); mResponseMessagePos = 0; *mResponseMessage = ""; @@ -1104,20 +1117,12 @@ void cHttpResource::writeMPD(float duration, float seg_dur, int end_seg) { string hdr = ""; - // sendHeaders(200, "OK", NULL, "application/xml", -1, -1); - *mResponseMessage += "\n"; snprintf(line, sizeof(line), "getHasMinBufferTime(), duration); *mResponseMessage = *mResponseMessage + line; - // *mResponseMessage += "\n"; *mResponseMessage += "\n"; @@ -1127,9 +1132,6 @@ void cHttpResource::writeMPD(float duration, float seg_dur, int end_seg) { snprintf(line, sizeof(line), "\n", mFactory->getHasBitrate()); *mResponseMessage = *mResponseMessage + line; - // *mResponseMessage += "\n"; - - // *mResponseMessage += "\n"; hdr = "size(), -1); + sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); } int cHttpResource::sendMediaSegment (struct stat *statbuf) { @@ -1160,7 +1162,6 @@ int cHttpResource::sendMediaSegment (struct stat *statbuf) { int seg_number; int seg_dur = mFactory->getSegmentDuration(); -// int seg_dur = SEGMENT_DURATION; int frames_per_seg = 0; sscanf(seg_name.c_str(), "%d-seg.ts", &seg_number); @@ -1262,9 +1263,11 @@ int cHttpResource::sendMediaSegment (struct stat *statbuf) { #endif } else { +#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX << " start_idx < end_idx " << endl; +#endif snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); if (stat(seg_fn, statbuf) < 0) { *(mLog->log()) << DEBUGPREFIX @@ -1274,6 +1277,8 @@ int cHttpResource::sendMediaSegment (struct stat *statbuf) { // issue: } rem_len = statbuf->st_size - start_offset; // remaining length of the first segment + + // loop over all idx files between start_idx and end_idx for (int idx = (start_idx+1); idx < end_idx; idx ++) { snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), idx); if (stat(seg_fn, statbuf) < 0) { @@ -1288,9 +1293,13 @@ int cHttpResource::sendMediaSegment (struct stat *statbuf) { } rem_len += end_offset; // mRemLength = rem_len; + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); + +#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX << " start_idx= " << start_idx << " != end_idx= "<< end_idx <<": mRemLength= " <log())<< DEBUGPREFIX << " Failed to open file= " << seg_fn << " mRemLength= " << mRemLength<< endl; @@ -1576,7 +1586,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { vector avps; parseQueryLine(&avps); string model = ""; - string link_ext = ""; + string link_ext = ""; string type = ""; string has_4_hd_str = ""; bool has_4_hd = true; @@ -1638,11 +1648,12 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", "-", 0, 0) == ERROR) return ERROR; + if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122:8000/hd2/mpeg/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS from own Server", + "-", 0, 0) == ERROR) + return ERROR; */ - //-------------------- cRecordings* recordings = &Recordings; - // char buff[20]; char f[600]; #ifndef DEBUG @@ -1739,68 +1750,6 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { return OKAY; } -int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { -#ifndef STANDALONE - - mResponseMessage = new string(); - mResponseMessagePos = 0; - *mResponseMessage = ""; - mContentType = MEMBLOCK; - - mConnState = SERVING; - - vector avps; - parseQueryLine(&avps); - string link_ext = ""; - string type = ""; - - if (getQueryAttributeValue(&avps, "type", type) == OKAY){ - *(mLog->log())<< DEBUGPREFIX - << " Found a Type Parameter: " << type - << endl; - if (type == "hls") { - link_ext = "/manifest-seg.m3u8"; - } - if (type == "has") { - link_ext = "/manifest-seg.mpd"; - } - - } - - sendHeaders(200, "OK", NULL, "text/html", -1, statbuf->st_mtime); - - string hdr = ""; - hdr += "Recordings\r\n"; - hdr += "\r\n"; - hdr += ""; - hdr += "

Recordings

\r\n
\n";
-  hdr += "
\r\n"; - - *mResponseMessage += hdr; - - char buff[20]; - char f[400]; - for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { - hdr = ""; - time_t start_time = recording->Start(); - strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&start_time)); - - if (recording->IsPesRecording()) - snprintf(f, sizeof(f), "%s %s\r\n", buff, cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), - recording->Name()); - else - snprintf(f, sizeof(f), "%s %s\r\n", buff, cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), - link_ext.c_str(), recording->Name()); - - hdr += f; - *mResponseMessage += hdr; - // start is time_t - } -#endif - return OKAY; -} - - int cHttpResource::sendVdrDir(struct stat *statbuf) { #ifndef DEBUG @@ -2074,17 +2023,20 @@ const char *cHttpResource::getMimeType(const char *name) { if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; if (strcmp(ext, ".gif") == 0) return "image/gif"; if (strcmp(ext, ".png") == 0) return "image/png"; + if (strcmp(ext, ".xml") == 0) return "application/xml"; if (strcmp(ext, ".css") == 0) return "text/css"; + if (strcmp(ext, ".js") == 0) return "text/javascript"; if (strcmp(ext, ".au") == 0) return "audio/basic"; if (strcmp(ext, ".wav") == 0) return "audio/wav"; if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; if (strcmp(ext, ".mp4") == 0) return "video/mp4"; if (strcmp(ext, ".vdr") == 0) return "video/mpeg"; if (strcmp(ext, ".ts") == 0) return "video/mpeg"; - if (strcmp(ext, ".mpd") == 0) return "application/dash+xml"; - if (strcmp(ext, ".xml") == 0) return "application/xml"; if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; + if (strcmp(ext, ".mpd") == 0) return "application/dash+xml"; + if (strcmp(ext, ".m3u8") == 0) return "application/x-mpegURL"; + return NULL; } diff --git a/vdr-smarttvweb/httpresource.h b/vdr-smarttvweb/httpresource.h index 31148e2..79459b2 100644 --- a/vdr-smarttvweb/httpresource.h +++ b/vdr-smarttvweb/httpresource.h @@ -110,7 +110,6 @@ class cHttpResource { int mBlkPos; int mBlkLen; - // string path; string mRequest; string mQuery; string mPath; @@ -131,9 +130,6 @@ class cHttpResource { bool mIsRecording; float mRecProgress; - // int writeToClient(const char *buf, size_t buflen); - // int sendDataChunk(); - void setNonBlocking(); int fillDataBlk(); @@ -143,20 +139,17 @@ class cHttpResource { int processHttpHeaderNew(); int readRequestPayload(); - // int processHttpHeader(); void sendError(int status, const char *title, const char *extra, const char *text); int sendDir(struct stat *statbuf); int sendVdrDir(struct stat *statbuf); - int sendRecordingsHtml (struct stat *statbuf); int sendRecordingsXml (struct stat *statbuf); int sendChannelsXml (struct stat *statbuf); int sendEpgXml (struct stat *statbuf); int sendMediaXml (struct stat *statbuf); - // int sendMPD (struct stat *statbuf); int sendManifest (struct stat *statbuf, bool is_hls = true); - void writeM3U8(float duration, float seg_dur, int end_seg); - void writeMPD(float duration, float seg_dur, int end_seg); + void writeM3U8(double duration, float seg_dur, int end_seg); + void writeMPD(double duration, float seg_dur, int end_seg); int sendMediaSegment (struct stat *statbuf); @@ -181,9 +174,5 @@ class cHttpResource { int getQueryAttributeValue(vector *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); - // string removeEtChar(string line); - // string hexDump(string input); - // string convertUrl(string input); - // string convertBack(string input); }; #endif diff --git a/vdr-smarttvweb/smarttvfactory.c b/vdr-smarttvweb/smarttvfactory.c index a57310a..b806021 100644 --- a/vdr-smarttvweb/smarttvfactory.c +++ b/vdr-smarttvweb/smarttvfactory.c @@ -1,7 +1,7 @@ /* * smarttvfactory.h: VDR on Smart TV plugin * - * Copyright (C) 2012 Thorsten Lohmar + * Copyright (C) 2012 T. Lohmar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -77,11 +77,28 @@ SmartTvServer::SmartTvServer(): mRequestCount(0), isInited(false), serverPort(PO SmartTvServer::~SmartTvServer() { + if (mConfig != NULL) delete mConfig; } void SmartTvServer::cleanUp() { + // close listening ports + for (uint idx= 0; idx < clientList.size(); idx++) { + if (clientList[idx] != NULL) { + close(idx); + delete clientList[idx]; + clientList[idx] = NULL; + } + } + + // close server port + close(mServerFd); + + // Leave thread + pthread_cancel(mThreadId); + pthread_join(mThreadId, NULL); + mLog.shutdown(); } diff --git a/vdr-smarttvweb/smarttvweb.c b/vdr-smarttvweb/smarttvweb.c index b7c907a..34d87ee 100644 --- a/vdr-smarttvweb/smarttvweb.c +++ b/vdr-smarttvweb/smarttvweb.c @@ -1,7 +1,7 @@ /* * smarttvweb.c: VDR on Smart TV plugin * - * Copyright (C) 2012 Thorsten Lohmar + * Copyright (C) 2012 T. Lohmar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,7 +41,7 @@ #include "smarttvfactory.h" -static const char *VERSION = "0.9.0"; +static const char *VERSION = "0.9.1"; static const char *DESCRIPTION = "SmartTV Web Server"; diff --git a/vdr-smarttvweb/smarttvweb.conf b/vdr-smarttvweb/smarttvweb.conf index 6afbbb9..382f5bb 100644 --- a/vdr-smarttvweb/smarttvweb.conf +++ b/vdr-smarttvweb/smarttvweb.conf @@ -8,8 +8,8 @@ SegmentDuration 10 HasMinBufferTime 30 -HasBitrate 6000 +HasBitrate 60000000 LiveChannels 30 -UseHasForHd false \ No newline at end of file +UseHasForHd false diff --git a/vdr-smarttvweb/stvw_cfg.c b/vdr-smarttvweb/stvw_cfg.c index 0c943b5..a7a5e58 100644 --- 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 Thorsten Lohmar + * Copyright (C) 2012 T. Lohmar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,7 +53,6 @@ cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NU } cSmartTvConfig::~cSmartTvConfig() { - fclose(mCfgFile); } void cSmartTvConfig::readConfig() { diff --git a/vdr-smarttvweb/web/Data.js b/vdr-smarttvweb/web/Data.js new file mode 100755 index 0000000..5e480d2 --- /dev/null +++ b/vdr-smarttvweb/web/Data.js @@ -0,0 +1,247 @@ +var Data = +{ + assets : new Item, + folderList : [], +}; + +Data.reset = function() { + this.assets = null; + this.assets = new Item; + + this.folderList = []; + + this.folderList.push({item : this.assets, id: 0}); +}; + +Data.completed= function(sort) { + if (sort == true) + this.assets.sortPayload(); + + this.folderList.push({item : this.assets, id: 0}); + console.log ("Data.completed()= " +this.folderList.length); + +}; + +Data.selectFolder = function (idx) { + this.folderList.push({item : this.getCurrentItem().childs[idx], id: idx}); +}; + +Data.isRootFolder = function() { + 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); +}; + +Data.dumpFolderStruct = function(){ + Main.log("---------- dumpFolderStruct ------------"); + this.assets.print(0); + Main.log("---------- dumpFolderStruct Done -------"); +}; + +Data.createDomTree = function () { + + return this.assets.createDomTree(0); +}; + +Data.createJQMDomTree = function () { + return this.assets.createJQMDomTree(0); +}; + +Data.getCurrentItem = function () { + return this.folderList[this.folderList.length-1].item; +}; + +Data.getVideoCount = function() +{ + return this.folderList[this.folderList.length-1].item.childs.length; +}; + +Data.getNumString =function(num, fmt) { + var res = ""; + + if (num < 10) { + for (var i = 1; i < fmt; i ++) { + res += "0"; + }; + } else if (num < 100) { + for (var i = 2; i < fmt; i ++) { + res += "0"; + }; + } + + res = res + num; + + return res; +}; + + +//----------------------------------------- +function Item() { + this.title = "root"; + this.isFolder = true; + this.childs = []; + this.payload = ""; // only set, if (isFolder == false) +} + +Item.prototype.isFolder = function() { + return this.isFolder; +}; + +Item.prototype.getTitle = function () { + return this.title; +}; + +Item.prototype.getPayload = function () { + if (this.isFolder == true) { + Main.log("WARNING: getting payload on a folder title=" +this.title); + } + return this.payload; +}; + +Item.prototype.getItem = function (title) { + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].title == title) { + return this.childs[i]; + } + } + return 0; +}; + +Item.prototype.addChild = function (key, pyld, level) { + if (key.length == 1) { + var folder = new Item; +// folder.title = pyld.startstr + " - " + key; + folder.title = key[0]; + folder.payload = pyld; + folder.isFolder = false; + this.childs.push(folder); +// this.titles.push({title: pyld.startstr + " - " + key , pyld : pyld}); + } + else { + if (level > 10) { + Main.log(" too many levels"); + return; + } + var t = key.shift(); + var found = false; + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].title == t) { + this.childs[i].addChild(key, pyld, level +1); + found = true; + break; + } + } + if (found == false) { + var folder = new Item; + folder.title = t; + folder.addChild(key, pyld, level+1); + this.childs.push(folder); + } + } +}; + +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.createDomTree = function(level) { + var prefix= ""; + for (var i = 0; i < level; i++) + prefix += "-"; +// var mydiv = $('
' +prefix+this.title+ '
'); + var mydiv = $('
-- cgit v1.2.3 From b90278ae3caf9f7d2424e3b220b8fbf3cbeb0625 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 5 Jan 2013 22:59:56 +0100 Subject: zipped Widget-0.83 --- release/Widget-v083.zip | Bin 0 -> 899119 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 release/Widget-v083.zip diff --git a/release/Widget-v083.zip b/release/Widget-v083.zip new file mode 100755 index 0000000..7917080 Binary files /dev/null and b/release/Widget-v083.zip differ -- cgit v1.2.3 From 21f592bf3c2f09f41cbc3c9c6a34ffe5d70df834 Mon Sep 17 00:00:00 2001 From: thlo Date: Mon, 7 Jan 2013 20:36:08 +0100 Subject: Various fixes. --- vdr-smarttvweb/httpresource.c | 57 +++++++++++++++++++++++++++++------------ vdr-smarttvweb/smarttvfactory.h | 7 +---- vdr-smarttvweb/stvw_cfg.h | 2 +- vdr-smarttvweb/web/Server.js | 16 +++++++----- vdr-smarttvweb/web/index.html | 4 +++ 5 files changed, 55 insertions(+), 31 deletions(-) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 1f4805a..44100aa 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -1,5 +1,5 @@ /* - * httpresource.h: VDR on Smart TV plugin + * httpresource.c: VDR on Smart TV plugin * * Copyright (C) 2012 T. Lohmar * @@ -749,17 +749,19 @@ void cHttpResource::sendError(int status, const char *title, const char *extra, mResponseMessagePos = 0; string hdr = ""; - sendHeaders(status, title, extra, "text/html", -1, -1); + // sendHeaders(status, title, extra, "text/html", -1, -1); + sendHeaders(status, title, extra, "text/plain", -1, -1); - snprintf(f, sizeof(f), "%d %s\r\n", status, title); + /* snprintf(f, sizeof(f), "%d %s\r\n", status, title); hdr += f; snprintf(f, sizeof(f), "

%d %s

\r\n", status, title); hdr += f; +*/ snprintf(f, sizeof(f), "%s\r\n", text); hdr += f; - snprintf(f, sizeof(f), "\r\n"); + /* snprintf(f, sizeof(f), "\r\n"); hdr += f; - +*/ strcpy(&(mBlkData[mBlkLen]), hdr.c_str()); mBlkLen += hdr.size(); @@ -962,6 +964,9 @@ int cHttpResource::parseFiles(vector *entries, string prefix, string #endif dir = opendir(dir_comp.c_str()); + if (stat(dir_comp.c_str(), statbuf) < 0) + return ERROR; + while ((de = readdir(dir)) != NULL) { if ((strcmp(de->d_name, ".") == 0) or (strcmp(de->d_name, "..") == 0)) { continue; @@ -1016,7 +1021,7 @@ int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) { mDir = mPath.substr(0, pos); string mpd_name = mPath.substr(pos+1); - float seg_dur = mFactory->getSegmentDuration() *1.0; + float seg_dur = mFactory->getConfig()->getSegmentDuration() *1.0; cRecordings* recordings = &Recordings; cRecording* rec = recordings->GetByName(mDir.c_str()); @@ -1120,7 +1125,7 @@ void cHttpResource::writeMPD(double duration, float seg_dur, int end_seg) { *mResponseMessage += "\n"; snprintf(line, sizeof(line), "getHasMinBufferTime(), duration); + mFactory->getConfig()->getHasMinBufferTime(), duration); *mResponseMessage = *mResponseMessage + line; *mResponseMessage += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\" "; @@ -1130,7 +1135,7 @@ void cHttpResource::writeMPD(double duration, float seg_dur, int end_seg) { *mResponseMessage += "\n"; *mResponseMessage += "\n"; - snprintf(line, sizeof(line), "\n", mFactory->getHasBitrate()); + snprintf(line, sizeof(line), "\n", mFactory->getConfig()->getHasBitrate()); *mResponseMessage = *mResponseMessage + line; hdr = "getConfig()->getMediaFolder(); mResponseMessage = new string(); mResponseMessagePos = 0; @@ -1340,7 +1345,12 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) { #endif vector entries; - parseFiles(&entries, "", media_folder, "", statbuf); + + //thlo + if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) { + sendError(404, "Not Found", NULL, "Media Folder likely not configured."); + return OKAY; + } sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); @@ -1510,7 +1520,7 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { *mResponseMessage += hdr; - int count = mFactory->getLiveChannels(); + int count = mFactory->getConfig()->getLiveChannels(); if (no_channels > 0) count = no_channels +1; @@ -1595,8 +1605,10 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { *(mLog->log())<< DEBUGPREFIX << " Found a Model Parameter: " << model << endl; - if (model == "samsung") + //thlo + /* if (model == "samsung") link_ext = "/manifest-seg.m3u8|COMPONENT=HLS"; +*/ } if (getQueryAttributeValue(&avps, "type", type) == OKAY){ @@ -1666,14 +1678,24 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { time_t now = time(NULL); vector act_rec; -#ifndef DEBUG + /*#ifndef DEBUG*/ *(mLog->log())<< DEBUGPREFIX << " checking avtive timer" << endl; -#endif + /*#endif*/ for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ ti->Matches(); + /* *(mLog->log()) << DEBUGPREFIX + << " Active Timer: " << ti->File() + << " EvStart= " << ti->Event()->StartTime() + << " EvDuration= " << ti->Event()->Duration() + << endl;*/ + /* *(mLog->log()) << DEBUGPREFIX + << " TiStart= " << ti->StartTime() + << " TiDuration= " << (ti->StopTime() - ti->StartTime()) + << endl; +*/ if (ti->HasFlags(tfRecording) ) { if (ti->Event() == NULL) { *(mLog->log()) << DEBUGPREFIX @@ -1687,6 +1709,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { << " Duration= " << ti->Event()->Duration() << endl; act_rec.push_back(sTimerEntry(ti->File(), ti->Event()->StartTime(), ti->Event()->Duration())); + //act_rec.push_back(sTimerEntry(ti->File(), ti->StartTime(), (ti->StopTime() - ti->StartTime()))); } } @@ -1717,10 +1740,10 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { for (uint x = 0; x < act_rec.size(); x++) { if (act_rec[x].name == name) { - *(mLog->log())<< DEBUGPREFIX + /* *(mLog->log())<< DEBUGPREFIX << " !!!!! Found active Recording !!! " << endl; - // link += "/hm-seg.m3u8"; +*/ rec_dur += (act_rec[x].startTime + act_rec[x].duration - now); diff --git a/vdr-smarttvweb/smarttvfactory.h b/vdr-smarttvweb/smarttvfactory.h index e2936bf..ff1af24 100644 --- a/vdr-smarttvweb/smarttvfactory.h +++ b/vdr-smarttvweb/smarttvfactory.h @@ -1,7 +1,7 @@ /* * smarttvfactory.h: VDR on Smart TV plugin * - * Copyright (C) 2012 Thorsten Lohmar + * Copyright (C) 2012 T. Lohmar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,11 +55,6 @@ class SmartTvServer { int isServing(); string getConfigDir() { return mConfigDir; }; - unsigned int getSegmentDuration() { return mSegmentDuration; }; - int getHasMinBufferTime() { return mHasMinBufferTime; }; - unsigned int getHasBitrate() { return mHasBitrate; }; - int getLiveChannels() { return mLiveChannels; }; - cSmartTvConfig* getConfig() { return mConfig; }; private: diff --git a/vdr-smarttvweb/stvw_cfg.h b/vdr-smarttvweb/stvw_cfg.h index 4d19953..3b440b1 100644 --- 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 Thorsten Lohmar + * Copyright (C) 2012 T. Lohmar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/vdr-smarttvweb/web/Server.js b/vdr-smarttvweb/web/Server.js index 9fb39c5..14385df 100755 --- a/vdr-smarttvweb/web/Server.js +++ b/vdr-smarttvweb/web/Server.js @@ -32,7 +32,7 @@ Server.fetchVideoList = function(url) { if (this.XHRObj) { this.XHRObj.onreadystatechange = function() { - var splashElement = document.getElementById("splashStatus"); + // var splashElement = document.getElementById("splashStatus"); if (Server.XHRObj.readyState == 4) { Server.createVideoList(); @@ -53,14 +53,14 @@ Server.fetchVideoList = function(url) { 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("ServerError"); + if (this.errorCallback != null) { + this.errorCallback(this.XHRObj.responseText); } } - else - { + else { var xmlResponse = this.XHRObj.responseXML; if (xmlResponse == null) { if (this.errorCallback != null) { @@ -79,7 +79,8 @@ Server.createVideoList = function() { 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++) { @@ -94,6 +95,7 @@ Server.createVideoList = function() { durVal = parseInt(items[index].getElementsByTagName("duration")[0].firstChild.data); } catch (e) { + this.errorCallback("XML Parsing Error: " + e); console.log("ERROR: "+e); } diff --git a/vdr-smarttvweb/web/index.html b/vdr-smarttvweb/web/index.html index 9e1117b..f134ece 100755 --- a/vdr-smarttvweb/web/index.html +++ b/vdr-smarttvweb/web/index.html @@ -21,6 +21,10 @@ $(document).ready(function(){ // end of dataReceivedCallBack }; + Server.errorCallback = function (msg) { + alert (msg); + buttonHandler('rec'); + }; $.mobile.loading('show'); Server.setSort(true); -- cgit v1.2.3 From 7034d92cbb1bf7b4c219b6ff4f744349211781dc Mon Sep 17 00:00:00 2001 From: thlo Date: Mon, 7 Jan 2013 20:41:02 +0100 Subject: Fixes (use liveChannels from Widget.conf, no error after start video, etc). --- smarttv-client/Javascript/Config.js | 6 ++++-- smarttv-client/Javascript/Main.js | 19 +++++++++++++------ smarttv-client/Javascript/Player.js | 8 ++++---- smarttv-client/Javascript/Server.js | 6 +++--- smarttv-client/config.xml | 2 +- smarttv-client/index.html | 12 ++++++------ 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index 8718fb5..1aa2f75 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -12,7 +12,7 @@ var Config = { pendingBuffer: 40, // in percent initialTimeOut: 3, // sec skipDuration : 30, // sec - noLiveChannels : 30, + liveChannels : 30, firstLaunch : false, deviceType : 0 // Used to differentiate between browsers and platforms @@ -241,7 +241,7 @@ Config.processConfig = function () { if (res != 0) this.initialTimeOut = 1.0 * res; res = Config.getXmlValue("liveChannels"); - if (res != 0) noLiveChannels = res; + if (res != 0) this.liveChannels = res; Player.skipDuration = Config.skipDuration; Main.log("**** Config ****"); @@ -253,6 +253,8 @@ Config.processConfig = function () { Main.log("pendingBuffer= " + Config.pendingBuffer); Main.log("skipDuration= " + Config.skipDuration); Main.log("initialTimeOut= " + Config.initialTimeOut); + Main.log("liveChannels= " + Config.liveChannels); + Main.log("**** /Config ****"); }; diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index 3294f02..42fa8f7 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -157,13 +157,11 @@ Main.log = function (msg) { }; Main.logToServer = function (msg) { -// if (Config.serverUrl == "" ) -// return; + if (Config.serverUrl == "" ) + return; var XHRObj = new XMLHttpRequest(); -// XHRObj.open("POST", Config.serverUrl + "/log", true); - XHRObj.open("POST", "http://192.168.1.122:8000/log", true); - + XHRObj.open("POST", Config.serverUrl + "/log", true); XHRObj.send("CLOG: " + msg); }; @@ -231,6 +229,11 @@ Main.recordingsSelected = function() { Display.show(); }; + Server.errorCallback = function (msg) { + Display.showPopup(msg); + Main.changeState(0); + }; + Player.isLive = false; Server.setSort(true); if (Config.format == "") { @@ -263,7 +266,7 @@ Main.liveSelected = function() { Player.isLive = true; Server.setSort(false); Server.errorCallback = Main.serverError; - Server.fetchVideoList(Config.serverUrl + "/channels.xml"); /* Request video information from server */ + Server.fetchVideoList(Config.serverUrl + "/channels.xml?channels="+Config.liveChannels); /* Request video information from server */ }; Main.mediaSelected = function() { @@ -271,6 +274,10 @@ Main.mediaSelected = function() { // Display.show(); }; + Server.errorCallback = function (msg) { + Display.showPopup(msg); + Main.changeState(0); + }; Player.isLive = false; Server.setSort(true); Server.fetchVideoList(Config.serverUrl + "/media.xml"); /* Request video information from server */ diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index 72c6d30..c8ab787 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -178,7 +178,7 @@ Player.playVideo = function() { this.plugin.Play( this.url ); Audio.plugin.SetSystemMute(false); - pluginObj.setOffScreenSaver(); + pluginObj.setOffScreenSaver(); this.pluginBD.DisplayVFD_Show(0100); // Play } }; @@ -192,7 +192,7 @@ Player.pauseVideo = function() { var res = this.plugin.Pause(); if (res == false) Display.showPopup("pause ret= " + ((res == true) ? "True" : "False")); - pluginAPI.setOnScreenSaver(); + pluginObj.setOnScreenSaver(); this.pluginBD.DisplayVFD_Show(0102); // Pause }; @@ -207,7 +207,7 @@ Player.stopVideo = function() { if (this.stopCallback) { this.stopCallback(); } - pluginAPI.setOnScreenSaver(); + pluginObj.setOnScreenSaver(); this.pluginBD.DisplayVFD_Show(0101); // Stop } else { @@ -223,7 +223,7 @@ Player.resumeVideo = function() { var res = this.plugin.Resume(); if (res == false) Display.showPopup("resume ret= " + ((res == true) ? "True" : "False")); - pluginObj.setOffScreenSaver(); + pluginObj.setOffScreenSaver(); this.pluginBD.DisplayVFD_Show(0100); // Play }; diff --git a/smarttv-client/Javascript/Server.js b/smarttv-client/Javascript/Server.js index ee23505..f24a789 100755 --- a/smarttv-client/Javascript/Server.js +++ b/smarttv-client/Javascript/Server.js @@ -67,10 +67,10 @@ Server.createVideoList = function() { if (this.XHRObj.status != 200) { Display.putInnerHTML(splashElement, "XML Server Error " + this.XHRObj.status); - Display.status("XML Server Error " + this.XHRObj.status); - Display.showPopup("XML Server Error " + this.XHRObj.status); +// Display.status("XML Server Error " + this.XHRObj.status); +// Display.showPopup("XML Server Error " + this.XHRObj.status); if (this.errorCallback != null) { - this.errorCallback("ServerError"); + this.errorCallback(this.XHRObj.responseText); } } else diff --git a/smarttv-client/config.xml b/smarttv-client/config.xml index 81588b6..821ccb7 100755 --- a/smarttv-client/config.xml +++ b/smarttv-client/config.xml @@ -9,7 +9,7 @@ Images/icon/SmartTvWeb_115.png Images/icon/SmartTvWeb_85.png Images/icon/SmartTvWeb_95.png - 0.83 + 0.84 y y diff --git a/smarttv-client/index.html b/smarttv-client/index.html index f9d969c..7c37790 100755 --- a/smarttv-client/index.html +++ b/smarttv-client/index.html @@ -4,15 +4,17 @@ Vdr on SmartTV + + - + - - + + @@ -46,10 +48,8 @@ - - - + -- cgit v1.2.3 From 3a12fd0c546292516099d8a8fc9c757e6bec3186 Mon Sep 17 00:00:00 2001 From: thlo Date: Mon, 7 Jan 2013 20:43:30 +0100 Subject: zipped Widget-v0.84 --- release/Widget-v084.zip | Bin 0 -> 900510 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 release/Widget-v084.zip diff --git a/release/Widget-v084.zip b/release/Widget-v084.zip new file mode 100755 index 0000000..58372dd Binary files /dev/null and b/release/Widget-v084.zip differ -- cgit v1.2.3 From e55c2188f7cf425876e69d62f529920366ddb20c Mon Sep 17 00:00:00 2001 From: thlo Date: Thu, 10 Jan 2013 22:25:38 +0100 Subject: obsolete files deleted. --- smarttv-client/Javascript/CVS/Entries | 13 - vdr-smarttvweb/httpresource-hmm.c | 1130 --------------------------------- vdr-smarttvweb/httpresource-hmm.h | 94 --- 3 files changed, 1237 deletions(-) delete mode 100755 smarttv-client/Javascript/CVS/Entries delete mode 100644 vdr-smarttvweb/httpresource-hmm.c delete mode 100644 vdr-smarttvweb/httpresource-hmm.h diff --git a/smarttv-client/Javascript/CVS/Entries b/smarttv-client/Javascript/CVS/Entries deleted file mode 100755 index 0ce6906..0000000 --- a/smarttv-client/Javascript/CVS/Entries +++ /dev/null @@ -1,13 +0,0 @@ -/Audio.js/1.2/Sun Oct 21 11:06:19 2012// -/Comm.js/-1.4/Wed Dec 12 19:36:09 2012// -/Config.js/1.2/Thu Dec 20 20:52:57 2012// -/Data.js/1.8/Thu Dec 13 18:51:43 2012// -/Display.js/1.33/Thu Dec 20 19:34:59 2012// -/HandlePages.js/-1.1/Sat Oct 6 06:18:38 2012// -/Main.js/1.38/Thu Dec 20 20:53:18 2012// -/Network.js/1.2/Tue Oct 23 18:04:28 2012// -/Options.js/1.2/Sun Dec 16 13:29:28 2012// -/Player.js/1.33/Thu Dec 20 20:42:11 2012// -/Server.js/1.13/Thu Dec 13 18:51:44 2012// -/debug.js/-1.2/Fri Oct 19 19:26:18 2012// -/dlna.js/-1.1/Wed Oct 17 13:56:23 2012// diff --git a/vdr-smarttvweb/httpresource-hmm.c b/vdr-smarttvweb/httpresource-hmm.c deleted file mode 100644 index 1c2117f..0000000 --- a/vdr-smarttvweb/httpresource-hmm.c +++ /dev/null @@ -1,1130 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "httpresource.h" - -#ifndef VOMPSTANDALONE -#include -#include -#endif - -#define SERVER "SmartTvWeb/0.1" -#define PROTOCOL "HTTP/1.1" -#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" - -#define MAXLEN 4096 -#define OKAY 0 -#define ERROR (-1) -#define DEBUG_REGHEADERS -#define DEBUGPREFIX "mReqId= " << mReqId << " fd= " << mFd -#define DEBUGHDR " " << __PRETTY_FUNCTION__ << " (" << __LINE__ << ") " - -using namespace std; - -struct sVdrFileEntry { - unsigned long long sSize; - unsigned long long sCumSize; - int sIdx; - - sVdrFileEntry () {}; - sVdrFileEntry (off_t s, off_t t, int i) - : sSize(s), sCumSize(t), sIdx(i) {}; -}; - -void HttpResourceStartThread(void* arg) { - cHttpResource* m = (cHttpResource*)arg; - m->threadLoop(); - delete m; - pthread_exit(NULL); -} - -// An HTTP resource has two states: Either Read data or write data. -// In read data (until the header and the full body is received), there is no data to write -// in Write data, there is no data to read -// The only think for both states is to watch the socket close state. -cHttpResource::cHttpResource(int f, int id,string addr, int port): mServerAddr(addr), mServerPort(port), mFd(f), mReqId(id), mConnected(true), mConnState(WAITING), - mMethod(), mDataBuffer(NULL), mBlkData(false), mBlkPos(0), mBlkLen(0), mPath(), mVersion(), protocol(), - mAcceptRanges(true), rangeHdr(), mFileSize(-1), mRemLength(0) { - - mLog = Log::getInstance(); - - *(mLog->log()) << DEBUGPREFIX - << " ------- Hello ------- " - << DEBUGHDR<< endl; - - pthread_mutex_init(&mSendLock, NULL); - mDataBuffer = new char[MAXLEN]; -} - - -cHttpResource::~cHttpResource() { - *(mLog->log())<< DEBUGPREFIX - << " ------- Bye ----- " - << " mConnState= " << getConnStateName() - << DEBUGHDR << endl; - delete mDataBuffer; -} - -void cHttpResource::setNonBlocking() { - *(mLog->log())<< DEBUGPREFIX - << " Set Socket to non-blocking" - << DEBUGHDR << endl; - int oldflags = fcntl(mFd, F_GETFL, 0); - oldflags |= O_NONBLOCK; - fcntl(mFd, F_SETFL, oldflags); -} - -int cHttpResource::run() { - if (pthread_create(&mThreadId, NULL, (void*(*)(void*))HttpResourceStartThread, (void *)this) == -1) - return 0; - return 1; -} - -void cHttpResource::threadLoop() { - *(mLog->log())<< DEBUGPREFIX - << " Thread Started" - << DEBUGHDR << endl; - - // The default is to read one HTTP request and then close... - - fd_set read_state; - int maxfd; - struct timeval waitd; - FD_ZERO(&read_state); - FD_SET(mFd, &read_state); - maxfd = mFd; - - for (;;) { - fd_set readfds; - int ret; - - waitd.tv_sec = 1; // Make select wait up to 1 second for data - waitd.tv_usec = 0; // and 0 milliseconds. - - readfds = read_state; - ret = select(maxfd + 1, &readfds, NULL, NULL, &waitd); - if ((ret < 0) && (errno == EINTR)) { - *(mLog->log())<< DEBUGPREFIX - << " Error: " << strerror(errno) - << DEBUGHDR << endl; - continue; - } - - // Check for data on already accepted connections - if (FD_ISSET(mFd, &readfds)) { - *(mLog->log())<< endl; - *(mLog->log())<< DEBUGPREFIX - << " Request Received from Client fd= "<< mFd - << DEBUGHDR << endl; - - ret = readFromClient(); - if (ret <0) { - *(mLog->log())<< DEBUGPREFIX - << " Closing Connection" - << DEBUGHDR - << endl; - close(mFd); - break; - } - } - } // for (;;) - - *(mLog->log())<< DEBUGPREFIX - << " Left loop to terminate fd= " << mFd - << DEBUGHDR << endl; -} - -int cHttpResource::readFromClient() { - // int ret =0; - struct stat statbuf; - int ret = OKAY; - - *(mLog->log())<< DEBUGPREFIX - << DEBUGHDR << endl; - - if (mConnState != WAITING) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: State is not WAITING" - << DEBUGHDR << endl; - return OKAY; - } - - processHttpHeaderNew(); - *(mLog->log())<< " mPath= " << mPath << endl; - // << " mPath(utf8)= " << iso8859ToUtf8(mPath) - - setNonBlocking(); - mConnState = SERVING; - - // done - look for range header - if (strcasecmp(mMethod.c_str(), "GET") != 0){ - sendError(501, "Not supported", NULL, "Method is not supported."); - return ERROR; - } - - if (mPath.compare("/recordings.html") == 0) { - *(mLog->log())<< DEBUGPREFIX - << "generating /recordings.html" - << DEBUGHDR << endl; - - ret = sendRecordingsHtml( &statbuf); - return ERROR; - } - if (mPath.compare("/recordings.xml") == 0) { - *(mLog->log())<< DEBUGPREFIX - << "generating /recordings.xml" - << DEBUGHDR << endl; - - ret = sendRecordingsXml( &statbuf); - return ERROR; - } - - if (stat(mPath.c_str(), &statbuf) < 0) { - sendError(404, "Not Found", NULL, "File not found."); - return ERROR; - } - else { - if (S_ISDIR(statbuf.st_mode)) { - if (mPath.find(".rec") != string::npos) { - ret = sendVdrDir( &statbuf); - } - else { - sendDir( &statbuf); - } - } - else { - printf ("file send\n"); - mFileSize = statbuf.st_size; - ret = sendFirstChunk(&statbuf); - } - } - if (mRemLength <=0) - ret = ERROR; - if (mConnState == TOCLOSE) - ret = ERROR; - - return ret; -} - -void cHttpResource::sendError(int status, const char *title, const char *extra, const char *text) { - char f[400]; - - mConnState = TOCLOSE; - string hdr = ""; - sendHeaders(status, title, extra, "text/html", -1, -1); - snprintf(f, sizeof(f), "%d %s\r\n", status, title); - hdr += f; - snprintf(f, sizeof(f), "

%d %s

\r\n", status, title); - hdr += f; - snprintf(f, sizeof(f), "%s\r\n", text); - hdr += f; - snprintf(f, sizeof(f), "\r\n"); - hdr += f; - - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return; - } -} - - -int cHttpResource::sendDir(struct stat *statbuf) { - char pathbuf[4096]; - char f[400]; - int len; - - mConnState = TOCLOSE; - *(mLog->log())<< "sendDir:" << endl; - *(mLog->log())<< "path= " << mPath << endl; - len = mPath.length(); - int ret = OKAY; - - if (len == 0 || mPath[len - 1] != '/') { - snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mPath.c_str()); - sendError(302, "Found", pathbuf, "Directories must end with a slash."); - ret = ERROR; - } - else { - snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mPath.c_str()); - if (stat(pathbuf, statbuf) >= 0) { - mPath = pathbuf; - sendFirstChunk(statbuf); - // sendFile(pathbuf, statbuf); // found an index.html file in the directory - } - else { - DIR *dir; - struct dirent *de; - - sendHeaders(200, "OK", NULL, "text/html", -1, statbuf->st_mtime); - - string hdr = ""; - snprintf(f, sizeof(f), "Index of %s\r\n", mPath.c_str()); - hdr += f; - snprintf(f, sizeof(f), "

Index of %s

\r\n
\n", mPath.c_str());
-      hdr += f;
-      snprintf(f, sizeof(f), "Name                             Last Modified              Size\r\n");
-      hdr += f;
-      snprintf(f, sizeof(f), "
\r\n"); - hdr += f; - - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - - if (len > 1) { - snprintf(f, sizeof(f), "..\r\n"); - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - } - - dir = opendir(mPath.c_str()); - while ((de = readdir(dir)) != NULL) { - char timebuf[32]; - struct tm *tm; - strcpy(pathbuf, mPath.c_str()); - printf (" -Entry: %s\n", de->d_name); - strcat(pathbuf, de->d_name); - - stat(pathbuf, statbuf); - tm = gmtime(&(statbuf->st_mtime)); - strftime(timebuf, sizeof(timebuf), "%d-%b-%Y %H:%M:%S", tm); - - hdr = ""; - snprintf(f, sizeof(f), "", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : ""); - hdr += f; - - snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : " "); - hdr += f; - - if (strlen(de->d_name) < 32) { - snprintf(f, sizeof(f), "%*s", 32 - strlen(de->d_name), ""); - hdr += f; - } - if (S_ISDIR(statbuf->st_mode)) { - snprintf(f, sizeof(f), "%s\r\n", timebuf); - hdr += f; - } - else { - snprintf(f, sizeof(f), "%s\r\n", timebuf); - hdr += f; - } - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - } - closedir(dir); - *(mLog->log())<< DEBUGPREFIX - << "Done" - << DEBUGHDR << endl; - - snprintf(f, sizeof(f), "
\r\n
\r\n
%s
\r\n\r\n", SERVER); - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - } - } - if (mRemLength != 0) - *(mLog->log())<< " WARNING: mRemLength not zero" << endl; - mRemLength = 0; - return ret; -} - -string cHttpResource::removeEtChar(string line) { - bool done = false; - size_t cur_pos = 0; - size_t pos = 0; - string res = ""; - - int end_after_done = 0; - - while (!done) { - pos = line.find('&', cur_pos); - if (pos == string::npos) { - done = true; - res += line.substr(cur_pos); - break; - } - if (pos >= 0) { - res += line.substr(cur_pos, (pos-cur_pos)) + "&"; - // cur_pos = cur_pos+ pos +1; - cur_pos = pos +1; - end_after_done ++; - } - } - if (end_after_done != 0) { - *(mLog->log())<< "removeEtChar" << " line= " << line; - *(mLog->log())<< " res= " << res << " occurances= " << end_after_done << endl; - } - return res; -} - -int cHttpResource::sendRecordingsXml(struct stat *statbuf) { - sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); - - string hdr = ""; - hdr += "\n"; - // hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - - char buff[20]; - char f[400]; - cRecordings Recordings; - Recordings.Load(); - - int count = 0; - for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { - hdr = ""; - if (recording->HierarchyLevels() == 0) { - if (++count != 11) - continue; - hdr += "\n"; - strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&recording->start)); - hdr += ""; - snprintf(f, sizeof(f), "%s - %s", buff, removeEtChar(recording->Name()).c_str()); - hdr += f; - hdr += "\n"; - hdr += ""; - snprintf(f, sizeof(f), "http://%s:%d%s/", mServerAddr.c_str(), mServerPort, removeEtChar(recording->FileName()).c_str()); - hdr += f; - hdr += "\n"; - hdr += ""; - // TODO Title - const cRecordingInfo* info = recording->Info(); - // snprintf(f, sizeof(f), "title= %s\n", info->Title()); - // hdr += f; - hdr += "Hallo"; - hdr += "HexDump title= \n" + hexDump(recording->Name()); - hdr += "\nHexDump link= \n" + hexDump(recording->FileName()) + "\n"; - *(mLog->log())<< DEBUGPREFIX - << " *** " - << " HexDump title= \n" << hexDump(recording->Name()) << endl - << " HexDump link= \n" << hexDump(recording->FileName()) << endl - << DEBUGHDR - << endl; - - // snprintf(f, sizeof(f), "short= %s\n", info->ShortText()); - // hdr += f; - // snprintf(f, sizeof(f), "desc= %s\n", info->Description()); - // hdr += f; - - hdr += "\n"; - hdr += "\n"; - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - } - // start is time_t - } - - hdr += "\n"; - hdr += "\n"; - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - - return ERROR; -} -int cHttpResource::sendRecordingsHtml(struct stat *statbuf) { - sendHeaders(200, "OK", NULL, "text/html", -1, statbuf->st_mtime); - - string hdr = ""; - hdr += "Recordings\r\n"; - hdr += "\r\n"; - hdr += ""; - hdr += "

Recordings

\r\n
\n";
-  hdr += "
\r\n"; - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - - char buff[20]; - char f[400]; - cRecordings Recordings; - Recordings.Load(); - for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { - hdr = ""; - strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&recording->start)); - snprintf(f, sizeof(f), "%s - %d %s\r\n", buff, recording->HierarchyLevels(), recording->FileName(), recording->Name()); - hdr += f; - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return ERROR; - } - // start is time_t - } - return ERROR; -} - -int cHttpResource::sendVdrDir(struct stat *statbuf) { - *(mLog->log())<< DEBUGPREFIX - << " *** " - << DEBUGHDR - << endl; - char pathbuf[4096]; - char f[400]; - int vdr_idx = 0; - off_t total_file_size = 0; - int ret = OKAY; - - string vdr_dir = mPath; - vector file_sizes; - bool more_to_go = true; - - while (more_to_go) { - vdr_idx ++; - snprintf(pathbuf, sizeof(pathbuf), "%s%03d.vdr", mPath.c_str(), vdr_idx); - if (stat(pathbuf, statbuf) >= 0) { - *(mLog->log())<< " found for " << pathbuf << endl; - file_sizes.push_back(sVdrFileEntry(statbuf->st_size, total_file_size, vdr_idx)); - total_file_size += statbuf->st_size; - } - else { - more_to_go = false; - } - } - if (file_sizes.size() < 1) { - // There seems to be vdr video file in the directory - *(mLog->log())<< DEBUGPREFIX - << " No video file in the directory" - << DEBUGHDR << endl; - sendError(404, "Not Found", NULL, "File not found."); - ret = ERROR; - return ret; - } - *(mLog->log())<< DEBUGPREFIX - << " vdr filesize list " - << DEBUGHDR << endl; - - for (uint i = 0; i < file_sizes.size(); i++) - *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " tSize= " << file_sizes[i].sCumSize << endl; - // *(mLog->log())<< endl; - *(mLog->log())<< " total_file_size= " << total_file_size << endl; - - uint cur_idx = 0; - - if (!rangeHdr.isRangeRequest) { - snprintf(pathbuf, sizeof(pathbuf), "%s%03d.vdr", mPath.c_str(), file_sizes[cur_idx].sIdx); - - if (openFile(pathbuf) != OKAY) - return ERROR; - - mRemLength = total_file_size; - sendHeaders(200, "OK", NULL, "video/mpeg", total_file_size, statbuf->st_mtime); - } - else { // Range request - // idenify the first file - *(mLog->log())<< DEBUGPREFIX - << " Range Request Handling" - << DEBUGHDR << endl; - - cur_idx = file_sizes.size() -1; - for (uint i = 0; i < file_sizes.size(); i++) { - if (file_sizes[i].sCumSize > rangeHdr.begin) { - cur_idx = i -1; - break; - } - } - *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sCumSize= " - << file_sizes[cur_idx].sCumSize << " rangeHdr.begin= " << rangeHdr.begin - << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; - snprintf(pathbuf, sizeof(pathbuf), "%s%03d.vdr", mPath.c_str(), file_sizes[cur_idx].sIdx); - *(mLog->log())<< " file identified= " << pathbuf << endl; - if (openFile(pathbuf) != OKAY) - return ERROR; - - mPath = pathbuf; - *(mLog->log())<< " Seeking into file= " << (rangeHdr.begin - file_sizes[cur_idx].sCumSize) - << " cur_idx= " << cur_idx - << " file_sizes[cur_idx].sCumSize= " << file_sizes[cur_idx].sCumSize - << endl; - - fseek(mFile, (rangeHdr.begin - file_sizes[cur_idx].sCumSize), SEEK_SET); - if (rangeHdr.end == 0) - rangeHdr.end = total_file_size; - mRemLength = (rangeHdr.end-rangeHdr.begin); - snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), total_file_size); - sendHeaders(206, "Partial Content", f, "video/mpeg", total_file_size, statbuf->st_mtime); - } - - *(mLog->log())<< " ***** Yes, vdr dir found ***** mPath= " << mPath<< endl; - - while (cur_idx < file_sizes.size()) { - ret = sendDataChunk(); - fclose(mFile); - - if (ret == ERROR) { - /* *(mLog->log())<< DEBUGPREFIX - << " Error, returning" - << DEBUGHDR << endl; - */ - return ERROR; - break; - } - cur_idx ++; - if (cur_idx == file_sizes.size()) { - *(mLog->log())<< DEBUGPREFIX - << " Done " - << DEBUGHDR << endl; - break; - } - if (ret == OKAY) { - snprintf(pathbuf, sizeof(pathbuf), "%s%03d.vdr", mPath.c_str(), file_sizes[cur_idx].sIdx); - *(mLog->log())<< " Next File pathbuf= " << pathbuf << endl; - if (openFile(pathbuf) != OKAY) - return ERROR; - } - } - return ret; -} - -void cHttpResource::sendHeaders(int status, const char *title, const char *extra, const char *mime, - off_t length, time_t date) { - - time_t now; - char timebuf[128]; - char f[400]; - - string hdr = ""; - snprintf(f, sizeof(f), "%s %d %s\r\n", PROTOCOL, status, title); - hdr += f; - snprintf(f, sizeof(f), "Server: %s\r\n", SERVER); - hdr += f; - now = time(NULL); - strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&now)); - snprintf(f, sizeof(f), "Date: %s\r\n", timebuf); - hdr += f; - if (extra) { - snprintf(f, sizeof(f), "%s\r\n", extra); - hdr += f; - } - if (mime) { - snprintf(f, sizeof(f), "Content-Type: %s\r\n", mime); - hdr += f; - } - if (length >= 0) { - snprintf(f, sizeof(f), "Content-Length: %lld\r\n", length); - *(mLog->log())<< DEBUGPREFIX - << " length= " << length - << DEBUGHDR << endl; - hdr += f; - } - if (date != -1) { - strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date)); - snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf); - hdr += f; - } - if (mAcceptRanges) { - snprintf(f, sizeof(f), "Accept-Ranges: bytes\r\n"); - hdr += f; - } - snprintf(f, sizeof(f), "Connection: close\r\n"); - hdr += f; - - snprintf(f, sizeof(f), "\r\n"); - hdr += f; - if ( writeToClient(hdr.c_str(), hdr.size()) == ERROR) { - return; - } - -} - -int cHttpResource::sendFirstChunk(struct stat *statbuf) { - // Send the First Datachunk, incl all headers - - *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId << " mPath= " << mPath - << DEBUGHDR - << endl; - - char f[400]; - - if (openFile(mPath.c_str()) == ERROR) - return ERROR; - mFile = fopen(mPath.c_str(), "r"); - int ret = OKAY; - - if (!mFile) { - sendError(403, "Forbidden", NULL, "Access denied."); - ret = ERROR; - } - else { - mFileSize = S_ISREG(statbuf->st_mode) ? statbuf->st_size : -1; - - if (!rangeHdr.isRangeRequest) { - mRemLength = mFileSize; - sendHeaders(200, "OK", NULL, getMimeType(mPath.c_str()), mFileSize, statbuf->st_mtime); - } - else { // Range request - fseek(mFile, rangeHdr.begin, SEEK_SET); - if (rangeHdr.end == 0) - rangeHdr.end = mFileSize; - mRemLength = (rangeHdr.end-rangeHdr.begin); - snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), mFileSize); - sendHeaders(206, "Partial Content", f, getMimeType(mPath.c_str()), (rangeHdr.end-rangeHdr.begin), statbuf->st_mtime); - } - - *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId - << ": Done mRemLength= "<< mRemLength << " ret= " << ret - << DEBUGHDR << endl; - - ret = sendDataChunk(); - *(mLog->log())<< " Close File" << endl; - fclose(mFile); - } - return ret; - -} - -int cHttpResource::sendDataChunk() { - // the the paload of an open file - *(mLog->log())<< DEBUGPREFIX - << " mConnState= " << getConnStateName() << " mRemLength= "<< mRemLength - << DEBUGHDR << endl; - - int n; - int chunk_no =0; - int bytes_to_send = 0; - - int ret = 0; - - char buf[MAXLEN]; - int buflen = sizeof(buf); - - if (!mConnected) - return ERROR; - - if (mConnState == WAITING) { - *(mLog->log())<< "fd= " << mFd - << " Something wrong: Should not be here" - << DEBUGHDR << endl; - return OKAY; - } - if (mRemLength ==0) { - *(mLog->log())<< " closing connection" - << DEBUGHDR << endl; - mConnected = false; - close (mFd); - return ERROR; - } - if (mConnState == TOCLOSE) { - *(mLog->log())<< DEBUGPREFIX - << " closing connection" - << DEBUGHDR << endl; - mConnected = false; - close (mFd); - return ERROR; - } - - bool done = false; - while (!done) { - // Check whethere there is no other data to send (from last time) - if (mRemLength == 0) { - *(mLog->log())<< DEBUGPREFIX - << " mRemLength == 0 --> closing connection" - << DEBUGHDR << endl; - mConnected = false; - close (mFd); - return ERROR; - - } - if (mRemLength >= buflen) { - bytes_to_send = buflen; - } - else - bytes_to_send = mRemLength; - - n = fread(buf, 1, bytes_to_send, mFile); - if (n != bytes_to_send) { - *(mLog->log())<< DEBUGPREFIX - << " -- Something wrong here - n= " << n << " bytes_to_send= " << bytes_to_send - << DEBUGHDR << endl; - done = true; - } - - ret = writeToClient( buf, bytes_to_send); - if (ret == ERROR) { - *(mLog->log())<< DEBUGPREFIX - << " Stopping - Client closed connection " - << DEBUGHDR << endl; - - // socket had blocket. wait until select comes back. - done = true; - // fclose(mFile); - ret = ERROR; - break; - } - - mRemLength -= bytes_to_send; - chunk_no ++; - // *(mLog->log())<< " chunk_no= " << chunk_no << endl; - } - - return ret; -} - -int cHttpResource::writeToClient(const char *buf, size_t buflen) { - // *(mLog->log())<< __PRETTY_FUNCTION__ << " (" << __LINE__ << "): " - // << "fd= " << mFd << " mReqId=" << mReqId << " mConnected= " << ((mConnected)? "true":"false") - // << " buflen= " << buflen << " mRemLength= "<< mRemLength << endl; - - unsigned int bytes_written = 0; - int this_write; - int retries = 0; - - struct timeval timeout; - - int ret = 0; - fd_set write_set; - - FD_ZERO(&write_set); - - pthread_mutex_lock(&mSendLock); - - if (!mConnected) { - *(mLog->log())<< DEBUGPREFIX - << " not connected anymore" - DEBUGHDR << endl; - pthread_mutex_unlock(&mSendLock); - return ERROR; - } - - if (mConnState == WAITING) { - *(mLog->log())<< DEBUGPREFIX - << " Should not be in WAITING state" - << DEBUGHDR << endl; - pthread_mutex_unlock(&mSendLock); - return OKAY; - } - - bool done = false; - while (!done) { - FD_ZERO(&write_set); - FD_SET(mFd, &write_set); - timeout.tv_sec = 10; - timeout.tv_usec = 0; - ret = select(mFd + 1, NULL, &write_set, NULL, NULL); - if (ret < 1) { - *(mLog->log())<< DEBUGPREFIX - << " Select returned error -- Closing connection" - << DEBUGHDR << endl; - mConnected = false; - pthread_mutex_unlock(&mSendLock); - close(mFd); - return ERROR; // error, or timeout - } - if (ret == 0) { - *(mLog->log())<< DEBUGPREFIX - << " Select returned ZERO -- Closing connection" - << DEBUGHDR << endl; - mConnected = false; - pthread_mutex_unlock(&mSendLock); - close(mFd); - return ERROR; // error, or timeout - } - this_write = write(mFd, &buf[bytes_written], buflen - bytes_written); - if (this_write <=0) { - /* *(mLog->log())<< DEBUGPREFIX - << " ERROR: Stopped (Client terminated Connection)" - << DEBUGHDR << endl; - */ - mConnected = false; - close(mFd); - pthread_mutex_unlock(&mSendLock); - return ERROR; - } - bytes_written += this_write; - - if (bytes_written == buflen) { - done = true; - break; - } - else { - if (++retries == 100) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: Too many retries " - << DEBUGHDR << endl; - mConnected = false; - close(mFd); - pthread_mutex_unlock(&mSendLock); - return ERROR; - } - } - } - // *(mLog->log())<< __PRETTY_FUNCTION__ << " (" << __LINE__ << "): " - // << " done "<< endl; - // Done with writing - pthread_mutex_unlock(&mSendLock); - - return OKAY; -} - - - - -char *cHttpResource::getMimeType(const char *name) { - char *ext = strrchr((char*)name, '.'); - if (!ext) - return NULL; - // if (ext.compare(".html") || ext.compare(".htm")) return "text/html"; - if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; - if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; - if (strcmp(ext, ".gif") == 0) return "image/gif"; - if (strcmp(ext, ".png") == 0) return "image/png"; - if (strcmp(ext, ".css") == 0) return "text/css"; - if (strcmp(ext, ".au") == 0) return "audio/basic"; - if (strcmp(ext, ".wav") == 0) return "audio/wav"; - if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; - if (strcmp(ext, ".mp4") == 0) return "video/mp4"; - if (strcmp(ext, ".vdr") == 0) return "video/mpeg"; - if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; - if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; - return NULL; -} - -int cHttpResource::processHttpHeaderNew() { - *(mLog->log())<< DEBUGPREFIX - << " processHttpHeaderNew " - << DEBUGHDR << endl; - - char buf[MAXLEN]; - int buflen = sizeof(buf); - - int line_count = 0; - bool hdr_end_found = false; - bool is_req = true; - // block until the entire request header is read - string rem_hdr = ""; - - while (!hdr_end_found) { - int count = 0; - while ((buflen = read(mFd, buf, sizeof(buf))) == -1) - count ++; - if (count != 0) - *(mLog->log())<< " Blocked for " << count << " Iterations " << endl; - //FIXME. Better return and wait. - if (buflen == -1) { - *(mLog->log())<< " Some Error" << endl; - return 2; // Nothing to read - } - #ifdef DEBUG_REQHEADERS - *(mLog->log())<< " Read " << buflen << " Bytes from " << fd << endl; - #endif - string req_line = rem_hdr + buf; - if (rem_hdr.size() != 0) { - *(mLog->log())<< DEBUGPREFIX - << " rem_hdr.size() = " << rem_hdr.size() - << DEBUGHDR << endl; - } - buflen += rem_hdr.size(); - - size_t last_pos = 0; - while (true) { - line_count ++; - size_t pos = req_line.find ("\r\n", last_pos); - if (pos > buflen) { - *(mLog->log())<< DEBUGPREFIX - << " Pos (" << pos << ") outside of read buffer" - << DEBUGHDR << endl; - rem_hdr = req_line.substr(last_pos, buflen - last_pos); - *(mLog->log())<< DEBUGPREFIX - << " No HdrEnd Found, read more data. rem_hdr= " << rem_hdr.size() - << DEBUGHDR << endl; - break; - } - if ((last_pos - pos) == 0) { - *(mLog->log())<< DEBUGPREFIX - << " Header End Found" - << DEBUGHDR << endl; - hdr_end_found = true; - break; - } - - if (pos == string::npos){ - // not found - rem_hdr = req_line.substr(last_pos, buflen - last_pos); - *(mLog->log())<< DEBUGPREFIX - << " No HdrEnd Found, read more data. rem_hdr= " << rem_hdr.size() - << DEBUGHDR << endl; - break; - } - - string line = req_line.substr(last_pos, (pos-last_pos)); - -#ifdef DEBUG_REQHEADERS - *(mLog->log())<< " Line= " << line << endl; -#endif - last_pos = pos +2; - if (is_req) { - is_req = false; - // Parse the request line - mMethod = line.substr(0, line.find_first_of(" ")); - mPath = line.substr(line.find_first_of(" ") +1, (line.find_last_of(" ") - line.find_first_of(" ") -1)); - mVersion = line.substr(line.find_last_of(" ") +1); - - *(mLog->log())<< DEBUGPREFIX - << " ReqLine= " << line << endl; - *(mLog->log())<< DEBUGPREFIX - << " mMethod= " << mMethod - << " mPath= " << mPath - // << " mPath(utf8)= " << iso8859ToUtf8(mPath) - << " mVer= " << mVersion - << " HexDump= " << endl << hexDump(mPath) - << DEBUGHDR << endl; - } - else { - string hdr_name = line.substr(0, line.find_first_of(":")); - string hdr_val = line.substr(line.find_first_of(":") +2); - if (hdr_name.compare("Range") == 0) { - parseRangeHeaderValue(hdr_val); - *(mLog->log())<< " Range: Begin= " << rangeHdr.begin - << " End= " << rangeHdr.end - << endl; - } - if (hdr_name.compare("User-Agent") == 0) { - // *(mLog->log())<< " ***" << hdr_name << endl; - *(mLog->log())<< " User-Agent: " << hdr_val - << endl; - } - - } - // *(mLog->log())<< " update last_pos= " << last_pos << endl;; - } - } - // exit(0); - return OKAY; -} - -string cHttpResource::getConnStateName() { - string state_string; - switch (mConnState) { - case WAITING: - state_string = "WAITING"; - break; - case SERVING: - state_string = "SERVING"; - break; - case TOCLOSE: - state_string = "TOCLOSE"; - break; - default: - state_string = "UNKNOWN"; - break; - } - return state_string; -} - -int cHttpResource::parseRangeHeaderValue(string val) { - rangeHdr.isRangeRequest = true; - size_t pos_equal = val.find_first_of('='); - size_t pos_minus = val.find_first_of('-'); - - string range_type = val.substr(0, pos_equal); - string first_val= val.substr(pos_equal+1, pos_minus -(pos_equal+1)); - rangeHdr.begin = atoll(first_val.c_str()); - - string sec_val = ""; - if ((pos_minus +1)< val.size()){ - sec_val = val.substr(pos_minus+1); - rangeHdr.end = atoll(sec_val.c_str()); - } - // *(mLog->log())<< " RangeType= (" << range_type - // << ") Begin= " << rangeHdr.begin - // << " End= " << rangeHdr.end - // << endl; - // *(mLog->log())<< " equal= " << equal << " minus= " << minus << " size= " << hdr_val.size() << endl; - -} - -int cHttpResource::openFile(const char *name) { - mFile = fopen(name, "r"); - if (!mFile) { - *(mLog->log())<< DEBUGPREFIX - << " fopen failed pathbuf= " << name - << DEBUGHDR << endl; - sendError(403, "Forbidden", NULL, "Access denied."); - return ERROR; - } - return OKAY; -} - -string cHttpResource::hexDump(string in) { - string res = ""; - string ascii = ""; - char buf[10]; - - int line_count = 0; - for (uint i = 0; i < in.size(); i++) { - unsigned char num = in[i]; - sprintf (buf, "%02hhX", num); - if ((num >= 32) && (num < 127)) { - ascii += char(num); - } - else - ascii += '.'; - res += buf; - - line_count++; - switch (line_count) { - case 8: - res += " "; - ascii += " "; - break; - case 16: - res += " " + ascii; - res += "\r\n"; - ascii = ""; - line_count = 0; - break; - default: - res += " "; - break; - } - } - if (line_count != 0) { - for (int i = 0; i < ((16 - line_count) * 3 ); i++) - res += " "; - if (line_count >= 8) - res += " "; - res += ascii; - } - return res; -} - - -string iso8859ToUtf8 (string input) { - string res = ""; - - /* for (uint i = 0; i < input.size(); i++) { - unsigned char num = input[i]; - if (num < 128) - res += char(num); - else { - // res += char(0xc2 + (num > 0xbf)); - // res += char((num & 0x3f) +0x80); - } - - }*/ - - return res; - // unsigned char *in, *out; - // while (*in) - // if (*in<128) *out++=*in++; - // else *out++=0xc2+(*in>0xbf), *out++=(*in++&0x3f)+0x80; - - } diff --git a/vdr-smarttvweb/httpresource-hmm.h b/vdr-smarttvweb/httpresource-hmm.h deleted file mode 100644 index 543377d..0000000 --- a/vdr-smarttvweb/httpresource-hmm.h +++ /dev/null @@ -1,94 +0,0 @@ - -#ifndef __HTTPREQUEST_H__ -#define __HTTPREQUEST_H__ - -#include -#include -#include -#include "log.h" - -using namespace std; - -struct cRange { -cRange(): isRangeRequest(false), begin(0), end(0) {}; - bool isRangeRequest; - unsigned long long begin; - unsigned long long end; -}; - -enum eConnState { - WAITING, - SERVING, - TOCLOSE -}; - -class cHttpResource { - - public: - cHttpResource(int, int, string, int); - virtual ~cHttpResource(); - - int readFromClient(); - // int sendNextChunk(); - void threadLoop(); - int run(); - - private: - Log* mLog; - pthread_t mThreadId; - pthread_mutex_t mSendLock; - string mServerAddr; - int mServerPort; - int mFd; - int mReqId; - - bool mConnected; - eConnState mConnState; - string mMethod; - char *mDataBuffer; - bool mBlkData; - int mBlkPos; - int mBlkLen; - - // string path; - string mPath; - string mVersion; - string protocol; - - bool mAcceptRanges; - cRange rangeHdr; - unsigned long long mFileSize; - uint mRemLength; - FILE *mFile; - - - // int tcpServerWrite(const char buf[], int buflen); - int writeToClient(const char *buf, size_t buflen); - int sendDataChunk(); - - void setNonBlocking(); - - int processHttpHeaderNew(); - // int processHttpHeader(); - void sendError(int status, const char *title, const char *extra, const char *text); - int sendDir(struct stat *statbuf); - int sendVdrDir(struct stat *statbuf); - int sendRecordingsHtml (struct stat *statbuf); - int sendRecordingsXml (struct stat *statbuf); - string removeEtChar(string line); - - void sendHeaders(int status, const char *title, const char *extra, const char *mime, - off_t length, time_t date); - - int sendFirstChunk(struct stat *statbuf); - - // Helper Functions - char *getMimeType(const char *name); - string getConnStateName(); - int parseRangeHeaderValue(string); - int openFile(const char *name); - string hexDump(string in); - string iso8859ToUtf8 (string); - -}; -#endif -- cgit v1.2.3 From b7f13070c402a975b36fe514c06da82d10238fcc Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 13 Jan 2013 20:55:36 +0100 Subject: Folder support for Live. Less conservative buffer times. --- vdr-smarttvweb/httpresource.c | 94 +++++++++++++++++++++++++++++++++---------- vdr-smarttvweb/httpresource.h | 3 +- vdr-smarttvweb/widget.conf | 4 +- 3 files changed, 77 insertions(+), 24 deletions(-) diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c index 44100aa..dc09329 100644 --- a/vdr-smarttvweb/httpresource.c +++ b/vdr-smarttvweb/httpresource.c @@ -869,7 +869,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) { +int cHttpResource::writeXmlItem(string name, string link, string programme, string desc, string guid, time_t start, int dur, int fps, int is_pes) { string hdr = ""; char f[400]; @@ -887,7 +887,7 @@ int cHttpResource::writeXmlItem(string name, string link, string programme, stri hdr += f; hdr += "\n"; - hdr += ""; + /* hdr += ""; if (start != 0) { strftime(f, sizeof(f), "%y%m%d %H:%M", localtime(&start)); hdr += f; @@ -895,16 +895,42 @@ int cHttpResource::writeXmlItem(string name, string link, string programme, stri else hdr += "0 0"; hdr += "\n"; - +*/ snprintf(f, sizeof(f), "%d", dur); hdr += ""; hdr += f; hdr += "\n"; + if (fps != -1) + snprintf(f, sizeof(f), "%d\n", fps); + else + snprintf(f, sizeof(f), "unknown\n"); + hdr += f; + + switch (is_pes){ + case -1: + // unknown + hdr += "unknown\n"; + break; + case 0: + // true + hdr += "true\n"; + break; + case 1: + // false + hdr += "false\n"; + break; + default: + break; + } + hdr += "\n"; *mResponseMessage += hdr; + + + // return writeToClient(hdr.c_str(), hdr.size()); return OKAY; } @@ -1346,14 +1372,11 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) { vector entries; - //thlo if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) { sendError(404, "Not Found", NULL, "Media Folder likely not configured."); return OKAY; } - sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); - string hdr = ""; hdr += "\n"; hdr += "\n"; @@ -1368,7 +1391,7 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) { snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", mServerAddr.c_str(), mServerPort, cUrlEncode::doUrlSaveEncode(entries[i].sPath).c_str()); if (writeXmlItem(cUrlEncode::doXmlSaveEncode(entries[i].sName), pathbuf, "NA", "NA", "-", - entries[i].sStart, -1) == ERROR) + entries[i].sStart, -1, -1, -1) == ERROR) return ERROR; } @@ -1376,7 +1399,8 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) { hdr = "\n"; hdr += "\n"; *mResponseMessage += hdr; - + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); + return OKAY; } @@ -1492,6 +1516,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") { @@ -1511,8 +1536,6 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { } - sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); - string hdr = ""; hdr += "\n"; hdr += "\n"; @@ -1528,8 +1551,10 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { const cSchedules *schedules = cSchedules::Schedules(*lock); for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { - if (channel->GroupSep()) + if (channel->GroupSep()) { + group_sep = channel->Name(); continue; + } if (--count == 0) { break; } @@ -1567,8 +1592,10 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { << count << " Name= " << channel->Name() << endl; } - - if (writeXmlItem(channel->Name(), link, title, desc, *(channel->GetChannelID()).ToString(), start_time, duration) == ERROR) + + string c_name = (group_sep != "") ? (group_sep + "~" + channel->Name()) : 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) == ERROR) return ERROR; } @@ -1578,6 +1605,7 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) { *mResponseMessage += hdr; delete lock; + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); #endif return OKAY; @@ -1605,10 +1633,6 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { *(mLog->log())<< DEBUGPREFIX << " Found a Model Parameter: " << model << endl; - //thlo - /* if (model == "samsung") - link_ext = "/manifest-seg.m3u8|COMPONENT=HLS"; -*/ } if (getQueryAttributeValue(&avps, "type", type) == OKAY){ @@ -1703,13 +1727,19 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { << " is skipped (No Event()" << endl; continue; } - *(mLog->log()) << DEBUGPREFIX + /* *(mLog->log()) << DEBUGPREFIX << " Active Timer: " << ti->File() << " Start= " << ti->Event()->StartTime() << " Duration= " << ti->Event()->Duration() << endl; - act_rec.push_back(sTimerEntry(ti->File(), ti->Event()->StartTime(), ti->Event()->Duration())); - //act_rec.push_back(sTimerEntry(ti->File(), ti->StartTime(), (ti->StopTime() - ti->StartTime()))); +*/ + *(mLog->log()) << DEBUGPREFIX + << " Active Timer: " << ti->File() + << " Start= " << ti->StartTime() + << " Duration= " << (ti->StopTime() - ti->StartTime()) + << endl; + // act_rec.push_back(sTimerEntry(ti->File(), ti->Event()->StartTime(), ti->Event()->Duration())); + act_rec.push_back(sTimerEntry(ti->File(), ti->StartTime(), (ti->StopTime() - ti->StartTime()))); } } @@ -1758,7 +1788,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { } if (writeXmlItem(cUrlEncode::doXmlSaveEncode(recording->Name()), link, "NA", desc, "-", - recording->Start(), rec_dur) == ERROR) + recording->Start(), rec_dur, recording->FramesPerSecond(), (recording->IsPesRecording() ? 0: 1)) == ERROR) return ERROR; } @@ -1773,6 +1803,26 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) { return OKAY; } +void cHttpResource::checkForTimeRequest() { + return; + + /* vector avps; + parseQueryLine(&avps); + string time = ""; + + if (getQueryAttributeValue(&avps, "time", time) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Time Parameter: " << time + << endl; + } + */ + // First, I need to get the fps value + // then I determine the frame number mathcing the time + // the I go into index and find the byte offset and the vdr idx + // Then I need to shortcut the calc in sendVdrDir + +} + int cHttpResource::sendVdrDir(struct stat *statbuf) { #ifndef DEBUG @@ -1789,6 +1839,8 @@ int cHttpResource::sendVdrDir(struct stat *statbuf) { checkRecording(); + // The range request functions are activated, when a time header is detected + // checkForTimeRequest(); mVdrIdx = 1; mFileStructure = "%s/%03d.vdr"; diff --git a/vdr-smarttvweb/httpresource.h b/vdr-smarttvweb/httpresource.h index 79459b2..bb0df3a 100644 --- a/vdr-smarttvweb/httpresource.h +++ b/vdr-smarttvweb/httpresource.h @@ -163,6 +163,7 @@ class cHttpResource { const char *getMimeType(const char *name); string getConnStateName(); void checkRecording(); + void checkForTimeRequest(); int parseRangeHeaderValue(string); int parseHttpRequestLine(string); int parseHttpHeaderLine (string); @@ -173,6 +174,6 @@ class cHttpResource { int getQueryAttributeValue(vector *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); + int writeXmlItem(string title, string link, string programme, string desc, string guid, time_t start, int dur, int fps, int is_pes); }; #endif diff --git a/vdr-smarttvweb/widget.conf b/vdr-smarttvweb/widget.conf index c028671..da4a006 100644 --- a/vdr-smarttvweb/widget.conf +++ b/vdr-smarttvweb/widget.conf @@ -3,8 +3,8 @@ hls 6000000 40 -20 -50 +5 +5 5 30 20 -- cgit v1.2.3 From 6df64d9467f59f6a5d8ae6aa6b177db8084beb43 Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 13 Jan 2013 21:04:42 +0100 Subject: zipped Widget-v85 --- release/Widget-v085.zip | Bin 0 -> 1024778 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 release/Widget-v085.zip diff --git a/release/Widget-v085.zip b/release/Widget-v085.zip new file mode 100755 index 0000000..4115d9b Binary files /dev/null and b/release/Widget-v085.zip differ -- cgit v1.2.3 From 1cb22449f7ce7ffe119b16bbd273177e6a9c4c9e Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 13 Jan 2013 21:18:14 +0100 Subject: Test Commit --- smarttv-client/Images/helpbar/help_0_9.png | Bin 0 -> 3263 bytes smarttv-client/Images/helpbar/help_back.png | Bin 0 -> 3070 bytes smarttv-client/Images/helpbar/help_blue.png | Bin 0 -> 3524 bytes smarttv-client/Images/helpbar/help_enter.png | Bin 0 -> 2906 bytes smarttv-client/Images/helpbar/help_ff_rew.png | Bin 0 -> 2950 bytes smarttv-client/Images/helpbar/help_green.png | Bin 0 -> 3543 bytes smarttv-client/Images/helpbar/help_ime.png | Bin 0 -> 2886 bytes smarttv-client/Images/helpbar/help_info.png | Bin 0 -> 3129 bytes smarttv-client/Images/helpbar/help_joy.png | Bin 0 -> 367 bytes smarttv-client/Images/helpbar/help_lr.png | Bin 0 -> 2903 bytes smarttv-client/Images/helpbar/help_mode.png | Bin 0 -> 380 bytes smarttv-client/Images/helpbar/help_move.png | Bin 0 -> 2923 bytes smarttv-client/Images/helpbar/help_pre_ch.png | Bin 0 -> 3043 bytes smarttv-client/Images/helpbar/help_red.png | Bin 0 -> 3487 bytes smarttv-client/Images/helpbar/help_size.png | Bin 0 -> 432 bytes smarttv-client/Images/helpbar/help_ud.png | Bin 0 -> 2906 bytes smarttv-client/Images/helpbar/help_user.png | Bin 0 -> 3102 bytes smarttv-client/Images/helpbar/help_video.png | Bin 0 -> 461 bytes smarttv-client/Images/helpbar/help_video2.png | Bin 0 -> 864 bytes smarttv-client/Images/helpbar/help_vod.png | Bin 0 -> 383 bytes smarttv-client/Images/helpbar/help_yellow.png | Bin 0 -> 3577 bytes smarttv-client/Images/helpbar/keyhelp_black.png | Bin 0 -> 7417 bytes smarttv-client/Images/helpbar/mute.png | Bin 0 -> 516 bytes smarttv-client/Images/helpbar/mute2.png | Bin 0 -> 752 bytes smarttv-client/Images/helpbar/tools.png | Bin 0 -> 2943 bytes smarttv-client/Images/helpbar/volume.png | Bin 0 -> 761 bytes smarttv-client/Images/helpbar/web.png | Bin 0 -> 1798 bytes smarttv-client/Images/keypad/kp-button-inv.png | Bin 0 -> 1633 bytes smarttv-client/Images/keypad/kp-button.png | Bin 0 -> 1561 bytes smarttv-client/Images/spinner/loadingBG.png | Bin 0 -> 8054 bytes smarttv-client/Images/spinner/loading_1.png | Bin 0 -> 6393 bytes smarttv-client/Images/spinner/loading_10.png | Bin 0 -> 6319 bytes smarttv-client/Images/spinner/loading_11.png | Bin 0 -> 6408 bytes smarttv-client/Images/spinner/loading_12.png | Bin 0 -> 6384 bytes smarttv-client/Images/spinner/loading_2.png | Bin 0 -> 6465 bytes smarttv-client/Images/spinner/loading_3.png | Bin 0 -> 6437 bytes smarttv-client/Images/spinner/loading_4.png | Bin 0 -> 6388 bytes smarttv-client/Images/spinner/loading_5.png | Bin 0 -> 6433 bytes smarttv-client/Images/spinner/loading_6.png | Bin 0 -> 6432 bytes smarttv-client/Images/spinner/loading_7.png | Bin 0 -> 6506 bytes smarttv-client/Images/spinner/loading_8.png | Bin 0 -> 6516 bytes smarttv-client/Images/spinner/loading_9.png | Bin 0 -> 6467 bytes 42 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 smarttv-client/Images/helpbar/help_0_9.png create mode 100644 smarttv-client/Images/helpbar/help_back.png create mode 100644 smarttv-client/Images/helpbar/help_blue.png create mode 100644 smarttv-client/Images/helpbar/help_enter.png create mode 100644 smarttv-client/Images/helpbar/help_ff_rew.png create mode 100644 smarttv-client/Images/helpbar/help_green.png create mode 100644 smarttv-client/Images/helpbar/help_ime.png create mode 100644 smarttv-client/Images/helpbar/help_info.png create mode 100644 smarttv-client/Images/helpbar/help_joy.png create mode 100644 smarttv-client/Images/helpbar/help_lr.png create mode 100644 smarttv-client/Images/helpbar/help_mode.png create mode 100644 smarttv-client/Images/helpbar/help_move.png create mode 100644 smarttv-client/Images/helpbar/help_pre_ch.png create mode 100644 smarttv-client/Images/helpbar/help_red.png create mode 100644 smarttv-client/Images/helpbar/help_size.png create mode 100644 smarttv-client/Images/helpbar/help_ud.png create mode 100644 smarttv-client/Images/helpbar/help_user.png create mode 100644 smarttv-client/Images/helpbar/help_video.png create mode 100644 smarttv-client/Images/helpbar/help_video2.png create mode 100644 smarttv-client/Images/helpbar/help_vod.png create mode 100644 smarttv-client/Images/helpbar/help_yellow.png create mode 100644 smarttv-client/Images/helpbar/keyhelp_black.png create mode 100644 smarttv-client/Images/helpbar/mute.png create mode 100644 smarttv-client/Images/helpbar/mute2.png create mode 100644 smarttv-client/Images/helpbar/tools.png create mode 100644 smarttv-client/Images/helpbar/volume.png create mode 100644 smarttv-client/Images/helpbar/web.png create mode 100644 smarttv-client/Images/keypad/kp-button-inv.png create mode 100644 smarttv-client/Images/keypad/kp-button.png create mode 100644 smarttv-client/Images/spinner/loadingBG.png create mode 100644 smarttv-client/Images/spinner/loading_1.png create mode 100644 smarttv-client/Images/spinner/loading_10.png create mode 100644 smarttv-client/Images/spinner/loading_11.png create mode 100644 smarttv-client/Images/spinner/loading_12.png create mode 100644 smarttv-client/Images/spinner/loading_2.png create mode 100644 smarttv-client/Images/spinner/loading_3.png create mode 100644 smarttv-client/Images/spinner/loading_4.png create mode 100644 smarttv-client/Images/spinner/loading_5.png create mode 100644 smarttv-client/Images/spinner/loading_6.png create mode 100644 smarttv-client/Images/spinner/loading_7.png create mode 100644 smarttv-client/Images/spinner/loading_8.png create mode 100644 smarttv-client/Images/spinner/loading_9.png diff --git a/smarttv-client/Images/helpbar/help_0_9.png b/smarttv-client/Images/helpbar/help_0_9.png new file mode 100644 index 0000000..309a3fe Binary files /dev/null and b/smarttv-client/Images/helpbar/help_0_9.png differ diff --git a/smarttv-client/Images/helpbar/help_back.png b/smarttv-client/Images/helpbar/help_back.png new file mode 100644 index 0000000..aff9672 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_back.png differ diff --git a/smarttv-client/Images/helpbar/help_blue.png b/smarttv-client/Images/helpbar/help_blue.png new file mode 100644 index 0000000..c0da080 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_blue.png differ diff --git a/smarttv-client/Images/helpbar/help_enter.png b/smarttv-client/Images/helpbar/help_enter.png new file mode 100644 index 0000000..7dcaa38 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_enter.png differ diff --git a/smarttv-client/Images/helpbar/help_ff_rew.png b/smarttv-client/Images/helpbar/help_ff_rew.png new file mode 100644 index 0000000..8d7140e Binary files /dev/null and b/smarttv-client/Images/helpbar/help_ff_rew.png differ diff --git a/smarttv-client/Images/helpbar/help_green.png b/smarttv-client/Images/helpbar/help_green.png new file mode 100644 index 0000000..1cb4725 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_green.png differ diff --git a/smarttv-client/Images/helpbar/help_ime.png b/smarttv-client/Images/helpbar/help_ime.png new file mode 100644 index 0000000..5cdf3c5 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_ime.png differ diff --git a/smarttv-client/Images/helpbar/help_info.png b/smarttv-client/Images/helpbar/help_info.png new file mode 100644 index 0000000..2cfeb3d Binary files /dev/null and b/smarttv-client/Images/helpbar/help_info.png differ diff --git a/smarttv-client/Images/helpbar/help_joy.png b/smarttv-client/Images/helpbar/help_joy.png new file mode 100644 index 0000000..0ded79a Binary files /dev/null and b/smarttv-client/Images/helpbar/help_joy.png differ diff --git a/smarttv-client/Images/helpbar/help_lr.png b/smarttv-client/Images/helpbar/help_lr.png new file mode 100644 index 0000000..f8841e3 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_lr.png differ diff --git a/smarttv-client/Images/helpbar/help_mode.png b/smarttv-client/Images/helpbar/help_mode.png new file mode 100644 index 0000000..d244fb8 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_mode.png differ diff --git a/smarttv-client/Images/helpbar/help_move.png b/smarttv-client/Images/helpbar/help_move.png new file mode 100644 index 0000000..4a03436 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_move.png differ diff --git a/smarttv-client/Images/helpbar/help_pre_ch.png b/smarttv-client/Images/helpbar/help_pre_ch.png new file mode 100644 index 0000000..92827d4 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_pre_ch.png differ diff --git a/smarttv-client/Images/helpbar/help_red.png b/smarttv-client/Images/helpbar/help_red.png new file mode 100644 index 0000000..43e2500 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_red.png differ diff --git a/smarttv-client/Images/helpbar/help_size.png b/smarttv-client/Images/helpbar/help_size.png new file mode 100644 index 0000000..08dd8ad Binary files /dev/null and b/smarttv-client/Images/helpbar/help_size.png differ diff --git a/smarttv-client/Images/helpbar/help_ud.png b/smarttv-client/Images/helpbar/help_ud.png new file mode 100644 index 0000000..df04b3c Binary files /dev/null and b/smarttv-client/Images/helpbar/help_ud.png differ diff --git a/smarttv-client/Images/helpbar/help_user.png b/smarttv-client/Images/helpbar/help_user.png new file mode 100644 index 0000000..bcd2fa1 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_user.png differ diff --git a/smarttv-client/Images/helpbar/help_video.png b/smarttv-client/Images/helpbar/help_video.png new file mode 100644 index 0000000..f029be2 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_video.png differ diff --git a/smarttv-client/Images/helpbar/help_video2.png b/smarttv-client/Images/helpbar/help_video2.png new file mode 100644 index 0000000..efb1fd1 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_video2.png differ diff --git a/smarttv-client/Images/helpbar/help_vod.png b/smarttv-client/Images/helpbar/help_vod.png new file mode 100644 index 0000000..47cdace Binary files /dev/null and b/smarttv-client/Images/helpbar/help_vod.png differ diff --git a/smarttv-client/Images/helpbar/help_yellow.png b/smarttv-client/Images/helpbar/help_yellow.png new file mode 100644 index 0000000..b62fae7 Binary files /dev/null and b/smarttv-client/Images/helpbar/help_yellow.png differ diff --git a/smarttv-client/Images/helpbar/keyhelp_black.png b/smarttv-client/Images/helpbar/keyhelp_black.png new file mode 100644 index 0000000..fd3f143 Binary files /dev/null and b/smarttv-client/Images/helpbar/keyhelp_black.png differ diff --git a/smarttv-client/Images/helpbar/mute.png b/smarttv-client/Images/helpbar/mute.png new file mode 100644 index 0000000..a215cc0 Binary files /dev/null and b/smarttv-client/Images/helpbar/mute.png differ diff --git a/smarttv-client/Images/helpbar/mute2.png b/smarttv-client/Images/helpbar/mute2.png new file mode 100644 index 0000000..e1c63be Binary files /dev/null and b/smarttv-client/Images/helpbar/mute2.png differ diff --git a/smarttv-client/Images/helpbar/tools.png b/smarttv-client/Images/helpbar/tools.png new file mode 100644 index 0000000..f74d00a Binary files /dev/null and b/smarttv-client/Images/helpbar/tools.png differ diff --git a/smarttv-client/Images/helpbar/volume.png b/smarttv-client/Images/helpbar/volume.png new file mode 100644 index 0000000..aa374fe Binary files /dev/null and b/smarttv-client/Images/helpbar/volume.png differ diff --git a/smarttv-client/Images/helpbar/web.png b/smarttv-client/Images/helpbar/web.png new file mode 100644 index 0000000..e3a5739 Binary files /dev/null and b/smarttv-client/Images/helpbar/web.png differ diff --git a/smarttv-client/Images/keypad/kp-button-inv.png b/smarttv-client/Images/keypad/kp-button-inv.png new file mode 100644 index 0000000..a42eef7 Binary files /dev/null and b/smarttv-client/Images/keypad/kp-button-inv.png differ diff --git a/smarttv-client/Images/keypad/kp-button.png b/smarttv-client/Images/keypad/kp-button.png new file mode 100644 index 0000000..697f295 Binary files /dev/null and b/smarttv-client/Images/keypad/kp-button.png differ diff --git a/smarttv-client/Images/spinner/loadingBG.png b/smarttv-client/Images/spinner/loadingBG.png new file mode 100644 index 0000000..5ef6f93 Binary files /dev/null and b/smarttv-client/Images/spinner/loadingBG.png differ diff --git a/smarttv-client/Images/spinner/loading_1.png b/smarttv-client/Images/spinner/loading_1.png new file mode 100644 index 0000000..20f8fd1 Binary files /dev/null and b/smarttv-client/Images/spinner/loading_1.png differ diff --git a/smarttv-client/Images/spinner/loading_10.png b/smarttv-client/Images/spinner/loading_10.png new file mode 100644 index 0000000..b331ecb Binary files /dev/null and b/smarttv-client/Images/spinner/loading_10.png differ diff --git a/smarttv-client/Images/spinner/loading_11.png b/smarttv-client/Images/spinner/loading_11.png new file mode 100644 index 0000000..21593c2 Binary files /dev/null and b/smarttv-client/Images/spinner/loading_11.png differ diff --git a/smarttv-client/Images/spinner/loading_12.png b/smarttv-client/Images/spinner/loading_12.png new file mode 100644 index 0000000..0d26c10 Binary files /dev/null and b/smarttv-client/Images/spinner/loading_12.png differ diff --git a/smarttv-client/Images/spinner/loading_2.png b/smarttv-client/Images/spinner/loading_2.png new file mode 100644 index 0000000..1099ae6 Binary files /dev/null and b/smarttv-client/Images/spinner/loading_2.png differ diff --git a/smarttv-client/Images/spinner/loading_3.png b/smarttv-client/Images/spinner/loading_3.png new file mode 100644 index 0000000..e9ed7cb Binary files /dev/null and b/smarttv-client/Images/spinner/loading_3.png differ diff --git a/smarttv-client/Images/spinner/loading_4.png b/smarttv-client/Images/spinner/loading_4.png new file mode 100644 index 0000000..8e792d5 Binary files /dev/null and b/smarttv-client/Images/spinner/loading_4.png differ diff --git a/smarttv-client/Images/spinner/loading_5.png b/smarttv-client/Images/spinner/loading_5.png new file mode 100644 index 0000000..a328995 Binary files /dev/null and b/smarttv-client/Images/spinner/loading_5.png differ diff --git a/smarttv-client/Images/spinner/loading_6.png b/smarttv-client/Images/spinner/loading_6.png new file mode 100644 index 0000000..4709247 Binary files /dev/null and b/smarttv-client/Images/spinner/loading_6.png differ diff --git a/smarttv-client/Images/spinner/loading_7.png b/smarttv-client/Images/spinner/loading_7.png new file mode 100644 index 0000000..160327d Binary files /dev/null and b/smarttv-client/Images/spinner/loading_7.png differ diff --git a/smarttv-client/Images/spinner/loading_8.png b/smarttv-client/Images/spinner/loading_8.png new file mode 100644 index 0000000..c4abe4b Binary files /dev/null and b/smarttv-client/Images/spinner/loading_8.png differ diff --git a/smarttv-client/Images/spinner/loading_9.png b/smarttv-client/Images/spinner/loading_9.png new file mode 100644 index 0000000..5959995 Binary files /dev/null and b/smarttv-client/Images/spinner/loading_9.png differ -- cgit v1.2.3 From f92e97207c9f6fc740bf4960049758a5a36d6898 Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 13 Jan 2013 21:28:52 +0100 Subject: Various changes: Folder Support for Live, Spinner, Keyboard, etc --- smarttv-client/CSS/Main.css | 17 +- smarttv-client/Images/leftHalf-bg.png | Bin 138277 -> 155643 bytes smarttv-client/Images/rightHalf-bg.png | Bin 124624 -> 140790 bytes smarttv-client/Javascript/Config.js | 45 ++-- smarttv-client/Javascript/Data.js | 32 +++ smarttv-client/Javascript/Display.js | 75 ++++--- smarttv-client/Javascript/Epg.js | 41 ++++ smarttv-client/Javascript/Helpbar.js | 58 ++++++ smarttv-client/Javascript/Main.js | 170 +++++++-------- smarttv-client/Javascript/Options.js | 367 ++++++++++++++++++++++++++++----- smarttv-client/Javascript/Player.js | 40 ++-- smarttv-client/Javascript/Server.js | 49 +++-- smarttv-client/Javascript/Spinner.js | 51 +++++ smarttv-client/config.xml | 2 +- smarttv-client/index.html | 23 ++- 15 files changed, 742 insertions(+), 228 deletions(-) create mode 100644 smarttv-client/Javascript/Epg.js create mode 100644 smarttv-client/Javascript/Helpbar.js create mode 100644 smarttv-client/Javascript/Spinner.js diff --git a/smarttv-client/CSS/Main.css b/smarttv-client/CSS/Main.css index e3ebaca..ccc6714 100755 --- a/smarttv-client/CSS/Main.css +++ b/smarttv-client/CSS/Main.css @@ -66,7 +66,6 @@ body { position: absolute; left: 0px; top: 0px; width: 960px; height: 540px; - display: none; background: url("../Images/selectScreen-bg.png"); background: -webkit-radial-gradient(80% 80%, ellipse cover, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); } @@ -226,6 +225,9 @@ body { background: -webkit-linear-gradient(-45deg, #1e5799 0%,#2989d8 41%,#7db9e8 100%); } +.style_videoitem { + display: inline-block; +} .style_videoList { color:#FFFFFF; @@ -383,3 +385,16 @@ body { text-align:center; font-size:14px; } + + +#Spinning { + position:absolute; + left:401px; top:196px; + width:158px; height:147px; + background-image:url("../Images/spinner/loadingBG.png"); + background-repeat:no-repeat; + opacity:0.5; + display:none; + z-index:20; +} + diff --git a/smarttv-client/Images/leftHalf-bg.png b/smarttv-client/Images/leftHalf-bg.png index 0be1542..d91b9b6 100644 Binary files a/smarttv-client/Images/leftHalf-bg.png and b/smarttv-client/Images/leftHalf-bg.png differ diff --git a/smarttv-client/Images/rightHalf-bg.png b/smarttv-client/Images/rightHalf-bg.png index 824f3a8..9cb89ac 100644 Binary files a/smarttv-client/Images/rightHalf-bg.png and b/smarttv-client/Images/rightHalf-bg.png differ diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index 1aa2f75..a950641 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -64,17 +64,14 @@ Config.init = function () { // should switch to the config screen here var res = fileSystemObj.createCommonDir(curWidget.id); if (res == true) { - Config.firstLaunch = true; - - Main.init(); // Obsolete? - - Main.changeState(4); + Config.doFirstLaunch(); return; } else { - Main.init(); - Display.showPopup ("ERROR: Cannot create widget folder"); - Main.logToServer("ERROR: Cannot create widget folder curWidget.id= " +curWidget.id); + Config.doFirstLaunch(); +// Main.init(); + Display.showPopup ("WARNING: Cannot create widget folder. Try Config"); +// Main.logToServer("ERROR: Cannot create widget folder curWidget.id= " +curWidget.id); } return; } @@ -86,6 +83,13 @@ Config.init = function () { Config.fetchConfig(); }; +Config.doFirstLaunch = function () { + Config.firstLaunch = true; +// Main.init(); // Obsolete? + + Main.changeState(4); +}; + Config.fetchConfig = function () { if (this.XHRObj == null) { this.XHRObj = new XMLHttpRequest(); @@ -114,6 +118,7 @@ Config.writeContext = function (addr) { }; Config.updateContext = function (addr) { + Main.log("Config.updateContext with ("+addr+")"); var fileSystemObj = new FileSystem(); var fd = fileSystemObj.openCommonFile(Config.cfgFileName,"w"); @@ -123,6 +128,7 @@ Config.updateContext = function (addr) { Config.serverAddr = addr; Config.serverUrl = "http://" + Config.serverAddr; + Config.fetchConfig(); }; Config.readContext = function () { @@ -141,29 +147,28 @@ Config.readContext = function () { Config.serverUrl = "http://" + Config.serverAddr; } else { - Display.showPopup ("WARNING: Error in Config File. Try widget restart."); + Display.showPopup ("ERROR: Error in Config File. Try widget re-install."); // TODO: I should re-write the config file } } fileSystemObj.closeCommonFile(fd); - } catch (e) { - Main.logToServer("Config.readContext: Error while reading: e= " +e); + Main.log("Config.readContext: Error while reading: e= " +e); var res = fileSystemObj.createCommonDir(curWidget.id); if (res == true) { - Main.logToServer("Config.readContext: Widget Folder created"); - Display.showPopup ("Config Read Error: Try widget restart"); + Main.log("WARNING: ConfigRead Error. Launching Config-Menu from here"); + // Display.showPopup ("Config Read Error: Try widget restart"); + } else { - Main.logToServer("Config.readContext: Widget Folder creation failed"); - Display.showPopup ("Config Read Error: Try re-installing the widget"); - Main.log("-------------- Error: res = false ------------------------"); + Main.log("Config.readContext: Widget Folder creation failed"); + + Display.showPopup ("WARNING: ConfigRead Error and WidgetFolder creation failed.
Launching Config-Menu from here"); +// Main.log("-------------- Error: res = false ------------------------"); } + Config.doFirstLaunch(); - Config.firstLaunch = true; - Main.init(); - Main.changeState(4); } }; @@ -219,6 +224,8 @@ Config.processConfig = function () { return; } else { + Config.firstLaunch = false; + Main.log ("Parsing config XML now"); Main.logToServer("Parsing config XML now"); this.format = Config.getXmlString("format"); diff --git a/smarttv-client/Javascript/Data.js b/smarttv-client/Javascript/Data.js index e7049ee..2b95fa7 100755 --- a/smarttv-client/Javascript/Data.js +++ b/smarttv-client/Javascript/Data.js @@ -48,6 +48,10 @@ Data.dumpFolderStruct = function(){ Main.log("---------- dumpFolderStruct Done -------"); }; +Data.findEpgUpdateTime = function() { + return this.assets.findEpgUpdateTime(Display.GetEpochTime() + 10000, "", 0); + // min, guid, level +}; Data.getCurrentItem = function () { return this.folderList[this.folderList.length-1].item; }; @@ -123,6 +127,34 @@ Item.prototype.addChild = function (key, pyld, level) { } }; +Item.prototype.findEpgUpdateTime = function (min, guid, level) { + var prefix= ""; + for (var i = 0; i < level; i++) + prefix += "-"; + + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].isFolder == true) { + 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); + } + } + } + + return { "min": min, "guid" : guid}; +}; + Item.prototype.print = function(level) { var prefix= ""; for (var i = 0; i < level; i++) diff --git a/smarttv-client/Javascript/Display.js b/smarttv-client/Javascript/Display.js index 483a141..218f942 100755 --- a/smarttv-client/Javascript/Display.js +++ b/smarttv-client/Javascript/Display.js @@ -49,10 +49,22 @@ Display.init = function() } for (var i = 0; i <= this.LASTIDX; i++) { - var elm = document.getElementById("video"+i); + var elm = $("#video"+i); + $(elm).css({"width" : "100%", "text-align": "left" }); + $(elm).append($("
").css({ "display": "inline-block", "padding-top": "4px", "padding-bottom": "6px", "width":"20%"})); + $(elm).append($("
").css({ "display": "inline-block", "padding-top": "4px", "padding-bottom": "6px", "width":"70%"})); + $(elm).append($("
").css({ "display": "inline-block", "padding-top": "4px", "padding-bottom": "6px", "width":"5%"})); + +/* $(elm).children("div").eq(0).text ("Hallo1"); + $(elm).children("div").eq(1).text ("Hallo2"); + $(elm).children("div").eq(2).text ("Hallo3"); +*/ +/* + * var elm = document.getElementById("video"+i); elm.style.paddingLeft = "10px"; elm.style.paddingTop = "4px"; elm.style.paddingBottom = "6px"; + */ } var done = false; @@ -76,8 +88,15 @@ Display.init = function() return success; }; +Display.setVideoItem = function (elm, cnt) { + // cnt + $(elm).children("div").eq(0).text (cnt.c1); + $(elm).children("div").eq(1).text (cnt.c2); + $(elm).children("div").eq(2).text (cnt.c3); +}; + Display.putInnerHTML = function (elm, val) { - + alert(Config.deviceType + " " +elm + " " + val); switch (Config.deviceType) { case 0: // Samsung specific handling of innerHtml @@ -133,7 +152,7 @@ Display.resetVideoList = function () { break; } Display.unselectItem(elm); - Display.putInnerHTML(elm, ""); + Display.setVideoItem(elm, {c1: "", c2: "", c3: ""}); } }; @@ -276,7 +295,6 @@ Display.handleDescription =function (selected) { var min = Display.getNumString (digi.getMinutes(), 2); var d_str =""; -// Main.log("handleDescription: " +Data.getCurrentItem().childs[selected].payload.desc); var msg = ""; if (Main.state == 1) { // Live @@ -326,19 +344,24 @@ Display.getNumString =function(num, fmt) { }; Display.getDisplayTitle = function(item) { - var res = ""; + var res = {c1:"", c2:"", c3:""}; switch (Main.state) { case 1: // Live - res = item.title; + if (item.isFolder == true) { + res.c2 = item.title; + res.c3 = "<" + Display.getNumString(item.childs.length, 2) +">"; + } + else { + res.c2 = item.title; + } break; case 2: case 3: // Recordings if (item.isFolder == true) { -// res = "<" + Display.getNumString(item.childs.length, 3) + ">-------- " + item.title; - res = "<" + Display.getNumString(item.childs.length, 3) + ">       - " + item.title; - + res.c1 = "<" + Display.getNumString(item.childs.length, 3) + ">"; + res.c2 = item.title; } else { var digi = new Date(parseInt(item.payload.start*1000)); @@ -348,7 +371,8 @@ Display.getDisplayTitle = function(item) { var min = Display.getNumString (digi.getMinutes(), 2); var d_str = mon + "/" + day + " " + hour + ":" + min; - res = d_str + " - " + item.title; + res.c1 = d_str; + res.c2 = item.title; } break; default: @@ -361,7 +385,9 @@ Display.getDisplayTitle = function(item) { Display.setVideoList = function(selected, first) { // var listHTML = ""; - var first_item = selected; + var res = {}; +// var first_item = selected; + var first_item = first; //thlo var i=0; Main.log("Display.setVideoList title= " +Data.getCurrentItem().childs[selected].title + " selected= " + selected + " first_item= " + first_item); @@ -369,15 +395,14 @@ Display.setVideoList = function(selected, first) { for (i = 0; i <= this.LASTIDX; i++) { if ((first_item+i) >= Data.getVideoCount()) { - listHTML = ""; + res = {c1: "", c2: "", c3: ""}; } else { - listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[first_item+i]); -// Main.log(" - title[first_item+i]= " +Data.getCurrentItem().childs[(first_item +i)].title + " i= " + i + " listHTML= " + listHTML); -// Main.log(" - listHTML= " + listHTML); + res = Display.getDisplayTitle (Data.getCurrentItem().childs[first_item+i]); } this.videoList[i] = document.getElementById("video"+i); - Display.putInnerHTML(this.videoList[i], listHTML); + + Display.setVideoItem(this.videoList[i], res); this.unselectItem(this.videoList[i]); } @@ -414,6 +439,7 @@ Display.unselectItem = function (item) { Display.setVideoListPosition = function(position, move) { var listHTML = ""; +// var res = {}; //thlo: unused? Main.log ("Display.setVideoListPosition title= " +Data.getCurrentItem().childs[position].title + " move= " +move); this.handleDescription(position); @@ -454,9 +480,7 @@ Display.setVideoListPosition = function(position, move) this.currentWindow = this.FIRSTIDX; for(i = 0; i <= this.LASTIDX; i++) { - listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i]); -// listHTML = Data.getCurrentItem().childs[i].title; - Display.putInnerHTML(this.videoList[i], listHTML); + Display.setVideoItem(this.videoList[i], Display.getDisplayTitle (Data.getCurrentItem().childs[i])); if(i == this.currentWindow) this.selectItem(this.videoList[i]); @@ -466,9 +490,7 @@ Display.setVideoListPosition = function(position, move) } else { for(i = 0; i <= this.LASTIDX; i++) { - listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position - this.currentWindow]); -// listHTML = Data.getCurrentItem().childs[i + position - this.currentWindow].title; - Display.putInnerHTML(this.videoList[i], listHTML); + Display.setVideoItem(this.videoList[i], Display.getDisplayTitle (Data.getCurrentItem().childs[i + position - this.currentWindow])); } } } @@ -479,10 +501,7 @@ Display.setVideoListPosition = function(position, move) this.currentWindow = this.LASTIDX; for(i = 0; i <= this.LASTIDX; i++) { - listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position - this.currentWindow]); -// listHTML = Data.getCurrentItem().childs[i + position - this.currentWindow].title; - Display.putInnerHTML(this.videoList[i], listHTML); - + Display.setVideoItem(this.videoList[i], Display.getDisplayTitle (Data.getCurrentItem().childs[i + position - this.currentWindow])); if(i == this.currentWindow) this.selectItem(this.videoList[i]); else @@ -491,9 +510,7 @@ Display.setVideoListPosition = function(position, move) } else { for(i = 0; i <= this.LASTIDX; i++) { - listHTML = Display.getDisplayTitle (Data.getCurrentItem().childs[i + position]); -// listHTML = Data.getCurrentItem().childs[i + position].title; - Display.putInnerHTML(this.videoList[i], listHTML); + Display.setVideoItem(this.videoList[i], Display.getDisplayTitle (Data.getCurrentItem().childs[i + position])); } } } diff --git a/smarttv-client/Javascript/Epg.js b/smarttv-client/Javascript/Epg.js new file mode 100644 index 0000000..c400e0e --- /dev/null +++ b/smarttv-client/Javascript/Epg.js @@ -0,0 +1,41 @@ +/* + * Principle: One monitor for each channel + * Nur max 20 + * Oder einer: + * Search once through all entries and look for the lowest expiry. + * Set a timer to update the entry + * check, whether the entry is "on screen" + * +*/ + +var Epg = { + restfulUrl : "" + +}; + +// Should be called after initial config +Epg.init = function () { + if (Config.serverUrl == "") + return; + if (Config.serverUrl.indexOf(':') != -1) { + Main.log ("Epg: Serverurl= " + Config.serverUrl); + this.restfulUrl = Config.serverUrl.splice(0, Config.serverUrl.indexOf(':')) + ":8002"; + } + + Main.log ("Restful API Url= "+ this.restfulUrl); + + $.ajax({ + type: "HEAD", + async: true, + url: this.restfulUrl + "channels.xml", + success: function(message,text,response){ + Main.log("AJAX Response: MSG= " + message + " txt= " + text + " resp= " + response); + } + }); +}; + +Epg.startEpgUpdating = function() { + + var res = Data.findEpgUpdateTime(); + Main.log("GUID= " + res.guid + " Min= " + res.min); +}; \ No newline at end of file diff --git a/smarttv-client/Javascript/Helpbar.js b/smarttv-client/Javascript/Helpbar.js new file mode 100644 index 0000000..0196d78 --- /dev/null +++ b/smarttv-client/Javascript/Helpbar.js @@ -0,0 +1,58 @@ + +var Helpbar = { + isInited : false +}; + +Helpbar.init = function () { + if (this.isInited == false) { + Helpbar.createHelpbar(); + Helpbar.hide(); + } +}; + +Helpbar.show = function () { + $("#helpbar").show(); +}; + +Helpbar.hide = function () { + $("#helpbar").hide(); +}; + +Helpbar.createHelpbar = function() { + this.isInited = true; + var sheet = $(" - - - -
- -
-

Recordings

-
- -
-
- - - -
-
-
-
- - - diff --git a/vdr-smarttvweb/widget.conf b/vdr-smarttvweb/widget.conf deleted file mode 100644 index da4a006..0000000 --- a/vdr-smarttvweb/widget.conf +++ /dev/null @@ -1,11 +0,0 @@ - - -hls -6000000 -40 -5 -5 -5 -30 -20 - diff --git a/web/Data.js b/web/Data.js new file mode 100755 index 0000000..49e9026 --- /dev/null +++ b/web/Data.js @@ -0,0 +1,296 @@ +//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}); +// Main.log("Data.reset: folderList.push. this.folderList.length= " + this.folderList.length); +}; + +Data.completed= function(sort) { + if (sort == true) + this.assets.sortPayload(); + + this.folderList.push({item : this.assets, id: 0}); +// Main.log("Data.completed: folderList.push. this.folderList.length= " + this.folderList.length); +// Main.log ("Data.completed()= " +this.folderList.length); + +}; + +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.addItem = function(t_list, pyld) { + this.assets.addChild(t_list, pyld, 0); +}; + +Data.dumpFolderStruct = function(){ + Main.log("---------- dumpFolderStruct ------------"); + this.assets.print(0); + Main.log("---------- dumpFolderStruct Done -------"); +}; + +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() { + return this.folderList[this.folderList.length-1].item.childs.length; +}; + +Data.getNumString =function(num, fmt) { + var res = ""; + + if (num < 10) { + for (var i = 1; i < fmt; i ++) { + res += "0"; + }; + } else if (num < 100) { + for (var i = 2; i < fmt; i ++) { + res += "0"; + }; + } + + res = res + num; + + return res; +}; + +Data.deleteElm = function (pos) { + Data.getCurrentItem().childs.remove(pos); +}; +//----------------------------------------- +function Item() { + this.title = "root"; + this.isFolder = true; + this.childs = []; + this.payload = ""; // only set, if (isFolder == false) +} + +Item.prototype.isFolder = function() { + return this.isFolder; +}; + +Item.prototype.getTitle = function () { + return this.title; +}; + +Item.prototype.getPayload = function () { + if (this.isFolder == true) { + Main.log("WARNING: getting payload on a folder title=" +this.title); + } + return this.payload; +}; + +Item.prototype.getItem = function (title) { + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].title == title) { + return this.childs[i]; + } + } + return 0; +}; + +Item.prototype.addChild = function (key, pyld, level) { + if (key.length == 1) { + var folder = new Item; +// folder.title = pyld.startstr + " - " + key; + folder.title = key[0]; + folder.payload = pyld; + folder.isFolder = false; + this.childs.push(folder); +// this.titles.push({title: pyld.startstr + " - " + key , pyld : pyld}); + } + else { + if (level > 10) { + Main.log(" too many levels"); + return; + } + var t = key.shift(); + var found = false; + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].title == t) { + this.childs[i].addChild(key, pyld, level +1); + found = true; + break; + } + } + if (found == false) { + var folder = new Item; + folder.title = t; + folder.addChild(key, pyld, level+1); + this.childs.push(folder); + } + } +}; + +Item.prototype.findEpgUpdateTime = function (min, guid, level) { + var prefix= ""; + for (var i = 0; i < level; i++) + prefix += "-"; + + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].isFolder == true) { + 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.updateEpgEntry = function (guid, entry, level) { + var prefix= ""; + for (var i = 0; i < level; i++) + prefix += "-"; + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].isFolder == true) { + var res = this.childs[i].updateEpgEntry(guid, entry, level+1); + if (res == true) + return true; + } + 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 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) { + var mydiv = $('
    '); +// if (level == 0) { + mydiv.attr('data-role', 'listview'); + mydiv.attr('data-inset', 'true'); +// }; +//, id:'dyncreated' + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].isFolder == true) { + var myh = $('
    ', {text: this.childs[i].title}); + var myli = $('
  • '); + myli.append(myh); + var mycount = $('

    ', {class: 'ui-li-count', text: this.childs[i].childs.length}); + myli.append(mycount); + myli.append(this.childs[i].createJQMDomTree(level+1)); + mydiv.append(myli); + } + else { + // Links + var digi = new Date(this.childs[i].payload['start'] *1000); + var mon = Data.getNumString ((digi.getMonth()+1), 2); + var day = Data.getNumString (digi.getDate(), 2); + var hour = Data.getNumString (digi.getHours(), 2); + var min = Data.getNumString (digi.getMinutes(), 2); + + var d_str = mon + "/" + day + " " + hour + ":" + min; + var mya = $('', { text: d_str + " - " + this.childs[i].title, + href: this.childs[i].payload['link'], + rel: 'external'} ); + var myli = $('

  • '); + myli.attr('data-icon', 'false'); + myli.attr('data-theme', 'c'); + + myli.append(mya); + mydiv.append(myli); + } + } + return mydiv; +}; + +Item.prototype.sortPayload = function() { + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].isFolder == true) { + this.childs[i].sortPayload(); + } + } + this.childs.sort(function(a,b) { + if (a.title == b.title) { + return (b.payload.start - a.payload.start); + } + else { + return ((a.title < b.title) ? -1 : 1); + } + }); +}; + diff --git a/web/Server.js b/web/Server.js new file mode 100755 index 0000000..1ab6d0a --- /dev/null +++ b/web/Server.js @@ -0,0 +1,180 @@ +var Server = { + dataReceivedCallback : null, + errorCallback : null, + doSort : false, + retries : 0, + + XHRObj : null +}; + +Server.init = function() +{ + var success = true; + + if (this.XHRObj) { + this.XHRObj.destroy(); + this.XHRObj = null; + } + + return success; +}; + +Server.setSort = function (val) { + this.doSort = val; +}; +//--------------------------------------------- +Server.fetchVideoList = function(url) { + + $.ajax({ + url: url, + type : "GET", + success : function(data, status, XHR ) { +// Main.log("Server.fetchVideoList Success Response - status= " + status + " mime= " + XHR.responseType + " data= "+ data); + + $(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(); + } + + }, + 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"); + } + + } + }); +}; + + +//--------------------------------------------- + +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); + } + }); +}; diff --git a/web/favicon.ico b/web/favicon.ico new file mode 100755 index 0000000..2236569 Binary files /dev/null and b/web/favicon.ico differ diff --git a/web/index.html b/web/index.html new file mode 100755 index 0000000..f396ed4 --- /dev/null +++ b/web/index.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + +
    + +
    +

    Recordings

    +
    + +
    + + + diff --git a/widget.conf b/widget.conf new file mode 100644 index 0000000..da4a006 --- /dev/null +++ b/widget.conf @@ -0,0 +1,11 @@ + + +hls +6000000 +40 +5 +5 +5 +30 +20 + -- cgit v1.2.3 From 73c9e696423fa6826d381b6beb9cf2f3f39852ce Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 17 Mar 2013 09:48:39 +0100 Subject: Change VDRDIR in Makefile. First version of new Makefile --- Makefile | 6 +-- Makefile.new | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 Makefile.new diff --git a/Makefile b/Makefile index b04d143..bc78cfa 100644 --- a/Makefile +++ b/Makefile @@ -25,10 +25,8 @@ endif ### The directory environment: -#VDRDIR = ../../.. -VDRDIR = /usr/include/vdr -LIBDIR = . -#LIBDIR = ../../lib +VDRDIR = ../../.. +LIBDIR = ../../lib TMPDIR = /tmp ### Allow user defined options to overwrite defaults: diff --git a/Makefile.new b/Makefile.new new file mode 100644 index 0000000..3c3d769 --- /dev/null +++ b/Makefile.new @@ -0,0 +1,122 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id: Makefile 2.18 2013/01/12 13:45:01 kls Exp $ + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. + +PLUGIN = smarttvweb + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The directory environment: + +# Use package data if installed...otherwise assume we're under the VDR source directory: +PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc)) +LIBDIR = $(call PKGCFG,libdir) +LOCDIR = $(call PKGCFG,locdir) +PLGCFG = $(call PKGCFG,plgcfg) +# +TMPDIR ?= /tmp + +### The compiler options: + +export CFLAGS = $(call PKGCFG,cflags) +export CXXFLAGS = $(call PKGCFG,cxxflags) + +### The version number of VDR's plugin API: + +APIVERSION = $(call PKGCFG,apiversion) + +### Allow user defined options to overwrite defaults: + +-include $(PLGCFG) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### The name of the shared object file: + +SOFILE = libvdr-$(PLUGIN).so + +### Includes and Defines (add further entries here): + +INCLUDES += + +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +### The object files (add further files here): + +OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o log.o url.o stvw_cfg.o + +### The main target: + +all: $(SOFILE) i18n + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< + +### Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Internationalization (I18N): + +PODIR = po +I18Npo = $(wildcard $(PODIR)/*.po) +I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) +I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) +I18Npot = $(PODIR)/$(PLUGIN).pot + +%.mo: %.po + msgfmt -c -o $@ $< + +$(I18Npot): $(wildcard *.c) + xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='' -o $@ `ls $^` + +%.po: $(I18Npot) + msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $< + @touch $@ + +$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo + install -D -m644 $< $@ + +.PHONY: i18n +i18n: $(I18Nmo) $(I18Npot) + +install-i18n: $(I18Nmsgs) + +### Targets: + +$(SOFILE): $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@ + +install-lib: $(SOFILE) + install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION) + +install: install-lib install-i18n + +dist: $(I18Npo) clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tgz + +clean: + @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot + @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ -- cgit v1.2.3 From ec487342e840d92dbe556a5cede0073d11fb2862 Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 17 Mar 2013 11:34:56 +0100 Subject: Widget v0.92: * Widget version number on select screen. * Configurable timeout for direct access. * Direct access takes first digit. * Pause-Key toggles between pause and play. * First version of info overlay. * Bug fixes. --- smarttv-client/CSS/Main.css | 15 +++ smarttv-client/Javascript/Config.js | 161 +++++++----------------------- smarttv-client/Javascript/DirectAccess.js | 4 +- smarttv-client/Javascript/Display.js | 136 +++++++++++++++++++++---- smarttv-client/Javascript/Lang.js | 12 +++ smarttv-client/Javascript/Main.js | 157 ++++++++++++++++++----------- smarttv-client/Javascript/Player.js | 6 +- smarttv-client/Javascript/Timers.js | 64 ++++++++++++ smarttv-client/config.xml | 2 +- smarttv-client/index.html | 32 +++++- 10 files changed, 381 insertions(+), 208 deletions(-) create mode 100644 smarttv-client/Javascript/Lang.js create mode 100644 smarttv-client/Javascript/Timers.js diff --git a/smarttv-client/CSS/Main.css b/smarttv-client/CSS/Main.css index b733c20..ca16181 100755 --- a/smarttv-client/CSS/Main.css +++ b/smarttv-client/CSS/Main.css @@ -136,6 +136,21 @@ body { } +#infoOverlay{ + position: absolute; + display: none; + left: 0px; top: 0px; + width: 960px; height: 30%; + border-style:solid; + z-index:10; + padding-top:20px; + padding-left:20px; + padding-right:20px; + background-color: darkblue; + background-color: rgba(0, 0, 139, 0.5); + background-color: -webkit-linear-gradient(-45deg, rgba(167,207,223,1) 0%,rgba(35,83,138,1) 100%); +} + #popup{ position: absolute; display: none; diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index b60d01d..38bed1c 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -17,7 +17,8 @@ var Config = { debug : false, usePdlForRecordings : true, uploadJsFile : "", - + directAcessTimeout : 1500, + widgetVersion : "unknown", deviceType : 0 // Used to differentiate between browsers and platforms // 0: Samsung @@ -72,8 +73,9 @@ Config.init = function () { } else { Config.doFirstLaunch(); -// Main.init(); - Display.showPopup ("WARNING: Cannot create widget folder. Try Config"); +// Display.showPopup ("WARNING: Cannot create widget folder. Try Config"); + Display.showPopup (Lang[Lang.sel].configInit); + // Main.logToServer("ERROR: Cannot create widget folder curWidget.id= " +curWidget.id); } return; @@ -88,35 +90,33 @@ Config.init = function () { Config.doFirstLaunch = function () { Config.firstLaunch = true; -// Main.init(); // Obsolete? Main.changeState(4); }; -/* -Config.fetchConfig = function () { - if (this.XHRObj == null) { - this.XHRObj = new XMLHttpRequest(); - } - - if (this.XHRObj) { - - this.XHRObj.onreadystatechange = function() { - if (Config.XHRObj.readyState == 4) { - Config.processConfig(); - } - }; - - this.XHRObj.open("GET", this.serverUrl + "/widget.conf", true); - this.XHRObj.send(null); - } + +Config.getWidgetVersion = function () { + $.ajax({ + url: "config.xml", + type : "GET", + success : function(data, status, XHR ) { + Config.widgetVersion = $(data).find('ver').text(); + Main.logToServer("Config.getWidgetVersion= " + Config.widgetVersion) ; + Display.updateWidgetVersion(Config.widgetVersion); + }, + error : function (XHR, status, error) { + Main.log("Config.getVersion ERROR" ) ; + Main.logToServer("Config.getVersion ERROR" ) ; + } + }); }; -*/ + Config.fetchConfig = function () { $.ajax({ url: this.serverUrl + "/widget.conf", type : "GET", success : function(data, status, XHR ) { + Main.log ("Parsing config XML now"); Main.logToServer("Parsing config XML now"); Config.format = $(data).find('format').text(); @@ -132,8 +132,12 @@ Config.fetchConfig = function () { Config.debug = ($(data).find('widgetdebug').text() == "true") ? true : false; Config.usePdlForRecordings = ($(data).find('usePdlForRecordings').text() == "false") ? false : true; Config.uploadJsFile = $(data).find('uploadJsFile').text(); - + Config.directAcessTimeout = $(data).find('directAcessTimeout').text(); + Player.skipDuration = Config.skipDuration; + if (Config.directAcessTimeout != "") { + DirectAccess.delay = Config.directAcessTimeout; + } Main.log("**** Config ****"); Main.log("serverUrl= " + Config.serverUrl); Main.log("format= " + Config.format); @@ -153,7 +157,9 @@ Config.fetchConfig = function () { }, error : function (XHR, status, error) { Main.log ("Config Server Error"); - Display.showPopup("Config Server Error " + XHR.status + " " + status); +// Display.showPopup("Config Server Error " + XHR.status + " " + status); + Display.showPopup(Lang[Lang.sel].configNoServer + " "+ XHR.status + " " + status); + Main.logToServer("Config Server Error " + XHR.status + " " + status); } @@ -194,13 +200,14 @@ Config.readContext = function () { while (line = fd.readLine()) { var avp = line.split(" "); -// Display.showPopup ("Reading Config: attr= " + avp[0] + " val= " + avp[1]); if (avp.length > 1) { Config.serverAddr = avp[1]; Config.serverUrl = "http://" + Config.serverAddr; } else { - Display.showPopup ("ERROR: Error in Config File. Try widget re-install."); +// Display.showPopup ("ERROR: Error in Config File. Try widget re-install."); + Display.showPopup (Lang[Lang.sel].configRead1); + // TODO: I should re-write the config file } } @@ -217,7 +224,9 @@ Config.readContext = function () { else { Main.log("Config.readContext: Widget Folder creation failed"); - Display.showPopup ("WARNING: ConfigRead Error and WidgetFolder creation failed.
    Launching Config-Menu from here"); +// Display.showPopup ("WARNING: ConfigRead Error and WidgetFolder creation failed.
    Launching Config-Menu from here"); + Display.showPopup (Lang[Lang.sel].configRead2); + // Main.log("-------------- Error: res = false ------------------------"); } Config.doFirstLaunch(); @@ -225,105 +234,7 @@ Config.readContext = function () { } }; -/* -Config.getXmlValue = function (itm) { - var val = this.xmlDocument.getElementsByTagName(itm); - var res = 0; - try { - res = val[0].firstChild.data; - } - catch (e) { - Main.logToServer("parsing widget.conf: Item= " + itm + " not found" + e); - Main.log ("parsing widget.conf: Item= " + itm + " not found e= " + e); - } - return res; - -}; -Config.getXmlString = function (itm) { - var val = this.xmlDocument.getElementsByTagName(itm); - - var res = ""; - try { - res = val[0].firstChild.data; - } - catch (e) { - Main.logToServer("parsing widget.conf: Item= " + itm + " not found" + e); - Main.log ("parsing widget.conf: Item= " + itm + " not found e= " + e); - }; - - return res; -}; -*/ -/* -Config.processConfig = function () { - if (this.XHRObj.status != 200) { - Main.log ("Config Server Error"); - Display.showPopup("Config Server Error " + this.XHRObj.status); - Main.logToServer("Config Server Error " + this.XHRObj.status); - } - else { - var xmlResponse = this.XHRObj.responseXML; - if (xmlResponse == null) { - Main.log ("xml error"); - Display.showPopup("Error in XML Config File"); - Main.logToServer("Error in XML Config File"); - return; - } - this.xmlDocument = xmlResponse.documentElement; - if (!this.xmlDocument ) { - Main.log("Failed to get valid Config XML"); - Display.showPopup("Failed to get valid Config XML"); - Main.logToServer("Failed to get valid Config XML"); - return; - } - else { - Config.firstLaunch = false; - - Main.log ("Parsing config XML now"); - Main.logToServer("Parsing config XML now"); - this.format = Config.getXmlString("format"); - var res = Config.getXmlValue("tgtBufferBitrate"); - if (res != 0) - this.tgtBufferBitrate = 1.0 * res; - res = Config.getXmlValue("totalBufferDuration"); - if (res != 0) this.totalBufferDuration = 1.0 * res; - - res= Config.getXmlValue("initialBuffer"); - if (res != 0) this.initialBuffer = 1.0 * res; - - res = Config.getXmlValue("pendingBuffer"); - if (res != 0) this.pendingBuffer = 1.0 * res; - - res = Config.getXmlValue("skipDuration"); - if (res != 0) this.skipDuration= 1.0 * res; - - res = Config.getXmlValue("initialTimeOut"); - if (res != 0) this.initialTimeOut = 1.0 * res; - - res = Config.getXmlValue("liveChannels"); - if (res != 0) this.liveChannels = res; - - Player.skipDuration = Config.skipDuration; - Main.log("**** Config ****"); - Main.log("serverUrl= " + Config.serverUrl); - Main.log("format= " + Config.format); - Main.log("tgtBufferBitrate= " + Config.tgtBufferBitrate); - Main.log("totalBufferDuration= " + Config.totalBufferDuration); - Main.log("initialBuffer= " + Config.initialBuffer); - Main.log("pendingBuffer= " + Config.pendingBuffer); - Main.log("skipDuration= " + Config.skipDuration); - Main.log("initialTimeOut= " + Config.initialTimeOut); - Main.log("liveChannels= " + Config.liveChannels); - - Main.log("**** /Config ****"); - }; - - }; - - Main.init(); -}; -*/ // This function cleans up after un-installation Config.reset = function () { var fileSystemObj = new FileSystem(); diff --git a/smarttv-client/Javascript/DirectAccess.js b/smarttv-client/Javascript/DirectAccess.js index 418a5c6..2406f89 100644 --- a/smarttv-client/Javascript/DirectAccess.js +++ b/smarttv-client/Javascript/DirectAccess.js @@ -82,7 +82,9 @@ DirectAccess.init = function (){ } }; -DirectAccess.show = function () { +DirectAccess.show = function (val) { + Main.log("DirectAccess.show " + val); + $("#directAccessText").text(val); $("#directChanAccess").show(); $("#directAccessAnchor").focus(); DirectAccess.timeout = Display.GetEpochTime() + (DirectAccess.delay / 1000.0); diff --git a/smarttv-client/Javascript/Display.js b/smarttv-client/Javascript/Display.js index 8d10024..66d22dd 100755 --- a/smarttv-client/Javascript/Display.js +++ b/smarttv-client/Javascript/Display.js @@ -16,6 +16,7 @@ var Display = volOlHandler : null, progOlHandler : null, popupOlHandler : null, + infoOlHandler : null, videoList : new Array() }; @@ -40,9 +41,11 @@ Display.init = function() this.progOlHandler = new OverlayHandler("ProgHndl"); this.volOlHandler = new OverlayHandler("VolHndl"); this.popupOlHandler = new OverlayHandler("PopupHndl"); + this.infoOlHandler = new OverlayHandler("InfoHndl"); this.progOlHandler.init(Display.handlerShowProgress, Display.handlerHideProgress); this.volOlHandler.init(Display.handlerShowVolume, Display.handlerHideVolume); this.popupOlHandler.init(Display.handlerShowPopup, Display.handlerHidePopup); + this.infoOlHandler.init(Display.handlerShowInfo, Display.handlerHideInfo); if (!this.statusDiv) { success = false; @@ -74,12 +77,11 @@ Display.init = function() elm.style.marginBottom= " 5px"; elm.style.textAlign = "center"; } - + Display.resetDescription(); Main.log("Display initialized" ); return success; }; - Display.putInnerHTML = function (elm, val) { switch (Config.deviceType) { case 0: @@ -209,6 +211,11 @@ Display.resetSelectItems = function (itm) { Display.selectItem(document.getElementById("selectItem"+itm)); }; +Display.updateWidgetVersion = function (ver) { + + $("#widgetVersion").text(" Version "+ ver); +}; + /* * Video Select Screen Functions * @@ -251,6 +258,7 @@ Display.show = function() { this.volOlHandler.cancel(); this.progOlHandler.cancel(); this.popupOlHandler.cancel(); + this.infoOlHandler.cancel(); $("#main").show(); }; @@ -380,16 +388,23 @@ Display.resetVideoList = function () { }; -Display.resetDescription = function () { +/*Display.resetDescription = function () { $("#description").text(""); // reset }; +*/ //Video Select Screen Display.handleDescription =function (selected) { if (Data.getCurrentItem().childs[selected].isFolder == true) { - Display.setDescription( "Dir: " +Data.getCurrentItem().childs[selected].title ); + $("#descTitle").text("Dir: " +Data.getCurrentItem().childs[selected].title); +// Display.setDescription( "Dir: " +Data.getCurrentItem().childs[selected].title ); + $("#descProg").text(""); + $("#descStart").text(""); + $("#descDuration").text(""); + $("#descRemaining").text(""); + $("#descDesc").text(""); } else { var itm = Data.getCurrentItem().childs[selected]; @@ -405,43 +420,69 @@ Display.handleDescription =function (selected) { var min = Display.getNumString (digi.getMinutes(), 2); var d_str =""; - var msg = ""; +// var msg = ""; switch (Main.state) { case Main.eLIVE: var now = Display.GetEpochTime(); d_str = hour + ":" + min; - msg += title + "
    "; +/* msg += title + "
    "; msg += ""+ prog + "
    "; msg += "
    Start: " + d_str + "
    "; msg += "Duration: " + Display.durationString(length) + "h
    "; msg += "Remaining: " + Display.durationString((itm.payload.start + length - now)); msg += "

    "+ desc; - break; + */ + $("#descProg").show(); + $("#descRemaining").show(); + + $("#descTitle").text(title); + $("#descProg").text(prog); + $("#descStart").text("Start: " + d_str); + $("#descDuration").text("Duration: " + Display.durationString(length) + "h"); + $("#descRemaining").text("Remaining: " + Display.durationString((itm.payload.start + length - now))); + $("#descDesc").text(desc); + + break; case Main.eREC: d_str = mon + "/" + day + " " + hour + ":" + min; - +/* msg += "" + title + ""; msg += "

    " + d_str; msg += " Duration: " + Display.durationString(length) + "h"; msg += "

    "+ desc; - break; +*/ + $("#descTitle").text(title); + $("#descStart").text("Start: " + d_str); + $("#descDuration").text("Duration: " + Display.durationString(length) + "h"); + $("#descDesc").text(desc); + + break; case Main.eMED: - msg += "" + title + ""; +// msg += "" + title + ""; + $("#descTitle").text(title); break; default: Main.logToServer("ERROR in Display.handleDescription: Should not be here"); break; } - Display.setDescription(msg); +// Display.setDescription(msg); } }; - - - +Display.resetDescription = function () { + $("#descTitle").text(""); + $("#descProg").text(""); + $("#descStart").text(""); + $("#descDuration").text(""); + $("#descRemaining").text(""); + $("#descDesc").text(""); + + $("#descProg").hide(); + $("#descRemaining").hide(); +}; /* * this.currentWindow: Cursor (selected item) */ @@ -499,12 +540,13 @@ Display.setVideoListPosition = function(position, move) { } }; +/* Display.setDescription = function(description) { var descriptionElement = document.getElementById("description"); Display.putInnerHTML(descriptionElement, description); }; - +*/ Display.getDisplayTitle = function(item) { var res = {c1:"", c2:"", c3:""}; switch (Main.state) { @@ -741,6 +783,60 @@ Display.handlerHideVolume = function() { document.getElementById("volume").style.display="none"; }; +//--------------------------------------------------------- +/* + * Info Overlay handlers + */ +Display.showInfo = function(selected) { + var itm = Data.getCurrentItem().childs[selected]; + var title = itm.title; + var prog = itm.payload.prog; + var desc = itm.payload.desc; + var length = itm.payload.dur; + + var digi = new Date(parseInt(itm.payload.start*1000)); + var mon = Display.getNumString ((digi.getMonth()+1), 2); + var day = Display.getNumString (digi.getDate(), 2); + var hour = Display.getNumString (digi.getHours(), 2); + var min = Display.getNumString (digi.getMinutes(), 2); + + var d_str =""; + switch (Main.state) { + case Main.eLIVE: + var now = Display.GetEpochTime(); + + d_str = hour + ":" + min; + + $("#infoTitle").text(title + "\n" + prog); + $("#infoDuration").text("Duration: " + Display.durationString(length) + "h Remaining: " + Display.durationString((itm.payload.start + length - now))); + $("#infoDesc").text(desc); + break; + case Main.eREC: + d_str = mon + "/" + day + " " + hour + ":" + min; + $("#infoTitle").text(title); + $("#infoDuration").text(d_str + " Duration: " + Display.durationString(length) + "h"); + $("#infoDesc").text(desc); + break; + case Main.eMED: + $("#infoTitle").text(title); + break; + default: + Main.logToServer("ERROR in Display.handleDescription: Should not be here"); + break; + } + this.infoOlHandler.show(); + +}; + +Display.handlerShowInfo = function() { +// $("#infoOverlay").show(); + $("#infoOverlay").slideDown(300); +}; + +Display.handlerHideInfo = function() { +// $("#infoOverlay").hide(); + $("#infoOverlay").slideUp(300); +}; //--------------------------------------------------------- /* @@ -776,28 +872,26 @@ Display.showProgress = function() { }; Display.handlerHideProgress = function() { - document.getElementById("overlay").style.display="none"; + $("#overlay").fadeOut(300); }; Display.handlerShowProgress = function() { + $("#overlay").fadeIn(400); - document.getElementById("overlay").style.display="block"; if (Player.isRecording == true) { - document.getElementById("olRecProgressBar").style.display="block"; + $("#olRecProgressBar").show(); var now = Display.GetEpochTime(); var remaining = Math.round(((Player.startTime + Player.duration) - now) * 100/ Player.duration); -// Main.log (" remaining= " + remaining); var elm = document.getElementById("olRecProgressBar"); elm.style.display="block"; elm.style.width = remaining + "%"; elm.style.left = (100 - remaining) + "%"; } else - document.getElementById("olRecProgressBar").style.display="none"; + $("#olRecProgressBar").hide(); var timePercent = (Player.curPlayTime *100)/ Player.totalTime; -// Main.log("show OL Progress timePercent= " + timePercent); document.getElementById("olProgressBar").style.width = timePercent + "%"; diff --git a/smarttv-client/Javascript/Lang.js b/smarttv-client/Javascript/Lang.js new file mode 100644 index 0000000..985b0d8 --- /dev/null +++ b/smarttv-client/Javascript/Lang.js @@ -0,0 +1,12 @@ +var Lang = { + sel : "en", + en : {} +}; + +Lang["en"].configInit = "WARNING: Cannot create widget folder. Try Config"; +Lang["en"].configNoServer = "Config Server Error"; +Lang["en"].configReadContext1 = "ERROR: Error in Config File. Try widget re-install."; +Lang["en"].configReadContext2 = "WARNING: ConfigRead Error and WidgetFolder creation failed.
    Launching Config-Menu from here"; + + + diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index f18948a..f2594d3 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -158,7 +158,9 @@ Main.init = function () { Server.updateVdrStatus(); DirectAccess.init(); + Config.getWidgetVersion(); // DirectAccess.show(); +// Timers.init(); // Display.initOlForRecordings(); /* * Fetch JS file @@ -685,7 +687,6 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function (event) { Player.jumpToVideo(90); break; -// case tvKey.KEY_FF: case tvKey.KEY_RIGHT: Main.log("Right: Skip Forward"); Display.showProgress(); @@ -696,7 +697,6 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function (event) { Player.skipForwardVideo(); break; -// case tvKey.KEY_RW: case tvKey.KEY_LEFT: Main.log("Left: Skip Backward"); Display.showProgress(); @@ -762,7 +762,12 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function (event) { break; case tvKey.KEY_PAUSE: Main.log("PAUSE"); - Player.pauseVideo(); + if(Player.getState() == Player.PAUSED) { + Player.resumeVideo(); + } + else { + Player.pauseVideo(); + } break; case tvKey.KEY_UP: Player.adjustSkipDuration(1); @@ -773,27 +778,11 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function (event) { Display.showProgress(); break; case tvKey.KEY_INFO: + Display.showInfo(Main.selectedVideo); + break; case tvKey.KEY_ASPECT: Player.toggleAspectRatio(); break; -/* case tvKey.KEY_UP: - case tvKey.KEY_PANEL_VOL_UP: - case tvKey.KEY_VOL_UP: - Main.log("VOL_UP"); - Display.showVolume(); - if(Main.mute == 0) - Audio.setRelativeVolume(0); - break; - - case tvKey.KEY_DOWN: - case tvKey.KEY_PANEL_VOL_DOWN: - case tvKey.KEY_VOL_DOWN: - Main.log("VOL_DOWN"); - Display.showVolume(); - if(Main.mute == 0) - Audio.setRelativeVolume(1); - break; -*/ default: Main.log("Calling Default Key Hanlder"); this.defaultKeyHandler.handleDefKeyDown(keyCode); @@ -825,22 +814,39 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function (event) { Main.log(this.handlerName+": Key pressed: " + Main.getKeyCode(keyCode)); switch(keyCode) { - case tvKey.KEY_INFO: case tvKey.KEY_ASPECT: Player.toggleAspectRatio(); break; case tvKey.KEY_0: + DirectAccess.show("0"); + break; case tvKey.KEY_1: + DirectAccess.show("1"); + break; case tvKey.KEY_2: + DirectAccess.show("2"); + break; case tvKey.KEY_3: + DirectAccess.show("3"); + break; case tvKey.KEY_4: + DirectAccess.show("4"); + break; case tvKey.KEY_5: + DirectAccess.show("5"); + break; case tvKey.KEY_6: + DirectAccess.show("6"); + break; case tvKey.KEY_7: + DirectAccess.show("7"); + break; case tvKey.KEY_8: + DirectAccess.show("8"); + break; case tvKey.KEY_9: - DirectAccess.show(); + DirectAccess.show("9"); break; case tvKey.KEY_UP: case tvKey.KEY_CH_UP: @@ -851,7 +857,7 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function (event) { // Check, weather I am the last element of a folder. If yes, go one level up if (Main.selectedVideo == (Data.getVideoCount() -1)) { //Last VideoItem, check wrap around or folder fall-down - if (Data.isRootFolder() != "true") { + if (Data.isRootFolder() != true) { // Main.selectedVideo = Data.folderUp(); var itm = Data.folderUp(); Main.selectedVideo = itm.id; @@ -879,7 +885,7 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function (event) { // if yes, then one up if (Main.selectedVideo == 0) { //First VideoItem, - if (Data.isRootFolder() != "true") { + if (Data.isRootFolder() != true) { // Main.selectedVideo = Data.folderUp(); var itm = Data.folderUp(); Main.selectedVideo = itm.id; @@ -909,7 +915,17 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function (event) { Main.log("STOP"); Player.stopVideo(); // Display.setVideoList(Main.selectedVideo, Main.selectedVideo- ( Main.selectedVideo % (Display.LASTIDX +1))); - Display.setVideoList(Main.selectedVideo, Main.selectedVideo- ( Main.selectedVideo % Display.getNumberOfVideoListItems())); + //thlo: here + + if (Data.isRootFolder() != true) { + Display.addHeadline(Data.getCurrentItem().title); + Display.setVideoList(Main.selectedVideo, Main.selectedVideo- ( Main.selectedVideo % Display.getNumberOfVideoListItems())); + } + else { + Display.removeHeadline(); + Display.setVideoList(Main.selectedVideo, Main.selectedVideo- ( Main.selectedVideo % Display.getNumberOfVideoListItems())); + } + Display.show(); widgetAPI.blockNavigation(event); @@ -917,29 +933,13 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function (event) { case tvKey.KEY_PAUSE: Main.log("PAUSE"); break; - case tvKey.KEY_INFO: + case tvKey.KEY_INFO: + Display.showInfo(Main.selectedVideo); + break; case tvKey.KEY_ASPECT: Player.toggleAspectRatio(); break; -/* case tvKey.KEY_UP: - case tvKey.KEY_PANEL_VOL_UP: - case tvKey.KEY_VOL_UP: - Main.log("VOL_UP"); - Display.showVolume(); - if(Main.mute == 0) - Audio.setRelativeVolume(0); - break; - - case tvKey.KEY_DOWN: - case tvKey.KEY_PANEL_VOL_DOWN: - case tvKey.KEY_VOL_DOWN: - Main.log("VOL_DOWN"); - Display.showVolume(); - if(Main.mute == 0) - Audio.setRelativeVolume(1); - break; -*/ default: this.defaultKeyHandler.handleDefKeyDown(keyCode); break; @@ -961,18 +961,63 @@ cMenuKeyHndl.prototype.handleKeyDown = function (event) { switch(keyCode) { case tvKey.KEY_0: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("0"); + } + break; case tvKey.KEY_1: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("1"); + } + break; case tvKey.KEY_2: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("2"); + } + break; case tvKey.KEY_3: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("3"); + } + break; case tvKey.KEY_4: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("4"); + } + break; case tvKey.KEY_5: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("5"); + } + break; case tvKey.KEY_6: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("6"); + } + break; case tvKey.KEY_7: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("7"); + } + break; case tvKey.KEY_8: + if (Main.state == Main.eLIVE) { + Main.log("cMenu DirectAccess: keyCode= " + keyCode); + DirectAccess.show("8"); + } + break; case tvKey.KEY_9: if (Main.state == Main.eLIVE) { Main.log("cMenu DirectAccess: keyCode= " + keyCode); - DirectAccess.show(); + DirectAccess.show("9"); } break; @@ -1413,6 +1458,16 @@ Main.tvKeys = { KEY_STOP :27, // ESC // KEY_MUTE :27, + KEY_1 :49, + KEY_2 :50, + KEY_3 :51, + KEY_4 :52, + KEY_5 :53, + KEY_6 :54, + KEY_7 :55, + KEY_8 :56, + KEY_9 :57, + KEY_0 :48, // Un-used keycodes KEY_RETURN :88, @@ -1426,16 +1481,6 @@ Main.tvKeys = { KEY_FF :72, KEY_PLAY :71, KEY_STOP :70, - KEY_1 :101, - KEY_2 :98, - KEY_3 :6, - KEY_4 :8, - KEY_5 :9, - KEY_6 :10, - KEY_7 :12, -// KEY_8 :13, - KEY_9 :14, - KEY_0 :17, KEY_PANEL_CH_UP :104, KEY_PANEL_CH_DOWN :106, diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index 67c747e..8f7a5cd 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -338,7 +338,7 @@ Player.playVideo = function(resume_pos) { Main.logToServer("Player.play with ?time=" + resume_pos); } - if ((this.mFormat != this.ePDL) && (this.isLive == false)){ + if ((this.mFormat == this.eHLS) && (this.isLive == false)){ Notify.showNotify("No Trickplay", true); } Audio.plugin.SetSystemMute(false); @@ -400,6 +400,7 @@ Player.resumeVideo = function() { }; Player.jumpToVideo = function(percent) { + Spinner.show(); if (this.isLive == true) { return; } @@ -407,7 +408,6 @@ Player.jumpToVideo = function(percent) { Main.logToServer ("Player.jumpToVideo: Player not Playing"); return; } - Spinner.show(); Player.bufferState = 0; Display.showProgress(); @@ -460,6 +460,7 @@ Player.skipForwardVideo = function() { if (Player.isRecording == false) res = this.plugin.JumpForward(Player.skipDuration); else { + Spinner.show(); this.bufferState = 0; this.plugin.Stop(); var old = Player.curPlayTime; @@ -485,6 +486,7 @@ Player.skipBackwardVideo = function() { if (Player.isRecording == false) res = this.plugin.JumpBackward(Player.skipDuration); else { + Spinner.show(); this.bufferState = 0; this.plugin.Stop(); var tgt = (Player.curPlayTime/1000.0) - Player.skipDuration; diff --git a/smarttv-client/Javascript/Timers.js b/smarttv-client/Javascript/Timers.js new file mode 100644 index 0000000..c7110c7 --- /dev/null +++ b/smarttv-client/Javascript/Timers.js @@ -0,0 +1,64 @@ +var Timers = { + haveRapi : false, + timerList : {} +}; + +Timers.init = function() { + var url = "http://192.168.1.122:8002/info.xml"; + $.ajax({ + url: url, + type : "GET", + success : function(data, status, XHR ) { + Timers.haveRapi = true; + Main.log ("Timers: Got response"); + Timers.readTimers(); + + }, + error : function (jqXHR, status, error) { + Timers.haveRapi = false; + Main.log ("Timers: Not found!!!"); + } + }); +}; + +Timers.readTimers = function () { + if (Timers.haveRapi == false) { + Main.log ("Timers.readTimers: no restful API!!!"); + return; + } + var url = "http://192.168.1.122:8002/timers.xml"; + $.ajax({ + url: url, + type : "GET", + success : function(data, status, XHR ) { + Main.log ("Timers.readTimer: Success"); + Main.log(" Count= " + $(data).find("count").text()); + $(data).find("timer").each(function () { + Main.log("timer: "); + Timers.curItem = {}; + $(this).find("param").each(function () { + Main.log(" + " + $(this).attr("name") + " val= " +$(this).text()); + Timers.curItem[$(this).attr("name")] = $(this).text(); + }); +/* for (var prop in Timers.curItem) { + Main.log (" -" + prop + " : " + Timers.curItem[prop]); + } + */ + Main.log("Adding id " +Timers.curItem["id"]); + Timers.timerList[Timers.curItem["id"]] = Timers.curItem; + Timers.curItem = {}; + }); + Main.log("Summary"); + for (var id in Timers.timerList) { + Main.log (id + " : "); + for (var prop in Timers.timerList[id]) { + Main.log (" - " + prop + " : " + Timers.timerList[id][prop]); + } + + } + }, + error : function (jqXHR, status, error) { + Main.log ("Timers.readTimer: Failure"); + } + }); +}; \ No newline at end of file diff --git a/smarttv-client/config.xml b/smarttv-client/config.xml index 9595184..cd6d56c 100755 --- a/smarttv-client/config.xml +++ b/smarttv-client/config.xml @@ -9,7 +9,7 @@ Images/icon/SmartTvWeb_115.png Images/icon/SmartTvWeb_85.png Images/icon/SmartTvWeb_95.png - 0.91 + 0.92 y y diff --git a/smarttv-client/index.html b/smarttv-client/index.html index 3651fad..3a2f188 100755 --- a/smarttv-client/index.html +++ b/smarttv-client/index.html @@ -33,6 +33,8 @@ + + @@ -68,6 +70,7 @@
    +
    @@ -101,7 +104,22 @@
    -
    +
    +
    + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    @@ -150,6 +168,16 @@
    +
    + + + + +
    +
    +
    +
    +
    @@ -161,7 +189,7 @@
  • -
    +
    -- cgit v1.2.3 From 09f926c410cc6cbed1905b8ddf35f04d14a7805a Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 17 Mar 2013 11:47:07 +0100 Subject: add directAccessTimeout element to widget.conf --- widget.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/widget.conf b/widget.conf index da4a006..0ce0ce7 100644 --- a/widget.conf +++ b/widget.conf @@ -8,4 +8,5 @@ 5 30 20 +2000 -- cgit v1.2.3 From d5c26eb7a9c51e82be0ac41bd04f4433043776cd Mon Sep 17 00:00:00 2001 From: thlo Date: Thu, 4 Apr 2013 21:05:33 +0200 Subject: New urls manager for YouTube URLs. New http client class. Various bug fixes. --- httpclient.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++++ httpclient.h | 90 +++++++++++++ httpresource.c | 357 +++++++++++++++++++++++++++++++++++++++++++++++---- httpresource.h | 22 +++- httpresource_base.h | 46 +++++++ mngurls.c | 106 +++++++++++++++ mngurls.h | 64 ++++++++++ smarttvfactory.c | 147 ++++++++++++++++++++- smarttvfactory.h | 39 +++++- 9 files changed, 1192 insertions(+), 40 deletions(-) create mode 100644 httpclient.c create mode 100644 httpclient.h create mode 100644 httpresource_base.h create mode 100644 mngurls.c create mode 100644 mngurls.h 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 +#include +#include +#include + +#include +#include + +#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 + + + +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 #include +#include #include #include #include "httpresource.h" #include "smarttvfactory.h" #include "stvw_cfg.h" - +#include "mngurls.h" #include "url.h" #ifndef STANDALONE @@ -49,6 +50,10 @@ #include #include +#else +//standalone +#include +#include #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 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 = "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + for (uint i = 0; i < urls->size(); i++) { + *mResponseMessage += "\n"; + *mResponseMessage += (urls->getEntry(i))->mEntry; + *mResponseMessage += "\n\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 += "\n"; + *mResponseMessage += value; + *mResponseMessage += "\n\n"; + continue; + } + + *mResponseMessage += "\n"; + *mResponseMessage += "Unknown: " + line + " type=" + type; + + *mResponseMessage += "\n\n"; + } + myfile.close(); +*/ + *mResponseMessage += "\n"; + *mResponseMessage += "\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 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 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 = "" + "SmartTVWeb Bookmarklets" + "" + "
    " + "

    Bookmarklet for collecting YouTube Pages

    " + "
    " + "
    " + "

    Installation

    " + "Drag the link below to your Bookmarks toolbar" + "

    or

    " + "

    Right click and select “Bookmark This Link”

    " + "

    YT SaveNPlay: Save the video and also Play it.

    " + "

    YT Play: Play the video without saving.

    " + "
    " + "
    " + "

    Usage

    " + "

    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.

    " + "
    " + "
    " + "

    Have fun...

    " + ""; + + 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 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 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 #include #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 +#include +#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 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::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 #include #include -#include "httpresource.h" +#include +#include +//#include "httpresource.h" +#include "httpresource_base.h" #include "log.h" #include "stvw_cfg.h" +#include "mngurls.h" #ifndef STANDALONE #include @@ -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 clientList; + vector clientList; + vector mConTvClients; + int mActiveSessions; + int mHttpClients; + string mConfigDir; cSmartTvConfig *mConfig; int mMaxFd; fd_set mReadState; fd_set mWriteState; + + cManageUrls* mManagedUrls; }; -- cgit v1.2.3 From 0d3bb5c8d940b416ccc06a68a8ccfc35370fdbd4 Mon Sep 17 00:00:00 2001 From: thlo Date: Fri, 5 Apr 2013 17:43:54 +0200 Subject: Selection of audio tracks. First version of web video (yt) support. various bug fixes. --- smarttv-client/CSS/Main.css | 60 ++++- smarttv-client/Javascript/Comm.js | 101 ++++++++ smarttv-client/Javascript/Config.js | 7 +- smarttv-client/Javascript/Display.js | 73 +++++- smarttv-client/Javascript/Main.js | 221 +++++++++------- smarttv-client/Javascript/Network.js | 3 + smarttv-client/Javascript/Player.js | 488 ++++++++++++++++++++--------------- smarttv-client/Javascript/Server.js | 49 +++- smarttv-client/Javascript/Urls.js | 257 ++++++++++++++++++ smarttv-client/config.xml | 2 +- smarttv-client/index.html | 24 +- 11 files changed, 940 insertions(+), 345 deletions(-) create mode 100644 smarttv-client/Javascript/Comm.js create mode 100644 smarttv-client/Javascript/Urls.js diff --git a/smarttv-client/CSS/Main.css b/smarttv-client/CSS/Main.css index ca16181..3b23da8 100755 --- a/smarttv-client/CSS/Main.css +++ b/smarttv-client/CSS/Main.css @@ -24,6 +24,8 @@ body { left: 0px; top: 0px; width: 960px; height: 540px; background-color: blue; + z-index:10; + } #splashStatus { @@ -41,6 +43,8 @@ body { position: absolute; left: 0px; top: 0px; width: 960px; height: 540px; + z-index:10; + background: url("../Images/selectScreen-bg.png"); background: radial-gradient(ellipse at 80% 80%, #c5deea 0%,#8abbd7 31%,#066dab 100%); background: -webkit-radial-gradient(80% 80%, ellipse cover, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); @@ -74,12 +78,18 @@ body { /* background-size: 100%; */ +#webapiplayer { + position: absolute; + left: 0px; top: 0px; + width: 960px; height: 540px; +} #optionsScreen { position: absolute; left: 0px; top: 0px; width: 960px; height: 540px; background: url("../Images/selectScreen-bg.png"); background: -webkit-radial-gradient(80% 80%, ellipse cover, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); + z-index:10; } #optionsView { @@ -100,7 +110,8 @@ body { #statusPopup { position: absolute; - left: 80%; top: 10px; + display: none; + left: 80%; top: 10px; width: 10%; height: 6%; -webkit-border-radius: 1em; -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); @@ -112,6 +123,7 @@ body { padding-left:20px; padding-right:20px; background-color: rgba(0,0,0,0.5); + z-index:20; } #notify { @@ -129,7 +141,7 @@ body { padding-left:20px; padding-right:20px; background-color: rgba(0,0,0,0.5); - z-index:20; + z-index:50; overflow:hidden; text-overflow: ellipsis; white-space : nowrap; @@ -140,9 +152,9 @@ body { position: absolute; display: none; left: 0px; top: 0px; - width: 960px; height: 30%; + width: 960px; height: 162px; border-style:solid; - z-index:10; + z-index:50; padding-top:20px; padding-left:20px; padding-right:20px; @@ -151,6 +163,36 @@ body { background-color: -webkit-linear-gradient(-45deg, rgba(167,207,223,1) 0%,rgba(35,83,138,1) 100%); } +#infoTitle{ + position: absolute; + left: 2px; top: 0px; + width: 960px; height: 26px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; +} + +#infoDuration{ + position: absolute; + left: 2px; top: 26px; + width: 960px; height: 21px; + font-size:14px; +} + +#infoDesc{ + position: absolute; + left: 2px; top: 47px; + width: 960px; height: 90px; + font-size:14px; + overflow:hidden; +} + +#infoAudio { + position: absolute; + left: 2px; top: 141px; + width: 960px; height: 21px; + font-size:14px; +} #popup{ position: absolute; display: none; @@ -159,7 +201,7 @@ body { border-style:solid; border-width:2px; -webkit-border-radius: 7px; - z-index:10; + z-index:50; overflow-y: scroll; padding-top:20px; @@ -173,6 +215,8 @@ body { #main { display: none; position: absolute; + z-index:10; + left: 0px; top: 0px; width: 960px; height: 540px; background: rgba(0, 0, 139, 0.5); @@ -188,7 +232,7 @@ body { background-repeat:repeat-x; padding-top:3px; padding-left:10px; - z-index:10; + z-index:15; } #logo div { @@ -374,7 +418,7 @@ body { background-repeat:repeat-x; background-color: black; background-color: rgba(0, 0, 0, 0.5); - z-index:20; + z-index:50; } #olProgressBarBG { @@ -465,6 +509,6 @@ body { background-repeat:no-repeat; opacity:0.5; display:none; - z-index:20; + z-index:60; } diff --git a/smarttv-client/Javascript/Comm.js b/smarttv-client/Javascript/Comm.js new file mode 100644 index 0000000..d4cdd18 --- /dev/null +++ b/smarttv-client/Javascript/Comm.js @@ -0,0 +1,101 @@ + +var Comm = { + customMgr : {}, + deviceInstance : [] +}; + +Comm.init = function () { + // >> Register custom manager callback to receive device connect and disconnect events + Comm.customMgr = webapis.customdevice || {}; + + Comm.customMgr.registerManagerCallback(Comm.onDeviceStatusChange); + + // >> Initializes custom device profile and gets available devices + Comm.customMgr.getCustomDevices(Comm.onCustomObtained); + Main.log("curWidget.id= " + curWidget.id); + Main.logToServer("curWidget.id= (" + curWidget.id+")"); +}; + +Comm.onDeviceStatusChange = function (sParam) { + switch( Number(sParam.eventType) ) { + case Comm.customMgr.MGR_EVENT_DEV_CONNECT: + Main.logToServer("onDeviceStatusChange - MGR_EVENT_DEV_CONNECT: name= " + sParam.name + " type= " +sParam.deviceType); + break; + case Comm.customMgr.MGR_EVENT_DEV_DISCONNECT: + Main.logToServer("onDeviceStatusChange - MGR_EVENT_DEV_DISCONNECT: name= " + sParam.name + " type= " +sParam.deviceType); + break; + default: + Main.logToServer("onDeviceStatusChange - Unknown event eType= " + sParam.eventType + " name= " + sParam.name+ " dType= "+sParam.deviceType); + break; + } + Comm.customMgr.getCustomDevices(Comm.onCustomObtained); +}; + +Comm.onCustomObtained = function (customs) { + Main.logToServer("onCustomObtained - found " + customs.length + " custom device(s)"); + for(var i=0; i"; $("#descTitle").text(title); break; + case Main.eURLS: +// msg += "" + title + ""; + $("#descTitle").text(title); + $("#descDuration").text("Duration: " + Display.durationString(length) + "h"); + $("#descDesc").text(desc); + break; default: Main.logToServer("ERROR in Display.handleDescription: Should not be here"); break; @@ -588,6 +602,9 @@ Display.getDisplayTitle = function(item) { res.c2 = item.title; } break; + case Main.eURLS: + res.c2 = item.title; + break; default: Main.logToServer("ERROR in Display.getDisplayTitle: Shall be in state 1, 2 or 3. State= " + Main.state); break; @@ -639,8 +656,9 @@ Display.updateOlForLive = function (start_time, duration, now) { Display.setOlTitle(Data.getCurrentItem().childs[Main.selectedVideo].title + " - " +Data.getCurrentItem().childs[Main.selectedVideo].payload.prog); Display.setStartStop (start_time, (start_time + duration)); - Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; - Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); + Player.setDuration(); +//thlo Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; +//thlo Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); // var digi = new Date((Data.getCurrentItem().childs[Main.selectedVideo].payload.start*1000)); // Main.log (" Date(): StartTime= " + digi.getHours() + ":" + digi.getMinutes() + ":" + digi.getSeconds()); @@ -706,11 +724,13 @@ Display.setTrickplay = function(direction, multiple) { // Player.OnCurrentPlayTime Display.updatePlayTime = function() { - $("#olTimeInfo").text(Player.curPlayTimeStr + " / " + Player.totalTimeStr); +// $("#olTimeInfo").text(Player.curPlayTimeStr + " / " + Player.totalTimeStr); + $("#olTimeInfo").text(Player.curPlayTimeStr + " / " + Player.getDurationStr()); }; Display.updateProgressBar = function () { - var timePercent = (Player.curPlayTime *100)/ Player.totalTime; +//thlo var timePercent = (Player.curPlayTime *100)/ Player.totalTime; + var timePercent = (Player.curPlayTime *100)/ Player.getDuration(); $("#olProgressBar").css("width", (Math.round(timePercent) + "%")); }; @@ -810,22 +830,48 @@ Display.showInfo = function(selected) { $("#infoTitle").text(title + "\n" + prog); $("#infoDuration").text("Duration: " + Display.durationString(length) + "h Remaining: " + Display.durationString((itm.payload.start + length - now))); $("#infoDesc").text(desc); + $("#infoAudio").text("Audio Tracks: " + Player.getNumOfAudioTracks() + " Subtitle Tracks: " + Player.getNumOfSubtitleTracks()); break; case Main.eREC: d_str = mon + "/" + day + " " + hour + ":" + min; $("#infoTitle").text(title); $("#infoDuration").text(d_str + " Duration: " + Display.durationString(length) + "h"); $("#infoDesc").text(desc); + $("#infoAudio").text("Audio Tracks: " + Player.getNumOfAudioTracks() + " Subtitle Tracks: " + Player.getNumOfSubtitleTracks()); break; case Main.eMED: $("#infoTitle").text(title); + $("#infoDuration").text("Duration: " + Display.durationString(Player.getDuration()) ); + $("#infoAudio").text("Audio Tracks: " + Player.getNumOfAudioTracks() + " Subtitle Tracks: " + Player.getNumOfSubtitleTracks()); + break; + case Main.eURLS: + $("#infoTitle").text(title); + $("#infoDuration").text("Duration: " + Display.durationString(length) ); + $("#infoDesc").text(desc); + +/* var tgt_height = $("#infoDesc").height(); + var temp = desc; + + Main.log("tgt_height= " +tgt_height + " outerHeight= " + $('#infoDesc').outerHeight()); + if( tgt_height < $('#infoDesc').outerHeight() ) { + while(tgt_height < $('#infoDesc').outerHeight()) { + $('#infoDesc').text( temp = temp.substr(0, temp.length-1) ); + } + $('#infoDesc').text( temp = temp.substr(0, temp.length-3) ); + $('#infoDesc').append('...'); + } + */ + $("#infoAudio").text("Audio Tracks: " + Player.getNumOfAudioTracks() + " Subtitle Tracks: " + Player.getNumOfSubtitleTracks()); break; default: Main.logToServer("ERROR in Display.handleDescription: Should not be here"); break; } this.infoOlHandler.show(); - + Main.log("Info title= (" + $("#infoTitle").position().top + ", " + $("#infoTitle").position().left+")"); + Main.log("Info dur= (" + $("#infoDuration").position().top + ", " + $("#infoDuration").position().left+")"); + Main.log("Info desc= (" + $("#infoDesc").position().top + ", " + $("#infoDesc").position().left+")"); + Main.log("Info desc line-height: " + $("#infoDesc").css('line-height')); }; Display.handlerShowInfo = function() { @@ -881,7 +927,8 @@ Display.handlerShowProgress = function() { if (Player.isRecording == true) { $("#olRecProgressBar").show(); var now = Display.GetEpochTime(); - var remaining = Math.round(((Player.startTime + Player.duration) - now) * 100/ Player.duration); +//thlo var remaining = Math.round(((Player.startTime + Player.duration) - now) * 100/ Player.duration); + var remaining = Math.round(((Player.startTime + Player.getDuration()) - now) * 100/ Player.getDuration()); var elm = document.getElementById("olRecProgressBar"); elm.style.display="block"; elm.style.width = remaining + "%"; @@ -890,12 +937,14 @@ Display.handlerShowProgress = function() { else $("#olRecProgressBar").hide(); - var timePercent = (Player.curPlayTime *100)/ Player.totalTime; +//thlo var timePercent = (Player.curPlayTime *100)/ Player.totalTime; + var timePercent = (Player.curPlayTime *100)/ Player.getDuration(); document.getElementById("olProgressBar").style.width = timePercent + "%"; - $("#olTimeInfo").text(Player.curPlayTimeStr + " / " + Player.totalTimeStr); +//thlo $("#olTimeInfo").text(Player.curPlayTimeStr + " / " + Player.totalTimeStr); + $("#olTimeInfo").text(Player.curPlayTimeStr + " / " + Player.getDurationStr()); var Digital=new Date(); var hours=Digital.getHours(); diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index f2594d3..2581074 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -51,7 +51,8 @@ var Main = { eLIVE : 1, // State Live Select Screen / Video Playing eREC : 2, // State Recording Select Screen / Video Playing eMED : 3, // State Media Select Screen / Video Playing - eOPT : 4, // Options + eURLS : 4, // State Urls + eOPT : 5, // Options defKeyHndl : null, selectMenuKeyHndl : null, @@ -128,8 +129,7 @@ Main.init = function () { Main.log("Main.init()"); Buttons.init(); - if ( Player.init() && Server.init() && Audio.init()) { - Display.setVolume( Audio.getVolume() ); + if ( Player.init() && Server.init() ) { // Start retrieving data from server Server.dataReceivedCallback = function() { @@ -146,6 +146,14 @@ Main.init = function () { } }; + UrlsFetcher.dataReceivedCallback = function() { + /* Use video information when it has arrived */ + Display.setVideoList(Main.selectedVideo, Main.selectedVideo); + Spinner.hide(); + Display.show(); + }; + + // Enable key event processing this.enableKeys(); @@ -155,10 +163,15 @@ Main.init = function () { } ClockHandler.start("#selectNow"); + HeartbeatHandler.start(); + Server.updateVdrStatus(); - +// Server.notifyServer("started"); + DirectAccess.init(); Config.getWidgetVersion(); + Comm.init(); + // DirectAccess.show(); // Timers.init(); // Display.initOlForRecordings(); @@ -207,11 +220,18 @@ Main.logToServer = function (msg) { */ }; -Main.onUnload = function() -{ +Main.onUnload = function() { + Server.notifyServer("stopped"); Player.deinit(); }; +Main.testUrls = function () { + Main.log("################## Main.testUrls"); + Spinner.show(); + UrlsFetcher.autoplay = "6927QNxye6k"; + UrlsFetcher.appendGuid("6927QNxye6k"); + +}; Main.changeState = function (state) { Main.log("change state: OldState= " + this.state + " NewState= " + state); @@ -232,50 +252,53 @@ Main.changeState = function (state) { Main.log ("old Select= " + Main.selectMenuKeyHndl.select); Display.resetSelectItems(old_state); -// document.getElementById("selectScreen").style.display="block"; $("#selectScreen").show(); ClockHandler.start("#selectNow"); Display.hide(); Data.reset (); + //TODO: Should reset progress bar as well Display.resetVideoList(); Display.resetDescription (); break; case Main.eLIVE: -// document.getElementById("selectScreen").style.display="none"; $("#selectScreen").hide(); ClockHandler.start("#logoNow"); Display.show(); Main.selectedVideo = 0; -// Data.reset (); Main.liveSelected(); break; case Main.eREC: -// document.getElementById("selectScreen").style.display="none"; $("#selectScreen").hide(); ClockHandler.start("#logoNow"); Display.show(); Main.selectedVideo = 0; -// Data.reset (); Main.recordingsSelected(); break; case Main.eMED: -// document.getElementById("selectScreen").style.display="none"; $("#selectScreen").hide(); ClockHandler.start("#logoNow"); Display.show(); Main.selectedVideo = 0; -// Data.reset (); Main.mediaSelected(); break; + case Main.eURLS: + $("#selectScreen").hide(); + ClockHandler.start("#logoNow"); + Display.show(); + Main.selectedVideo = 0; + Main.urlsSelected(); + +// window.setTimeout(function() {Main.testUrls (); }, (5*1000)); + + break; + case Main.eOPT: // Options // Options.init(); -// document.getElementById("selectScreen").style.display="none"; $("#selectScreen").hide(); Options.show(); -// document.getElementById("optionsScreen").style.display="block"; Main.optionsSelected(); break; } @@ -303,10 +326,6 @@ Main.recordingsSelected = function() { }; Server.errorCallback = Main.serverError; -/* Server.errorCallback = function (msg) { - Server.errorCallback = Main.serverError; - }; -*/ Server.setSort(true); /* if (Config.format == "") { Server.fetchVideoList(Config.serverUrl + "/recordings.xml?model=samsung"); @@ -336,12 +355,26 @@ Main.mediaSelected = function() { Display.showPopup(msg); Main.changeState(0); }; -// Player.isLive = false; Server.setSort(true); Spinner.show(); Server.fetchVideoList(Config.serverUrl + "/media.xml"); /* Request video information from server */ }; +Main.urlsSelected = function() { + Server.retries = 0; + Player.stopCallback = function() { + // + Display.show(); + }; + Server.errorCallback = function (msg) { + Display.showPopup(msg); + Main.changeState(0); + }; + Server.setSort(true); + Spinner.show(); + UrlsFetcher.fetchUrlList(); +}; + Main.optionsSelected = function() { Main.log ("Main.optionsSelected"); }; @@ -378,7 +411,7 @@ event = event || window.event; // selectView this.selectMenuKeyHndl.handleKeyDown(event); break; - case 1: + case Main.eLIVE: // Live Main.log("Live - Main.keyDown PlayerState= " + Player.getState()); if(Player.getState() == Player.STOPPED) { @@ -391,8 +424,9 @@ event = event || window.event; }; break; - case 2: - case 3: + case Main.eREC: + case Main.eMED: + case Main.eURLS: // recordings // Main.log("Recordings - Main.keyDown PlayerState= " + Player.getState()); if(Player.getState() == Player.STOPPED) { @@ -405,7 +439,7 @@ event = event || window.event; }; break; - case 4: + case Main.eOPT: // Options.onInput(); // Main.log ("ERROR: Wrong State"); break; @@ -447,9 +481,11 @@ Main.playItem = function (url) { // Server.getResume(Player.guid); Main.log(" playItem: now= " + now + " start_time= " + start_time + " dur= " + duration + " (Start + Dur - now)= " + ((start_time + duration) -now)); + Main.logToServer(" playItem: now= " + now + " start_time= " + start_time + " dur= " + duration + " (Start + Dur - now)= " + ((start_time + duration) -now)); - Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; - Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); + Player.setDuration(); +//thlo Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; +//thlo Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); //thlo if (Config.usePdlForRecordings == false) { @@ -471,9 +507,10 @@ Main.playItem = function (url) { if ((now - (start_time + duration)) < 0) { // still recording Main.log("*** Still Recording! ***"); + Main.logToServer("*** Still Recording! ***"); Player.isRecording = true; Player.startTime = start_time; - Player.duration = duration; + Player.duration = duration; // EpgDuration // document.getElementById("olRecProgressBar").style.display="block"; $("#olRecProgressBar").show(); Display.updateRecBar(start_time, duration); @@ -496,12 +533,6 @@ Main.playItem = function (url) { } */ } -/* else { - // TODO: Obsolete - $("#olRecProgressBar").hide(); -// document.getElementById("olRecProgressBar").display="none"; - } -*/ Player.setVideoURL( Data.getCurrentItem().childs[Main.selectedVideo].payload.link + url_ext); Player.guid = Data.getCurrentItem().childs[Main.selectedVideo].payload.guid; Main.log("Main.playItem - Player.guid= " +Player.guid); @@ -527,6 +558,19 @@ Main.playItem = function (url) { Player.guid = "unknown"; + break; + case Main.eURLS: + Display.hide(); + Display.showProgress(); + Player.mFormat = Player.ePDL; + + Main.log(" playItem: now= " + now + " start_time= " + start_time + " dur= " + duration + " (Start + Dur - now)= " + ((start_time + duration) -now)); + + Display.setOlTitle(Data.getCurrentItem().childs[Main.selectedVideo].title); + + Main.log("playItem: guid= " + Data.getCurrentItem().childs[Main.selectedVideo].payload.guid); + UrlsFetcher.getYtVideoUrl( Data.getCurrentItem().childs[Main.selectedVideo].payload.guid); + break; default: Main.logToServer("ERROR in Main.playItem: should not be here"); @@ -536,19 +580,8 @@ Main.playItem = function (url) { }; Main.selectPageUp = function() { -/* if (this.selectedVideo == 0) { - Main.changeState(0); - return; - }; -*/ -// Main.previousVideo(Display.LASTIDX + 1); Main.previousVideo(Display.getNumberOfVideoListItems()); -/* this.selectedVideo = (this.selectedVideo - (Display.LASTIDX + 1)); - if (this.selectedVideo < 0) { - this.selectedVideo = 0; - } -*/ var first_item = this.selectedVideo - Display.currentWindow; if (first_item < 0 ) first_item = 0; @@ -558,14 +591,8 @@ Main.selectPageUp = function() { }; Main.selectPageDown = function() { -// Main.nextVideo (Display.LASTIDX + 1); Main.nextVideo (Display.getNumberOfVideoListItems()); -/* this.selectedVideo = (this.selectedVideo + Display.LASTIDX + 1); - if (this.selectedVideo >= Data.getVideoCount()) { - this.selectedVideo = Data.getVideoCount() -1; - } -*/ var first_item = this.selectedVideo - Display.currentWindow; Main.log("selectPageDown: this.selectedVideo= " + this.selectedVideo + " first_item= " + first_item + " curWind= " + Display.currentWindow); @@ -783,6 +810,13 @@ cPlayStateKeyHndl.prototype.handleKeyDown = function (event) { case tvKey.KEY_ASPECT: Player.toggleAspectRatio(); break; + case tvKey.KEY_BLUE: + Player.nextAudioTrack(); + break; + case tvKey.KEY_YELLOW: + Player.nextSubtitleTrack(); + break; + break; default: Main.log("Calling Default Key Hanlder"); this.defaultKeyHandler.handleDefKeyDown(keyCode); @@ -939,6 +973,12 @@ cLivePlayStateKeyHndl.prototype.handleKeyDown = function (event) { case tvKey.KEY_ASPECT: Player.toggleAspectRatio(); break; + case tvKey.KEY_BLUE: + Player.nextAudioTrack(); + break; + case tvKey.KEY_YELLOW: + Player.nextSubtitleTrack(); + break; default: this.defaultKeyHandler.handleDefKeyDown(keyCode); @@ -1103,7 +1143,7 @@ function cSelectMenuKeyHndl (def_hndl) { Main.log(this.handlerName + " created"); this.select = 1; - this.selectMax = 4; // Highest Select Entry + this.selectMax = 5; // Highest Select Entry }; cSelectMenuKeyHndl.prototype.handleKeyDown = function (event) { @@ -1133,35 +1173,42 @@ cSelectMenuKeyHndl.prototype.handleKeyDown = function (event) { this.select = 4; Main.changeState (this.select); break; - - case tvKey.KEY_ENTER: - case tvKey.KEY_PLAY: - case tvKey.KEY_PANEL_ENTER: - Main.log("ENTER"); - Main.log ("CurSelect= " + this.select); - Main.changeState (this.select); + case tvKey.KEY_5: + Main.log("KEY_5 pressed"); + this.select = 5; + Main.changeState (this.select); + break; - case tvKey.KEY_DOWN: - Display.unselectItem(document.getElementById("selectItem"+this.select)); - if (++this.select > this.selectMax) - this.select = 1; - Display.selectItem(document.getElementById("selectItem"+this.select)); - Main.log("DOWN " +this.select); - break; + case tvKey.KEY_ENTER: + case tvKey.KEY_PLAY: + case tvKey.KEY_PANEL_ENTER: + Main.log("ENTER"); + Main.log ("CurSelect= " + this.select); + + Main.changeState (this.select); + + break; //thlo: correct? + case tvKey.KEY_DOWN: + Display.unselectItem(document.getElementById("selectItem"+this.select)); + if (++this.select > this.selectMax) + this.select = 1; + Display.selectItem(document.getElementById("selectItem"+this.select)); + Main.log("DOWN " +this.select); + break; - case tvKey.KEY_UP: - Display.unselectItem(document.getElementById("selectItem"+this.select)); - - if (--this.select < 1) - this.select = this.selectMax; - Display.selectItem(document.getElementById("selectItem"+this.select)); - - Main.log("UP "+ this.select); - break; - default: - this.defaultKeyHandler.handleDefKeyDown(keyCode); - break; + case tvKey.KEY_UP: + Display.unselectItem(document.getElementById("selectItem"+this.select)); + + if (--this.select < 1) + this.select = this.selectMax; + Display.selectItem(document.getElementById("selectItem"+this.select)); + + Main.log("UP "+ this.select); + break; + default: + this.defaultKeyHandler.handleDefKeyDown(keyCode); + break; } }; @@ -1187,29 +1234,11 @@ cDefaulKeyHndl.prototype.handleDefKeyDown = function (keyCode) { widgetAPI.blockNavigation(event); } else { + Server.notifyServer("stopped"); widgetAPI.sendReturnEvent(); } break; - -/* case tvKey.KEY_VOL_UP: - Main.log(this.handlerName + "VOL_UP"); - Display.showVolume(); - if(Main.mute == 0) - Audio.setRelativeVolume(0); - break; - - case tvKey.KEY_VOL_DOWN: - Main.log(this.handlerName + "VOL_DOWN"); - Display.showVolume(); - if(Main.mute == 0) - Audio.setRelativeVolume(1); - break; - case tvKey.KEY_MUTE: - Main.log(this.handlerName + "MUTE"); - Main.muteMode(); - break; -*/ default: Main.log(this.handlerName + "Unhandled key"); break; diff --git a/smarttv-client/Javascript/Network.js b/smarttv-client/Javascript/Network.js index b4c0772..314dcca 100755 --- a/smarttv-client/Javascript/Network.js +++ b/smarttv-client/Javascript/Network.js @@ -2,6 +2,7 @@ var Network = { plugin : null, ownMac : "", ownGw : "", + ownIp : "", isInited: false }; @@ -12,9 +13,11 @@ Network.init = function () { if ((nw_type == 0) || (nw_type == 1)) { this.ownMac = this.plugin.GetMAC(nw_type); this.ownGw = this.plugin.GetGateway(nw_type); + this.ownIp = this.plugin.GetIP(nw_type); } Main.log( "ownMac= " + this.ownMac); Main.log ("ownGw= " + this.ownGw); + Main.log ("ownIp= " + this.ownIp); this.isInited = true; } catch (e) { diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index 8f7a5cd..6708558 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -1,12 +1,11 @@ /* * This module only works with the Samsung Media Players. For other player objects, the code need to be adjusted */ +var mainPlayer; var Player = { - plugin : null, - pluginBD : null, - mFrontPanel : null, + AVPlayerObj : null, isLive : false, isRecording : false, mFormat : 0, @@ -19,7 +18,7 @@ var Player = url : "", guid : "unknown", startTime : 0, - duration : 0, + duration : 0, // EpgDuration resumePos : -1, state : 0, @@ -40,6 +39,8 @@ var Player = trickPlaySpeed : 1, // Multiple of 2 only. trickPlayDirection : 1, + curAudioTrack : 0, + curSubtitleTrack : 0, // Zero means off STOPPED : 0, PLAYING : 1, @@ -81,8 +82,9 @@ Player.resetAtStop = function () { this.isLive =false; this.isRecording = false; this.mFormat =Player.eUND; - - }; + this.curAudioTrack =0; + this.curSubtitleTrack = 0; +}; Player.toggleAspectRatio = function () { /* var height = Player.plugin.GetVideoHeight(); @@ -105,24 +107,10 @@ Player.toggleAspectRatio = function () { // it is cropped 16 to 9 this.aspectRatio = this.eASP16to9; Notify.showNotify("16 : 9", true); - Main.logToServer("Player.toggleAspectRatio: 16 by 9 Now"); +// Main.logToServer("Player.toggleAspectRatio: 16 by 9 Now"); break; } Player.setFullscreen(); -/* if (this.aspectRatio == this.eASP16to9) { - // Do 4 to 3 - this.plugin.SetDisplayArea(120, 0, 720, 540); - // 4/3 = x/540 - this.aspectRatio = this.eASP4to3; - Main.logToServer("Player.toggleAspectRatio: 4 by 3 Now"); - } - else { - // do 16 to 9 - Player.setFullscreen(); - this.aspectRatio = this.eASP16to9; - Main.logToServer("Player.toggleAspectRatio: 16 by 9 Now"); - } - */ }; @@ -133,141 +121,184 @@ Player.setWindow = function() { Player.setFullscreen = function() { // this.plugin.SetDisplayArea(0, 0, 960, 540); - var h = Player.plugin.GetVideoHeight(); - var w = Player.plugin.GetVideoWidth(); - Main.logToServer ("Resolution= " + w + " x " + h ); + var resolution = Player.AVPlayerObj.getVideoResolution().split("|"); + + var w = resolution[0]; + var h = resolution[1]; + Main.logToServer ("Player.setFullscreen: Resolution= " + w + " x " + h ); Main.log ("Resolution= " + w + " x " + h ); switch (this.aspectRatio) { case this.eASP16to9: - this.plugin.SetDisplayArea(0, 0, 960, 540); - this.plugin.SetCropArea(0, 0, w, h); - Main.logToServer("Player.toggleAspectRatio: 16 by 9 Now"); +// this.plugin.SetDisplayArea(0, 0, 960, 540); +// this.plugin.SetCropArea(0, 0, w, h); + Player.AVPlayerObj.setDisplayArea({left: 0, top:0, width:960, height:540 }); + Player.AVPlayerObj.setCropArea(Player.onCropSuccess, Player.onError, {left: 0, top:0, width:w, height:h }); + + Main.logToServer("Player.setFullscreen: 16 by 9 Now"); break; case this.eASP4to3: // it is 4 to 3. do cropped do 16 to 9 - this.plugin.SetDisplayArea(120, 0, 720, 540); - this.plugin.SetCropArea(0, 0, w, h); +// this.plugin.SetDisplayArea(120, 0, 720, 540); +// this.plugin.SetCropArea(0, 0, w, h); + Player.AVPlayerObj.setDisplayArea({left: 120, top:0, width:720, height:540 }); + Player.AVPlayerObj.setCropArea(Player.onCropSuccess, Player.onError, {left: 0, top:0, width:w, height:h }); // 4/3 = x/540 - Main.logToServer("Player.toggleAspectRatio: 4 by 3 Now"); + Main.logToServer("Player.setFullscreen: 4 by 3 Now"); break; case this.eASPcrop16to9: // it is cropped 16 to 9 var z = Math.ceil(w*w*27 /(h*64)); - Main.logToServer("Player.toggleAspectRatio: Crop 16 by 9 Now: z= " + z); - this.plugin.SetDisplayArea(0, 0, 960, 540); - this.plugin.SetCropArea(0, Math.round((h-z)/2), w, z); + Main.logToServer("Player.setFullscreen: Crop 16 by 9 Now: z= " + z); +// this.plugin.SetDisplayArea(0, 0, 960, 540); +// this.plugin.SetCropArea(0, Math.round((h-z)/2), w, z); + Player.AVPlayerObj.setDisplayArea({left: 0, top:0, width:960, height:540 }); + Player.AVPlayerObj.setCropArea(Player.onCropSuccess, Player.onError, {left: 0, top:Math.round((h-z)/2), width:w, height:z }); break; } -/* if (this.aspectRatio == this.eASP16to9) { - // Do 4 to 3 - this.plugin.SetDisplayArea(120, 0, 720, 540); - // 4/3 = x/540 - this.aspectRatio = this.eASP4to3; - Main.logToServer("Player.toggleAspectRatio: 4 by 3 Now"); - } - else { - // do 16 to 9 - Player.setFullscreen(); - this.aspectRatio = this.eASP16to9; - Main.logToServer("Player.toggleAspectRatio: 16 by 9 Now"); - } - */ }; +//successcallback +//function onAVPlayObtained(avplay) { +Player.onAVPlayObtained = function (avplay) { + Player.AVPlayerObj = avplay; + Player.AVPlayerObj.hide(); + Main.logToServer("onAVPlayObtained: sName= " + avplay.sName+ " sVersion: " + avplay.sVersion); + var cb = new Object(); + cb.containerID = 'webapiplayer'; + cb.zIndex = 0; + cb.bufferingCallback = new Object(); + cb.bufferingCallback.onbufferingstart= Player.onBufferingStart; + cb.bufferingCallback.onbufferingprogress = Player.onBufferingProgress; + cb.bufferingCallback.onbufferingcomplete = Player.onBufferingComplete; + + cb.playCallback = new Object; + cb.playCallback.oncurrentplaytime = Player.OnCurrentPlayTime; + cb.playCallback.onresolutionchanged = Player.onResolutionChanged; + cb.playCallback.onstreamcompleted = Player.OnRenderingComplete; + cb.playCallback.onerror = Player.onError; + + cb.displayRect= new Object(); + cb.displayRect.top = 0; + cb.displayRect.left = 0; + cb.displayRect.width = 960; + cb.displayRect.height = 540; + cb.autoRatio = false; + + try { + Player.AVPlayerObj.init(cb); + } + catch (e) { + Main.log("Player: Error during init: " + e.message); + Main.logToServer("Player: Error during init: " + e.message); + }; +}; + +//errorcallback +//function onGetAVPlayError(error) { +Player.onGetAVPlayError = function (error) { + Main.log('Player.onGetAVPlayError: ' + error.message); + Main.logToServer('Player.onGetAVPlayError: ' + error.message); +}; + + Player.init = function() { var success = true; Main.log("success vale : " + success); this.state = this.STOPPED; + + try { + var custom = webapis.avplay; + Main.logToServer("webapis.ver= " + webapis.ver); + custom.getAVPlay(Player.onAVPlayObtained, Player.onGetAVPlayError); + } + catch(exp) { + Main.log('Player.init: getAVplay Exception :[' +exp.code + '] ' + exp.message); + } + - this.plugin = document.getElementById("pluginPlayer"); - this.pluginBD = document.getElementById("pluginBD"); - try { - this.pluginBD.DisplayVFD_Show(0101); // Stop - } - catch (e) { - - } - -/* var pl_version = ""; - try { - pl_version = this.plugin.GetPlayerVersion(); - } - catch (e) { - Main.logToServer("Error while getting player version: " +e); - } - Main.logToServer("PlayerVersion= " + pl_version); -*/ - if (!this.plugin) - { - Main.log("success vale this.plugin : " + success); - success = false; - } this.skipDuration = Config.skipDuration; // Use Config default also here - -// var vermsg = this.plugin.GetPlayerVersion(); -// Main.log ("player plugin version: " +vermsg); - - this.plugin.OnCurrentPlayTime = 'Player.OnCurrentPlayTime'; - this.plugin.OnStreamInfoReady = 'Player.OnStreamInfoReady'; - this.plugin.OnBufferingStart = 'Player.onBufferingStart'; - this.plugin.OnBufferingProgress = 'Player.onBufferingProgress'; - this.plugin.OnBufferingComplete = 'Player.onBufferingComplete'; - this.plugin.OnConnectionFailed = 'Player.OnConnectionFailed'; // fails to connect to the streaming server - this.plugin.OnStreamNotFound = 'Player.OnStreamNotFound'; // 404 file not found - this.plugin.OnNetworkDisconnected = 'Player.OnNetworkDisconnected'; // when the ethernet is disconnected or the streaming server stops supporting the content in the middle of streaming. - this.plugin.OnRenderingComplete = 'Player.OnRenderingComplete'; - + Main.log("success= " + success); return success; }; -Player.deinit = function() -{ - Main.log("Player deinit !!! " ); - - if (this.plugin) - { - this.plugin.Stop(); - } +Player.deinit = function() { + Main.log("Player deinit !!! " ); + + if (Player.AVPlayerObj != null) { + Player.AVPlayerObj.stop(); + } +}; + +Player.getNumOfAudioTracks = function () { + return (Player.AVPlayerObj.totalNumOfAudio != null) ? Player.AVPlayerObj.totalNumOfAudio : "Unknown"; +}; + +Player.getNumOfSubtitleTracks = function () { + return (Player.AVPlayerObj.totalNumOfSubtitle != null) ? Player.AVPlayerObj.totalNumOfSubtitle : "Unknown"; +}; + +Player.nextAudioTrack = function () { + + Player.curAudioTrack = (Player.curAudioTrack +1 ) % Player.AVPlayerObj.totalNumOfAudio; + + try { + if (Player.AVPlayerObj.setAudioStreamID(Player.curAudioTrack) == false) { + Main.logToServer("Player.nextAudioTrack: Failed to set audio track to " + Player.curAudioTrack); + Display.showPopup("Player.nextAudioTrack: Failed to set audio track to " + Player.curAudioTrack); + } + else { + Main.logToServer("Player.nextAudioTrack: Track= " + Player.curAudioTrack); + Notify.showNotify("Audio Track " + Player.curAudioTrack, true); + } + + } + catch (e) { + Main.logToServer("Player.nextAudioTrack: Caught Error: " + e.message); + Display.showPopup("Player.nextAudioTrack: Caught Error: " + e.message); + } }; +Player.nextSubtitleTrack = function () { + if (!Player.AVPlayerObj.getSubtitleAvailable() ) { + return; + } + + Player.curSubtitleTrack = (Player.curSubtitleTrack +1 ) % (Player.AVPlayerObj.totalNumOfSubtitle +1); -Player.setBuffer = function (){ - var res = true; + try { + if (Player.AVPlayerObj.setSubtitleStreamID(Player.curSubtitleTrack) == false) { + Main.logToServer("Player.nextSubtitleTrack: Failed to set subtitle track to " + Player.curSubtitleTrack); + Display.showPopup("Player.nextSubtitleTrack: Failed to set subtitle track to " + Player.curSubtitleTrack); + } + else { + Main.logToServer("Player.nextSubtitleTrack: Track= " + Player.curSubtitleTrack); + Notify.showNotify("Subtitle " + Player.curSubtitleTrack, true); + } + + } + catch (e) { + Main.logToServer("Player.nextSubtitleTrack: Caught Error: " + e.message); + Display.showPopup("Player.nextSubtitleTrack: Caught Error: " + e.message); + } + +}; + +Player.getBuffer = function (){ + var res = {}; var buffer_byte = (Config.totalBufferDuration * Config.tgtBufferBitrate) / 8.0; var initial_buf = Config.initialBuffer; if (Player.isLive == true) initial_buf = initial_buf *4; - Main.logToServer("Seting TotalBufferSize to " + Math.round(buffer_byte) +"Byte dur= " +Config.totalBufferDuration + "sec init_buffer_perc= " +initial_buf +"% pend_buffer_perc= " +Config.pendingBuffer + "% initialTimeOut= " +Config.initialTimeOut + "sec"); - - //The SetTotalBufferSize function sets the streaming buffer size of media player. - res = this.plugin.SetTotalBufferSize(Math.round(buffer_byte)); - if (res == false) { - Display.showPopup("SetTotalBufferSize(" + Math.round(buffer_byte) +") returns error"); - Main.logToServer("SetTotalBufferSize(" + Math.round(buffer_byte) +") returns error"); - } - - // The SetInitialBuffer function sets the first buffering size as percentage of buffer size before starting playback. - res = this.plugin.SetInitialBuffer(Math.round( buffer_byte * initial_buf/ 100.0)); - if (res == false) { - Display.showPopup("SetInitialBuffer(" + Math.round(buffer_byte * initial_buf/ 100.0) +") returns error"); - Main.logToServer("SetInitialBuffer(" + Math.round(buffer_byte * initial_buf/ 100.0) +") returns error"); - } - //he SetInitialTimeOut function sets the maximum time out value for initial buffering before starting playback. - res = this.plugin.SetInitialTimeOut(Config.initialTimeOut); - if (res == false) { - Display.showPopup("SetInitialTimeOut(" + 2 +") returns error"); - Main.logToServer("SetInitialTimeOut(" + 2 +") returns error"); - } + res.totalBufferSize = Math.round(buffer_byte); + res.initialBufferSize = Math.round( buffer_byte * initial_buf/ 100.0); + res.pendingBufferSize = Math.round(buffer_byte * Config.pendingBuffer /100.0); - // The SetPendingBuffer function sets the size of a buffer as percentage of total buffer size that media player goes out from buffering status. - res = this.plugin.SetPendingBuffer(Math.round(buffer_byte * Config.pendingBuffer /100.0)); - if (res == false) { - Display.showPopup("SetPendingBuffer(" + Math.round(buffer_byte * Config.pendingBuffer /100.0) +") returns error"); - Main.logToServer("SetPendingBuffer(" + Math.round(buffer_byte * Config.pendingBuffer /100.0) +") returns error"); - } + Main.logToServer("Setting totalBufferSize= " + res.totalBufferSize +"Byte initialBufferSize= " +res.initialBufferSize + "byte pendingBufferSize= " +res.pendingBufferSize +"byte " ); + + return res; }; Player.setVideoURL = function(url) { @@ -282,6 +313,19 @@ Player.setCurrentPlayTimeOffset = function(val) { // Display.showPopup("CurrentPlayTimeOffset= " + this.cptOffset); }; +Player.setDuration = function () { + Player.totalTime = Data.getCurrentItem().childs[Main.selectedVideo].payload.dur * 1000; + Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); +}; + +Player.getDuration = function () { + return Player.totalTime; +}; + +Player.getDurationStr = function () { + return Player.totalTimeStr; +}; + Player.playVideo = function(resume_pos) { if (Config.deviceType != 0) { Display.showPopup ("Only supported for TVs"); @@ -291,23 +335,20 @@ Player.playVideo = function(resume_pos) { Main.log("No videos to play"); } else { - Player.setFullscreen(); Player.bufferState = 0; Display.bufferUpdate(); + Player.AVPlayerObj.show(); Spinner.show(); -// Player.curPlayTime = 0; Display.updatePlayTime(); Display.status("Play"); Display.hideStatus(); -// Display.showStatus(); Display.showProgress(); this.state = this.PLAYING; -// Player.setBuffer(); Player.ResetTrickPlay(); Player.skipDuration = Config.skipDuration; // reset @@ -315,35 +356,43 @@ Player.playVideo = function(resume_pos) { this.requestStartTime = new Date().getTime(); if (Player.isRecording == false) { - if (resume_pos == -1) { -// this.plugin.Play( this.url ); - this.plugin.InitPlayer(this.url); - Player.setBuffer(); - this.plugin.StartPlayback(); - } - else { - Main.logToServer ("Player.playVideo: resume_pos= " +resume_pos); - Player.setBuffer(); - this.plugin.ResumePlay(this.url, resume_pos); - } + if (resume_pos == -1) + resume_pos = 0; + + try { + // Player.AVPlayerObj.open (this.url, Player.getBuffer()); + Player.AVPlayerObj.open (this.url); + Player.AVPlayerObj.play(Player.onPlaySuccess, Player.onError, resume_pos); + } + catch (e) { + Main.log("Player.play: Error caugth " + e.msg); + Main.logToServer("Player.play: Error caugth " + e.msg); + Display.showPopup("Player.play: Error caugth " + e.msg); + } + } else { if (resume_pos == -1) resume_pos = 0; Player.setCurrentPlayTimeOffset(resume_pos * 1000.0); -// this.plugin.Play( this.url+ "?time=" + resume_pos ); - this.plugin.InitPlayer(this.url+ "?time=" + resume_pos ); - Player.setBuffer(); - this.plugin.StartPlayback(); + try { +// Player.AVPlayerObj.open(this.url+ "?time=" + resume_pos, Player.getBuffer() ); + Player.AVPlayerObj.open(this.url+ "?time=" + resume_pos ); + Player.AVPlayerObj.play(Player.onPlaySuccess , Player.onError); + } + catch(e) { + Main.log("Player.play: Error: " + e.message); + Main.logToServer("Player.play: Error: " + e.message); + Display.showPopup("Player.play: Error: " + e.message); + }; Main.logToServer("Player.play with ?time=" + resume_pos); } if ((this.mFormat == this.eHLS) && (this.isLive == false)){ Notify.showNotify("No Trickplay", true); } - Audio.plugin.SetSystemMute(false); - pluginObj.setOffScreenSaver(); - this.pluginBD.DisplayVFD_Show(0100); // Play +// Audio.plugin.SetSystemMute(false); +// pluginObj.setOffScreenSaver(); } }; @@ -354,21 +403,32 @@ Player.pauseVideo = function() { this.state = this.PAUSED; Display.status("Pause"); Display.showStatus(); - var res = this.plugin.Pause(); + var res = false; + try { + res = Player.AVPlayerObj.pause(); + } + catch(e) { + Main.log("Player.pause: Error " + e.message); + Main.logToServer("Player.pause: Error " + e.message); + Display.showPopup("Player.pause: Error " + e.message); + } if (res == false) Display.showPopup("pause ret= " + ((res == true) ? "True" : "False")); - pluginObj.setOnScreenSaver(); - this.pluginBD.DisplayVFD_Show(0102); // Pause +// pluginObj.setOnScreenSaver(); }; Player.stopVideo = function() { if (this.state != this.STOPPED) { + this.state = this.STOPPED; Display.status("Stop"); - this.plugin.Stop(); - -// Display.setTime(0); + Player.AVPlayerObj.hide(); + try { + Player.AVPlayerObj.stop(); + } + catch (e) { + } if (this.stopCallback) { Main.log(" StopCallback"); this.stopCallback(); @@ -378,8 +438,7 @@ Player.stopVideo = function() { Display.resetAtStop(); Spinner.hide(); - pluginObj.setOnScreenSaver(); - this.pluginBD.DisplayVFD_Show(0101); // Stop +// pluginObj.setOnScreenSaver(); } else { Main.log("Ignoring stop request, not in correct state"); @@ -392,11 +451,15 @@ Player.resumeVideo = function() { this.state = this.PLAYING; Display.status("Play"); Display.hideStatus(); - var res = this.plugin.Resume(); + var res = false; + try { + res = Player.AVPlayerObj.resume(); + } + catch (e){ + } if (res == false) Display.showPopup("resume ret= " + ((res == true) ? "True" : "False")); - pluginObj.setOffScreenSaver(); - this.pluginBD.DisplayVFD_Show(0100); // Play +// pluginObj.setOffScreenSaver(); }; Player.jumpToVideo = function(percent) { @@ -413,7 +476,7 @@ Player.jumpToVideo = function(percent) { //TODO: the totalTime should be set already if (this.totalTime == -1 && this.isLive == false) - this.totalTime = this.plugin.GetDuration(); + this.totalTime = Player.AVPlayerObj.getDuration(); var tgt = Math.round(((percent-2)/100.0) * this.totalTime/ 1000.0); var res = false; @@ -421,34 +484,26 @@ Player.jumpToVideo = function(percent) { if (Player.isRecording == false) { if (tgt > (Player.curPlayTime/1000.0)) - res = this.plugin.JumpForward(tgt - (Player.curPlayTime/1000.0)); + res = Player.AVPlayerObj.jumpForward(tgt - (Player.curPlayTime/1000.0)); else - res = this.plugin.JumpBackward( (Player.curPlayTime/1000.0)- tgt); + res = Player.AVPlayerObj.jumpBackward( (Player.curPlayTime/1000.0)- tgt); } else { - this.plugin.Stop(); + Player.AVPlayerObj.stop(); var old = Player.curPlayTime; Player.setCurrentPlayTimeOffset(tgt * 1000.0); -// res = this.plugin.Play( this.url+ "?time=" + tgt ); - this.plugin.InitPlayer(this.url+ "?time=" + tgt ); - res = this.plugin.StartPlayback(); +// Player.AVPlayerObj.open(this.url+ "?time=" + tgt, Player.getBuffer() ); + Player.AVPlayerObj.open(this.url+ "?time=" + tgt); + res = Player.AVPlayerObj.play(Player.onPlaySuccess , Player.onError); Main.logToServer("Player.play with ?time=" + tgt); if (res == false) Player.setCurrentPlayTimeOffset(old); - - // set currentPlayTimeOffsert to tgt - // set new url with time - // play } Main.logToServer("Player.jumpToVideo: jumpTo= " + percent + "% of " + (this.totalTime/1000) + "sec tgt = " + tgt + "sec cpt= " + (this.curPlayTime/1000) +"sec" + " res = " + res); -// Display.showPopup("jumpToVideo= " + percent + "% of " + (this.totalTime/1000) + "sec
    --> tgt = " + tgt + "sec curPTime= " + (this.curPlayTime/1000)+"sec"); -// Display.showStatus(); - // this.plugin.Stop(); -// var res = this.plugin.ResumePlay(this.url, tgt ); if (res == false) Display.showPopup("ResumePlay ret= " + ((res == true) ? "True" : "False")); }; @@ -458,17 +513,17 @@ Player.skipForwardVideo = function() { Display.showProgress(); var res = false; if (Player.isRecording == false) - res = this.plugin.JumpForward(Player.skipDuration); + res = Player.AVPlayerObj.jumpForward(Player.skipDuration); else { Spinner.show(); this.bufferState = 0; - this.plugin.Stop(); + Player.AVPlayerObj.stop(); var old = Player.curPlayTime; var tgt = (Player.curPlayTime/1000.0) + Player.skipDuration; Player.setCurrentPlayTimeOffset(tgt * 1000.0); -// res = this.plugin.Play( this.url+ "?time=" + tgt ); - this.plugin.InitPlayer(this.url+ "?time=" + tgt ); - res = this.plugin.StartPlayback(); +// Player.AVPlayerObj.open(this.url+ "?time=" + tgt, Player.getBuffer()); + Player.AVPlayerObj.open(this.url+ "?time=" + tgt); + res = Player.AVPlayerObj.play(Player.onPlaySuccess , Player.onError); Main.logToServer("Player.skipForwardVideo with ?time=" + tgt); if (res == false) @@ -484,18 +539,18 @@ Player.skipBackwardVideo = function() { Display.showProgress(); var res = false; if (Player.isRecording == false) - res = this.plugin.JumpBackward(Player.skipDuration); + res = Player.AVPlayerObj.jumpBackward(Player.skipDuration); else { Spinner.show(); this.bufferState = 0; - this.plugin.Stop(); + Player.AVPlayerObj.stop(); var tgt = (Player.curPlayTime/1000.0) - Player.skipDuration; if (tgt < 0) tgt = 0; Player.setCurrentPlayTimeOffset(tgt * 1000.0); -// res = this.plugin.Play( this.url+ "?time=" + tgt ); - this.plugin.InitPlayer(this.url+ "?time=" + tgt ); - res = this.plugin.StartPlayback(); +// Player.AVPlayerObj.open(this.url+ "?time=" + tgt, Player.getBuffer()); + Player.AVPlayerObj.open(this.url+ "?time=" + tgt); + res = Player.AVPlayerObj.play(Player.onPlaySuccess , Player.onError); Main.logToServer("Player.skipBackwardVideo with ?time=" + tgt); if (res == false) @@ -548,7 +603,6 @@ Player.fastForwardVideo = function() { this.trickPlaySpeed = 1; this.trickPlayDirection = 1; } - } if (Player.isRecording == true) { if (this.trickPlaySpeed > 2) @@ -566,7 +620,7 @@ Player.fastForwardVideo = function() { Main.log("FastForward: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); Main.logToServer("FastForward: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); - if (this.plugin.SetPlaybackSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { + if (Player.AVPlayerObj.setSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { Display.showPopup("trick play returns false. Reset Trick-Play" ); Player.ResetTrickPlay(); // this.trickPlaySpeed = 1; @@ -575,11 +629,9 @@ Player.fastForwardVideo = function() { }; Player.RewindVideo = function() { - if ((this.trickPlayDirection == 1) && (this.trickPlaySpeed == 1)){ this.trickPlayDirection = -1; - this.trickPlaySpeed = this.trickPlaySpeed * 2; - + this.trickPlaySpeed = this.trickPlaySpeed * 2; } else if (this.trickPlayDirection == 1) { // I am in fast forward mode, so decrease @@ -588,7 +640,6 @@ Player.RewindVideo = function() { this.trickPlaySpeed = 1; this.trickPlayDirection = -1; } - } else this.trickPlaySpeed = this.trickPlaySpeed * 2; @@ -597,7 +648,6 @@ Player.RewindVideo = function() { if (this.trickPlayDirection <0 ) Player.ResetTrickPlay(); return; -// this.trickPlayDirection = 1; } if (this.trickPlaySpeed != 1) { @@ -610,23 +660,15 @@ Player.RewindVideo = function() { Main.log("Rewind: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); - if (this.plugin.SetPlaybackSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { + if (Player.AVPlayerObj.setSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { Display.showPopup("trick play returns false. Reset Trick-Play" ); Player.ResetTrickPlay(); -// this.trickPlaySpeed = 1; -// this.trickPlayDirection = 1; } Main.log("Rewind: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); Main.logToServer("Rewind: Direction= " + ((this.trickPlayDirection == 1) ? "Forward": "Backward") + "trickPlaySpeed= " + this.trickPlaySpeed); - /* if (this.plugin.SetPlaybackSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { - Display.showPopup("trick play returns false. Reset Trick-Play" ); - this.trickPlaySpeed = 1; - this.trickPlayDirection = 1; - } -*/ }; Player.ResetTrickPlay = function() { @@ -634,7 +676,7 @@ Player.ResetTrickPlay = function() { this.trickPlaySpeed = 1; this.trickPlayDirection = 1; Main.log("Reset Trickplay " ); - if (this.plugin.SetPlaybackSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { + if (Player.AVPlayerObj.setSpeed(this.trickPlaySpeed * this.trickPlayDirection) == false) { Display.showPopup("trick play returns false. Reset Trick-Play" ); this.trickPlaySpeed = 1; this.trickPlayDirection = 1; @@ -656,30 +698,44 @@ Player.getState = function() { // ------------------------------------------------ // Global functions called directly by the player //------------------------------------------------ +Player.onResolutionChanged = function () { + Main.log('Player.onResolutionChanged : '); +}; + +Player.onError = function () { + Main.log('Player.onError: ' ); + Main.logToServer('Player.onError: ' ); +}; + + +Player.onPlaySuccess = function () { + Player.OnStreamInfoReady(); +}; + +Player.onCropSuccess = function () { + Main.log('Player.onCropSuccess: '); +// Main.logToServer('Player.onCropSuccess: '); +}; + Player.onBufferingStart = function() { - Main.logToServer("Buffer Start: cpt= " + (Player.curPlayTime/1000.0) +"sec"); +// Main.logToServer("Buffer Start: cpt= " + (Player.curPlayTime/1000.0) +"sec"); + Main.log("Buffer Start: cpt= " + (Player.curPlayTime/1000.0) +"sec"); Player.bufferStartTime = new Date().getTime(); if (this.requestStartTime != 0) { - Main.logToServer("Player.onBufferingStart Server RTT= " + (Player.bufferStartTime -this.requestStartTime ) + "ms"); this.requestStartTime = 0; } Spinner.show(); Player.bufferState = 0; Display.bufferUpdate(); - // should trigger from here the overlay + Display.showProgress(); Display.status("Buffering..."); -// Display.showStatus(); }; -Player.onBufferingProgress = function(percent) -{ - // should trigger from here the overlay -// Display.status("Buffering:" + percent + "%"); - +Player.onBufferingProgress = function(percent) { Player.bufferState = percent; Display.bufferUpdate(); Display.showProgress(); @@ -691,6 +747,7 @@ Player.onBufferingComplete = function() { Spinner.hide(); Main.logToServer("onBufferingComplete cpt= " +(Player.curPlayTime/1000.0) +"sec - Buffering Duration= " + (new Date().getTime() - Player.bufferStartTime) + " ms"); + Main.log("onBufferingComplete cpt= " +(Player.curPlayTime/1000.0) +"sec - Buffering Duration= " + (new Date().getTime() - Player.bufferStartTime) + " ms"); Player.bufferState = 100; Display.bufferUpdate(); @@ -700,14 +757,16 @@ Player.onBufferingComplete = function() { // or I should set it according to the aspect ratio Display.hide(); -// Main.logToServer("onBufferingComplete "); -/* Player.pauseVideo(); - window.setTimeout(Player.resumeVideo, 1000); */ }; Player.OnCurrentPlayTime = function(time) { - Player.curPlayTime = parseInt(time) + parseInt(Player.cptOffset); + Main.log ("Player.OnCurrentPlayTime " + time.millisecond); + + if (typeof time == "number") + Player.curPlayTime = parseInt(time) + parseInt(Player.cptOffset); + else + Player.curPlayTime = parseInt(time.millisecond) + parseInt(Player.cptOffset); // Update the Current Play Progress Bar Display.updateProgressBar(); @@ -715,24 +774,23 @@ Player.OnCurrentPlayTime = function(time) { if (Player.isRecording == true) { Display.updateRecBar(Player.startTime, Player.duration); } + Main.log ("Player.OnCurrentPlayTime: curPlayTimeStr= " + Player.curPlayTimeStr); Player.curPlayTimeStr = Display.durationString(Player.curPlayTime / 1000.0); + Display.updatePlayTime(); }; Player.OnStreamInfoReady = function() { Main.log("*** OnStreamInfoReady ***"); +// Main.logToServer("*** OnStreamInfoReady ***"); Player.setFullscreen(); -// Main.logToServer("GetCurrentBitrates= " + Player.plugin.GetCurrentBitrates()); if ((Player.isLive == false) && (Player.isRecording == false)) { - Player.totalTime = Player.plugin.GetDuration(); + Player.totalTime = Player.AVPlayerObj.getDuration(); } -// Player.curPlayTimeStr = Display.durationString(Player.totalTime / 1000.0); Player.totalTimeStr =Display.durationString(Player.totalTime / 1000.0); + Main.log("Player.totalTimeStr= " + Player.totalTimeStr); -// var height = Player.plugin.GetVideoHeight(); -// var width = Player.plugin.GetVideoWidth(); -// Main.logToServer("Resolution= " + width + " x " + height ); }; Player.OnRenderingComplete = function() { diff --git a/smarttv-client/Javascript/Server.js b/smarttv-client/Javascript/Server.js index ec969b8..77b8cff 100755 --- a/smarttv-client/Javascript/Server.js +++ b/smarttv-client/Javascript/Server.js @@ -7,6 +7,7 @@ var Server = { XHRObj : null }; + Server.init = function() { var success = true; @@ -90,8 +91,8 @@ Server.updateVdrStatus = function (){ 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 used = $(data).find('used').text() / 1024.0; +// var percent = $(data).find('percent').text(); var unit = "GB"; var free_str = free.toFixed(2); @@ -107,7 +108,7 @@ Server.updateVdrStatus = function (){ Main.log("VdrStatus: Error"); } }); -} +}; Server.getResume = function (guid) { @@ -179,3 +180,45 @@ Server.deleteRecording = function(guid) { } }); }; + + +Server.notifyServer = function (state) { + Main.log("Server.notifyServer state="+state +"&mac=" + Network.ownMac + "&ip=" + Network.ownIp); + $.ajax({ + url: Config.serverUrl + "/clients?state="+state +"&mac=" + Network.ownMac + "&ip=" + Network.ownIp, + type : "GET", + success : function(data, status, XHR ) { + Main.log("Config.notifyServer OK" ) ; + }, + error : function (XHR, status, error) { + Main.log("Config.notifyServer failed" ) ; + } + }); +}; + +var HeartbeatHandler = { + timeoutObj : null, + isActive : false +}; + + +HeartbeatHandler.start = function(){ + if (this.isActive ==true) + window.clearTimeout(this.timeoutObj); + + this.isActive = true; + HeartbeatHandler.update(); +}; + +HeartbeatHandler.update = function() { + Server.notifyServer("running"); + this.timeoutObj = window.setTimeout(function() {HeartbeatHandler.update(); }, (60*1000)); // once every 1min +}; + +HeartbeatHandler.stop = function(){ + if (this.isActive == false ) + return; + + window.clearTimeout(this.timeoutObj); + this.isActive = false; +}; diff --git a/smarttv-client/Javascript/Urls.js b/smarttv-client/Javascript/Urls.js new file mode 100644 index 0000000..f7c2e36 --- /dev/null +++ b/smarttv-client/Javascript/Urls.js @@ -0,0 +1,257 @@ +var UrlsFetcher = { + qualities : {17 : "144p", 36 : "240p", 18 : "360p", 22 : "720p", 37 : "1080p" }, + preference : [36, 18, 22, 37], + usable : [], + curQuality : 2, + fv : "", + urls : {}, + guids : [], + guidDict :{}, + issuedRequests : 0, + caughtResponses : 0, + dataReceivedCallback : null, + autoplay : "" + }; + +UrlsFetcher.resetCurQuality = function () { + UrlsFetcher.curQuality = 2; + if ((Config.preferredQuality > 0) && (Config.preferredQuality < UrlsFetcher.preference.length)) { + UrlsFetcher.curQuality = Config.preferredQuality; + Main.log("Setting UrlsFetcher.curQuality= " + UrlsFetcher.curQuality); + } +}; + +UrlsFetcher.fetchUrlList = function() { + Main.log(" --- UrlsFetcher.fetchUrlList --- "); + + UrlsFetcher.resetCurQuality(); + + Data.sort = true; + Data.createAccessMap = true; + UrlsFetcher.issuedRequests = 0; + UrlsFetcher.caughtResponses = 0; + UrlsFetcher.guids = []; + UrlsFetcher.fetchUrls(Config.serverUrl + "/urls.xml"); +}; + +UrlsFetcher.fetchUrls = function(url) { + Main.log("UrlsFetcher.fetchUrls url= " + url); + + $.ajax({ + url: url, + type : "GET", + success : function(data, status, XHR ) { + Main.log("UrlsFetcher.fetchUrls Success Response - status= " + status + " mime= " + XHR.responseType + " data= "+ data); + + $(data).find("item").each(function () { + var guid = $(this).find('guid').text(); + Main.log("UrlsFetcher.fetchUrls : guid= " + guid ); + UrlsFetcher.guids.push(guid); +// UrlsFetcher.getYtDescription(guid); + }); // each + // Done + UrlsFetcher.getAllDescription(); + }, + error : function (jqXHR, status, error) { + Main.log("UrlsFetcher.fetchUrls Error Response - status= " + status + " error= "+ error); + }, + parsererror : function () { + Main.log("UrlsFetcher.fetchUrls parserError " ); + + } + }); + +}; + +UrlsFetcher.appendGuid = function (guid) { + Main.log("UrlsFetcher.appendGuid guid= " + guid + " UrlsFetcher.guids.length= " + UrlsFetcher.guids.length); + + UrlsFetcher.guids.push(guid); + UrlsFetcher.guidDict[guid] = {}; + UrlsFetcher.guidDict[guid]["num"] = (UrlsFetcher.guids.length); + UrlsFetcher.guidDict[guid]["start"] = (UrlsFetcher.guids.length); + UrlsFetcher.getYtDescription(guid); + Main.logToServer("Data.getVideoCount= " + Data.getVideoCount()); +}; + + + +UrlsFetcher.getAllDescription = function () { + Main.log("UrlsFetcher.getAllDescription guids.length= " + UrlsFetcher.guids.length); + for (var i = 0; i < UrlsFetcher.guids.length; i++) { + Main.log("Calling " + UrlsFetcher.guids[i]); + var guid = UrlsFetcher.guids[i]; + UrlsFetcher.guidDict[guid] = {}; + UrlsFetcher.guidDict[guid]["num"] = (i+1); + UrlsFetcher.guidDict[guid]["start"] = (i+1); + + UrlsFetcher.getYtDescription(guid); +// UrlsFetcher.getYtDescription(UrlsFetcher.guids[i]); + } + Main.log("UrlsFetcher.getAllDescription Done" ); +}; + +UrlsFetcher.handleResponse = function (success) { + UrlsFetcher.caughtResponses++; + Main.log("UrlsFetcher.handleResponse caughtResponses= " + UrlsFetcher.caughtResponses); + + if (UrlsFetcher.caughtResponses >= UrlsFetcher.issuedRequests) { + //Done + Main.logToServer("UrlsFetcher.handleResponse - Done: " + UrlsFetcher.caughtResponses); + + if (UrlsFetcher.autoplay == "") { + Data.completed(false); + if (UrlsFetcher.dataReceivedCallback) { + UrlsFetcher.dataReceivedCallback(); + } + } + else { +// Main.logToServer("UrlsFetcher.handleResponse Autoplay " + UrlsFetcher.autoplay); + Main.log("UrlsFetcher.handleResponse Autoplay " + UrlsFetcher.autoplay); + Spinner.hide(); + + // autoplay: + if (success == true) { +// Display.setVideoList(Main.selectedVideo, Main.selectedVideo); + + Main.logToServer("UrlsFetcher.handleResponse play guid= " + UrlsFetcher.autoplay); + var num = UrlsFetcher.guidDict[UrlsFetcher.autoplay]["num"]; + DirectAccess.selectNewChannel(num); + + var first_idx = (Data.getVideoCount() < Display.getNumberOfVideoListItems()) ?0: Data.getVideoCount() - Display.getNumberOfVideoListItems(); + Display.setVideoList(Main.selectedVideo, first_idx); + + } + else { + // remove + Main.logToServer("Error - UrlsFetcher.handleResponse: remove guid= " + UrlsFetcher.autoplay); + delete UrlsFetcher.guidDict[UrlsFetcher.autoplay]; + } + + UrlsFetcher.autoplay = ""; + + } + } +}; + +UrlsFetcher.getYtDescription = function (vid) { + Main.log("UrlsFetcher.getYtDescription vid= " + vid); + var url = 'http://gdata.youtube.com/feeds/api/videos/'+vid+'?v=2&alt=json'; + Main.log("UrlsFetcher.getYtDescription url= " + url); + UrlsFetcher.issuedRequests ++; + + $.ajax({ + url: url, + type : "GET", + dataType: "json", + success: function(data, status, XHR ) { + var title = data.entry.title.$t; + var description = data.entry.media$group.media$description.$t; + var duration = data.entry.media$group.yt$duration.seconds; + var guid = data.entry.media$group.yt$videoid.$t; + + var num = UrlsFetcher.guidDict[guid]["num"]; + var start = UrlsFetcher.guidDict[guid]["start"]; + + Data.addItem( [title], {link : "", prog: "", desc: description, guid : guid, start: start, + dur: duration, ispes : "false", isnew : "false", fps : 0, num : num}); + Main.log("UrlsFetcher.getYtDescription: guid= " + guid + "was inserted at= " + Data.directAccessMap[num]); + Main.logToServer("UrlsFetcher.getYtDescription: guid= " + guid + "was inserted at= " + Data.directAccessMap[num]); + UrlsFetcher.handleResponse(true); + //insert into data-base + }, + error : function(jqXHR, status, error) { + Main.log("UrlsFetcher.getYtDescription: Error"); + UrlsFetcher.handleResponse(false); + } + + }); + +}; + + + +UrlsFetcher.getYtVideoUrl = function (vid) { + Main.log("UrlsFetcher.getYtVideoUrl: vid= " + vid); + + //Reset + UrlsFetcher.fv = ""; + UrlsFetcher.usable = []; + UrlsFetcher.urls = {}; // reset + UrlsFetcher.resetCurQuality(); +// UrlsFetcher.curQuality = 2; + + $.get('http://www.youtube.com/get_video_info?video_id=' + vid, function(data) { + UrlsFetcher.fv = data; + var status = UrlsFetcher.getQueryAttrib(UrlsFetcher.fv, "status"); + if (status == "fail") { + var reason = UrlsFetcher.getQueryAttrib(UrlsFetcher.fv, "reason"); + Main.log("reason: " + reason); + Main.log("fv= "+UrlsFetcher.fv); + Display.showPopup(reason); + return; + } + + UrlsFetcher.extractYtUrl(vid); + + }); +}; + + +UrlsFetcher.getQueryAttrib = function (querystring, item) { + var filter = new RegExp( item + "=([^&]+)" ); + return unescape( querystring.match(filter)[1] ); +}; + +UrlsFetcher.extractYtUrl = function (vid) { + var stream_map = UrlsFetcher.getQueryAttrib(UrlsFetcher.fv, "url_encoded_fmt_stream_map").split(","); + var itags = []; + + for (var i = 0; i < stream_map.length; i++) { + var url = + UrlsFetcher.getQueryAttrib(stream_map[i], "url") + "&signature=" + + UrlsFetcher.getQueryAttrib(stream_map[i], "sig"); + var itag= url.match(/itag=(\d+)/)[1]; + itags.push(itag); + if ( itag in UrlsFetcher.qualities) { + // store only the wanted itags + UrlsFetcher.urls[itag] = url; + UrlsFetcher.usable.push(itag); + } + }; + if (UrlsFetcher.usable.length == 0) { + // Nothing to play + Display.showPopup("No supported format found for this clip."); + Player.stop(); + Spinner.hide(); + Display.resetAtStop(); + Display.show(); + } + // Done: Play now + Main.log(vid+ ": Available Qualities= "+ itags.toString()); + Main.logToServer(vid+ ": Available Qualities= "+ itags.toString()); + + if (UrlsFetcher.usable.length == 0) { + Display.showPopup ("Error: No Suitable Formats founds " ); + return; + } + var ok = false; + + + while (!ok) { + if (UrlsFetcher.preference[UrlsFetcher.curQuality] in UrlsFetcher.urls) { + Player.setVideoURL(UrlsFetcher.urls[UrlsFetcher.preference[UrlsFetcher.curQuality]]); + ok = true; + Notify.showNotify("Quality: " + UrlsFetcher.qualities[UrlsFetcher.preference[UrlsFetcher.curQuality]], true); + } + else + UrlsFetcher.curQuality --; + if (UrlsFetcher.curQuality <0) { + Player.setVideoURL(UrlsFetcher.urls[UrlsFetcher.usable[0]]); + Notify.showNotify("Quality: " + UrlsFetcher.qualities[UrlsFetcher.usable[0]], true); + ok = true; + } + } + Player.playVideo(-1); + +}; diff --git a/smarttv-client/config.xml b/smarttv-client/config.xml index cd6d56c..c3f8550 100755 --- a/smarttv-client/config.xml +++ b/smarttv-client/config.xml @@ -9,7 +9,7 @@ Images/icon/SmartTvWeb_115.png Images/icon/SmartTvWeb_85.png Images/icon/SmartTvWeb_95.png - 0.92 + 0.93 y y diff --git a/smarttv-client/index.html b/smarttv-client/index.html index 3a2f188..5172cac 100755 --- a/smarttv-client/index.html +++ b/smarttv-client/index.html @@ -16,6 +16,7 @@ + @@ -23,7 +24,7 @@ - + @@ -35,13 +36,16 @@ + + - - + + + @@ -51,7 +55,6 @@ - @@ -61,6 +64,7 @@ +
    @@ -81,7 +85,8 @@
    1: Live
    2: Recordings
    3: Media
    -
    4: Options
    +
    4: YouTube
    +
    5: Options
    @@ -169,13 +174,16 @@
    - - ", {style: "width:100%; align:center"}); + var cell = $("", {style: "width:100%; align:center"}); + cell = $("
    + + - + +
    +
    +
    -- cgit v1.2.3 From cc00a9c44bde4527dafc0143cb3055404f102da4 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 6 Apr 2013 13:06:43 +0200 Subject: added mngurls and httpclient files. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bc78cfa..3798c6c 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE ### The object files (add further files here): -OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o log.o url.o stvw_cfg.o +OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o httpclient.o mngurls.o log.o url.o stvw_cfg.o OBJS2 = -- cgit v1.2.3 From 7fe1953d5bb204ec97e8f5032b0f7ec5d0e1cbda Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 21 Apr 2013 16:20:52 +0200 Subject: Plugin code refactoring. --- Makefile | 10 +- httpresource.c | 2867 ++++----------------------------------------------- httpresource.h | 98 +- httpresource_base.h | 2 - mngurls.c | 22 + mngurls.h | 8 +- responsebase.c | 162 +++ responsebase.h | 77 ++ responsefile.c | 174 ++++ responsefile.h | 51 + responsememblk.c | 1689 ++++++++++++++++++++++++++++++ responsememblk.h | 92 ++ responsevdrdir.c | 781 ++++++++++++++ responsevdrdir.h | 64 ++ 14 files changed, 3334 insertions(+), 2763 deletions(-) create mode 100644 responsebase.c create mode 100644 responsebase.h create mode 100644 responsefile.c create mode 100644 responsefile.h create mode 100644 responsememblk.c create mode 100644 responsememblk.h create mode 100644 responsevdrdir.c create mode 100644 responsevdrdir.h diff --git a/Makefile b/Makefile index 3798c6c..f957795 100644 --- a/Makefile +++ b/Makefile @@ -16,12 +16,12 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri CXX ?= g++ -ifdef DEBUG +#ifdef DEBUG CXXFLAGS ?= -g -O0 -fPIC -Wall -Woverloaded-virtual #-Werror -else -CXXFLAGS ?= -fPIC -Wall -Woverloaded-virtual #-Werror +#else +#CXXFLAGS ?= -fPIC -Wall -Woverloaded-virtual #-Werror #CXXFLAGS ?= -O2 -fPIC -Wall -Woverloaded-virtual #-Werror -endif +#endif ### The directory environment: @@ -55,7 +55,7 @@ DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE ### The object files (add further files here): -OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o httpclient.o mngurls.o log.o url.o stvw_cfg.o +OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o httpclient.o mngurls.o log.o url.o stvw_cfg.o responsebase.o responsefile.o responsevdrdir.o responsememblk.o OBJS2 = diff --git a/httpresource.c b/httpresource.c index 2da4345..e20bf00 100755 --- a/httpresource.c +++ b/httpresource.c @@ -42,6 +42,10 @@ #include "stvw_cfg.h" #include "mngurls.h" #include "url.h" +#include "responsebase.h" +#include "responsefile.h" +#include "responsevdrdir.h" +#include "responsememblk.h" #ifndef STANDALONE #include @@ -56,10 +60,10 @@ #include #endif -#define PROTOCOL "HTTP/1.1" -#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" -#define MAXLEN 4096 +//TODO: Should I increase the MAXLEN from 4k to 64k? +//#define MAXLEN 4096 +//#define MAXLEN 32768 #define OKAY 0 #define ERROR (-1) #define DEBUG_REGHEADERS @@ -72,71 +76,21 @@ using namespace std; -class cResumeEntry { - public: - string mFilename; - float mResume; - - friend ostream& operator<<(ostream& out, const cResumeEntry& o) { - out << "mFilename= " << o.mFilename << " mResume= " << o.mResume << endl; - return out; - }; - cResumeEntry():mFilename(), mResume(-1.0) {}; -}; - - -struct sVdrFileEntry { - uint64_t sSize; - uint64_t sFirstOffset; - int sIdx; - - sVdrFileEntry () {}; - sVdrFileEntry (uint64_t s, uint64_t t, int i) - : sSize(s), sFirstOffset(t), sIdx(i) {}; -}; - - -struct sTimerEntry { - string name; - time_t startTime; - int duration; -sTimerEntry(string t, time_t s, int d) : name(t), startTime(s), duration(d) {}; -}; - -// 8 Byte Per Entry -struct tIndexPes { - uint32_t offset; - uint8_t type; // standalone - uint8_t number; // standalone - uint16_t reserved; - }; - - -// 8 Byte per entry -struct tIndexTs { - uint64_t offset:40; // up to 1TB per file (not using long long int here - must definitely be exactly 64 bit!) - int reserved:7; // reserved for future use - int independent:1; // marks frames that can be displayed by themselves (for trick modes) - 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), + 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), + mConnected(true), mConnState(WAITING), mReadBuffer(), mMethod(), + // mBlkData(NULL), mBlkPos(0), mBlkLen(0), mPath(), mVersion(), protocol(), mReqContentLength(0), mPayload(), mUserAgent(), - mAcceptRanges(true), rangeHdr(), mFileSize(-1), mStreamToEnd(false), mRemLength(0), mFile(NULL), mVdrIdx(1), mFileStructure(), - mIsRecording(false), mRecProgress(0.0) { + mAcceptRanges(true), rangeHdr(), mResponse(NULL) { mLog = Log::getInstance(); mPath = ""; mConnTime = time(NULL); setNonBlocking(); - mBlkData = new char[MAXLEN]; + // mBlkData = new char[MAXLEN]; //#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX @@ -146,19 +100,14 @@ cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): c cHttpResource::~cHttpResource() { - //#ifndef DEBUG +#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX - << " Destructor of cHttpResource called" + << " cHttpResource destroyed" << endl; - //#endif - delete[] mBlkData; - if (mFile != NULL) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: mFile still open. Closing now..." << endl; - fclose(mFile); - mFile = NULL; - } - +#endif + // delete[] mBlkData; + if (mResponse != NULL) + delete mResponse; } int cHttpResource::checkStatus() { @@ -320,6 +269,9 @@ int cHttpResource::handleRead() { return OKAY; } + + + int cHttpResource::processRequest() { // do stuff based on the request and the query #ifndef DEBUG @@ -333,89 +285,71 @@ int cHttpResource::processRequest() { } if (!((strcasecmp(mMethod.c_str(), "GET") == 0) or (strcasecmp(mMethod.c_str(), "HEAD") == 0))) { - sendError(501, "Not supported", NULL, "Method is not supported."); + mResponse = new cResponseError(this, 501, "Not supported", NULL, "Method is not supported."); + // ((cResponseError*)mResponse)->sendError(501, "Not supported", NULL, "Method is not supported."); return OKAY; } #ifndef STANDALONE if (mPath.compare("/recordings.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendRecordingsXml( &statbuf); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendRecordingsXml( &statbuf); return OKAY; } if (mPath.compare("/channels.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendChannelsXml( &statbuf); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendChannelsXml( &statbuf); return OKAY; } if (mPath.compare("/epg.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendEpgXml( &statbuf); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendEpgXml( &statbuf); return OKAY; } if (mPath.compare("/setResume.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - receiveResume(); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveResume(); return OKAY; } if (mPath.compare("/getResume.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendResumeXml(); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendResumeXml(); return OKAY; } if (mPath.compare("/vdrstatus.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendVdrStatusXml( &statbuf); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendVdrStatusXml( &statbuf); return OKAY; } #endif if (mPath.compare("/yt-bookmarklet.js") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendYtBookmarkletJs(); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendYtBookmarkletJs(); return OKAY; } if (mPath.compare("/bmlet-inst.html") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendBmlInstHtml(); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendBmlInstHtml(); return OKAY; } - if (mPath.compare("/media.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - sendMediaXml( &statbuf); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendMediaXml( &statbuf); return OKAY; } if (mPath.compare("/clients") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - handleClients(); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveClientInfo(); return OKAY; } @@ -423,64 +357,61 @@ int cHttpResource::processRequest() { mPath = mFactory->getConfigDir() + "/widget.conf"; if (stat(mPath.c_str(), &statbuf) < 0) { - sendError(404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } - if (handleHeadRequest() != 0) - return OKAY; - mFileSize = statbuf.st_size; - mContentType = SINGLEFILE; - return sendFile(&statbuf); + mResponse = new cResponseFile(this); + return ((cResponseFile*)mResponse)->sendFile(); + // mContentType = SINGLEFILE; + // mFileSize = statbuf.st_size; + // return sendFile(&statbuf); } - if (mPath.compare("/favicon.ico") == 0) { mPath = mFactory->getConfigDir() + "/web/favicon.ico"; if (stat(mPath.c_str(), &statbuf) < 0) { - sendError(404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } - if (handleHeadRequest() != 0) - return OKAY; - - mFileSize = statbuf.st_size; - mContentType = SINGLEFILE; - return sendFile(&statbuf); + mResponse = new cResponseFile(this); + return ((cResponseFile*)mResponse)->sendFile(); + // mFileSize = statbuf.st_size; + // mContentType = SINGLEFILE; + // return sendFile(&statbuf); } if (mPath.compare("/urls.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - sendUrlsXml(); + + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendUrlsXml(); return OKAY; } if (mPath.size() > 8) { if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - sendManifest( &statbuf, false); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendManifest( &statbuf, false); return OKAY; } } if (mPath.size() > 9) { if (mPath.compare(mPath.size() -9, 9, "-seg.m3u8") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - sendManifest( &statbuf, true); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendManifest( &statbuf, true); return OKAY; } } if (mPath.size() > 7) { if (mPath.compare(mPath.size() -7, 7, "-seg.ts") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - sendMediaSegment( &statbuf); + mResponse = new cResponseVdrDir(this); + ((cResponseVdrDir*)mResponse)->sendMediaSegment( &statbuf); return OKAY; } } @@ -492,12 +423,12 @@ int cHttpResource::processRequest() { ok_to_serve = true; } - if (stat(mPath.c_str(), &statbuf) < 0) { // checking, whether the file or directory exists *(mLog->log())<< DEBUGPREFIX << " File Not found " << mPath << endl; - sendError(404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } @@ -512,25 +443,35 @@ int cHttpResource::processRequest() { if (mPath.size() >4) { if (mPath.compare(mPath.size() - 4, 4, ".rec") == 0) { // Handle any recording directory specifically - if (handleHeadRequest() != 0) - return OKAY; - mContentType = VDRDIR; - return sendVdrDir( &statbuf); + mResponse = new cResponseVdrDir(this); + return ((cResponseVdrDir*)mResponse)->sendVdrDir( &statbuf); + // mContentType = VDRDIR; + // return sendVdrDir( &statbuf); } } if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { // No directory access outside of MediaFolder (and also VDRCONG/web) - sendError(404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } - if (handleHeadRequest() != 0) - return OKAY; - mContentType = MEMBLOCK; - sendDir( &statbuf); - return OKAY; + // mContentType = MEMBLOCK; + string tmp = mPath + "index.html"; + // snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mRequest->mPath.c_str()); + // if (stat(pathbuf, statbuf) >= 0) { + if (stat(tmp.c_str(), &statbuf) >= 0) { + mPath = tmp; + mResponse = new cResponseFile(this); + return ((cResponseFile*)mResponse)->sendFile(); + } + else { + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendDir( &statbuf); + return OKAY; + } } else { // mPath is not a folder, thus it is a file @@ -541,24 +482,28 @@ int cHttpResource::processRequest() { // Check, if requested file is in Media Directory if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { - sendError(404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } - if (handleHeadRequest() != 0) - return OKAY; - mFileSize = statbuf.st_size; - mContentType = SINGLEFILE; - return sendFile(&statbuf); + + mResponse = new cResponseFile(this); + return ((cResponseFile*)mResponse)->sendFile(); + // mFileSize = statbuf.st_size; + //mContentType = SINGLEFILE; + //return sendFile(&statbuf); } #ifndef DEBUG *(mLog->log())<< DEBUGPREFIX - << " processRequest: Not Handled SHOULD not be here\n"; + << " processRequest: Not Handled SHOULD not be here\n"; #endif return ERROR; } + + int cHttpResource::handleWrite() { if (mConnState == TOCLOSE) { @@ -571,268 +516,74 @@ int cHttpResource::handleWrite() { if (mConnState != SERVING) { return OKAY; } - - if (mBlkLen == mBlkPos) { + + // if (mResponse->mBlkLen == mResponse->mBlkPos) { + if (mResponse->isBlkWritten()) { // note the mBlk may be filled with header info first. - if (fillDataBlk() != OKAY) { + if (mResponse->fillDataBlk() != OKAY) { return ERROR; } } - int this_write = write(mFd, &mBlkData[mBlkPos], mBlkLen - mBlkPos); - if (this_write <=0) { + // int this_write = write(mFd, &(mResponse->mBlkData[mBlkPos]), mResponse->mBlkLen - mResponse->mBlkPos); + // int this_write = mResponse->writeData(mFd); + // if (this_write <=0) { + if (mResponse->writeData(mFd) <=0) { #ifndef DEBUG *(mLog->log())<< DEBUGPREFIX << " ERROR after write: Stopped (Client terminated Connection)" - << " mBlkPos= " << mBlkPos << " mBlkLen= " << mBlkLen + << " mBlkPos= " << mResponse->mBlkPos << " mBlkLen= " << mResponse->mBlkLen << DEBUGHDR << endl; #endif mConnState = TOCLOSE; mConnected = false; return ERROR; } - mBlkPos += this_write; - - return OKAY; -} - - -int cHttpResource::fillDataBlk() { - char pathbuf[4096]; - - mBlkPos = 0; - int to_read = 0; - - switch(mContentType) { - case NYD: - - break; - case VDRDIR: - // Range requests are assumed to be all open - if (mFile == NULL) { - *(mLog->log()) << DEBUGPREFIX << " no open file anymore " - << "--> Done " << endl; - return ERROR; - } - if ((mRemLength == 0) && !mStreamToEnd){ -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " mRemLength is zero " - << "--> Done " << endl; -#endif - fclose(mFile); - mFile = NULL; - return ERROR; - } - if (!mStreamToEnd) - to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); - else - to_read = MAXLEN; - - mBlkLen = fread(mBlkData, 1, to_read, mFile); - if (!mStreamToEnd) - mRemLength -= mBlkLen; - - if ((mRemLength == 0) && (!mStreamToEnd)) { -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " last Block read " - << "--> Almost Done " << endl; -#endif - return OKAY; - } - - // if (mBlkLen != MAXLEN) { // thlo verify - if (mBlkLen != to_read) { - fclose(mFile); - mFile = NULL; - mVdrIdx ++; - - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); - mPath = pathbuf; - - if (openFile(pathbuf) != OKAY) { - *(mLog->log())<< DEBUGPREFIX << " Failed to open file= " << pathbuf << " mRemLength= " << mRemLength<< endl; - mFile = NULL; - if (mBlkLen == 0) { - *(mLog->log()) << DEBUGPREFIX << " mBlkLen is zero --> Done " << endl; - return ERROR; - } - else - *(mLog->log()) << DEBUGPREFIX << " Still data to send mBlkLen= " << mBlkLen <<" --> continue " << endl; - return OKAY; - } // Error: Open next file failed - - if (mBlkLen == 0) { - if (!mStreamToEnd) - to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); - else - to_read = MAXLEN; - mBlkLen = fread(mBlkData, 1, to_read, mFile); - if (!mStreamToEnd) - mRemLength -= mBlkLen ; - } - } - break; - case SINGLEFILE: - - to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); - mBlkLen = fread(mBlkData, 1, to_read, mFile); - mRemLength -= mBlkLen; - if (mBlkLen == 0) { - - // read until EOF - fclose(mFile); - mFile = NULL; - return ERROR; - } - break; - case MEMBLOCK: - int rem_len = mResponseMessage->size() - mResponseMessagePos; - if (rem_len == 0) { - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " fillDataBlock: MEMBLOCK done" << endl; -#endif - delete mResponseMessage; - mResponseMessagePos = 0; - mConnState = TOCLOSE; - return ERROR; - } - if (rem_len > MAXLEN) - rem_len = MAXLEN; - - string sub_msg = mResponseMessage->substr(mResponseMessagePos, rem_len); - mResponseMessagePos += rem_len; - mBlkLen = sub_msg.size(); - memcpy(mBlkData, sub_msg.c_str(), rem_len); - break; - } + // mResponse->mBlkPos += this_write; return OKAY; } -int cHttpResource::parseResume(cResumeEntry &entry, string &id) { - bool done = false; - size_t cur_pos = 0; - - // The asset_id should the the filename, which is provided by the link element in the xml - // the link is url-safe encoded. - - // bool have_devid = false; - bool have_filename = false; - bool have_resume = false; - - while (!done) { - size_t pos = mPayload.find('\n', cur_pos); - if (pos == string::npos) { - done = true; - continue; - } - size_t pos_col = mPayload.find(':', cur_pos); - string attr= mPayload.substr(cur_pos, (pos_col- cur_pos)); - string val = mPayload.substr(pos_col +1, (pos - pos_col-1)); - - /* if (attr== "devid") { - have_devid = true; - id = val; - } - else */ - if (attr == "filename") { - have_filename = true; - entry.mFilename = cUrlEncode::doXmlSaveDecode(val); - *(mLog->log())<< DEBUGPREFIX - << " filename= " << entry.mFilename - << endl; - } - else if (attr == "resume") { - have_resume = true; - entry.mResume = atof(val.c_str()); - *(mLog->log())<< DEBUGPREFIX - << " mResume= " << entry.mResume - << endl; - } - else { - *(mLog->log())<< DEBUGPREFIX - << " parseResume: ERROR: Unknown attr= " << attr - << " with val= " << val - << endl; - } - cur_pos = pos +1; - if (cur_pos >= mPayload.size()) - done= true; - } - if (have_resume && have_filename ) - return OKAY; - else - return ERROR; -} - -int cHttpResource::handleHeadRequest() { - if (mMethod.compare("HEAD") != 0) { - return 0; - } - *(mLog->log())<< DEBUGPREFIX - << " Handle HEAD Request for Url " << mPath << endl; - mConnState = SERVING; - mContentType = MEMBLOCK; - - // sent an empty response message with just the OK header - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - - sendHeaders(200, "OK", NULL, NULL, -1, -1); - return 1; -} int cHttpResource::handlePost() { mConnState = SERVING; - mContentType = MEMBLOCK; - - // sent an empty response message with just the OK header - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; if (mPath.compare("/log") == 0) { *(mLog->log())<< mPayload << endl; - sendHeaders(200, "OK", NULL, NULL, -1, -1); + mResponse = new cResponseOk(this, 200, "OK", NULL, NULL, -1, -1); + // ((cResponseError*)mResponse)->sendHeaders(200, "OK", NULL, NULL, -1, -1); return OKAY; } if (mPath.compare("/getResume.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - // sendResumeXml( &statbuf); - sendResumeXml( ); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendResumeXml( ); return OKAY; } if (mPath.compare("/setResume.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - receiveResume(); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveResume(); return OKAY; } if (mPath.compare("/setYtUrl") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - receiveYtUrl(); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveYtUrl(); return OKAY; } if (mPath.compare("/deleteRecording.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - deleteRecording(); + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveDelRecReq(); return OKAY; } @@ -841,217 +592,108 @@ int cHttpResource::handlePost() { } -void cHttpResource::sendError(int status, const char *title, const char *extra, const char *text) { - char f[400]; - - mConnState = SERVING; - mContentType = MEMBLOCK; - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - - string hdr = ""; - // sendHeaders(status, title, extra, "text/html", -1, -1); - sendHeaders(status, title, extra, "text/plain", -1, -1); - - /* snprintf(f, sizeof(f), "%d %s\r\n", status, title); - hdr += f; - snprintf(f, sizeof(f), "

    %d %s

    \r\n", status, title); - hdr += f; -*/ - snprintf(f, sizeof(f), "%s\r\n", text); - hdr += f; - /* snprintf(f, sizeof(f), "\r\n"); - hdr += f; -*/ - - strcpy(&(mBlkData[mBlkLen]), hdr.c_str()); - mBlkLen += hdr.size(); - -} - - -int cHttpResource::sendDir(struct stat *statbuf) { - char pathbuf[4096]; - char f[400]; - int len; - - mConnState = SERVING; +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)); + mVersion = line.substr(line.find_last_of(" ") +1); #ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " sendDir: mPath= " << mPath << endl; + *(mLog->log())<< DEBUGPREFIX + << " ReqLine= " << line << endl; #endif - len = mPath.length(); - // int ret = OKAY; - - if (len == 0 || mPath[len - 1] != '/') { - snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mPath.c_str()); - sendError(302, "Found", pathbuf, "Directories must end with a slash."); - return OKAY; + if (mVersion.compare(0, 4, "HTTP") != 0) { +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " ERROR: No HTTP request -> Closing Connection" << line << endl; +#endif + return ERROR; } - snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mPath.c_str()); - if (stat(pathbuf, statbuf) >= 0) { - mPath = pathbuf; - mFileSize = statbuf->st_size; - mContentType = SINGLEFILE; - return sendFile(statbuf); - } + size_t pos = mRequest.find('?'); + if (pos != string::npos) + mQuery = mRequest.substr (pos+1, string::npos); + mPath = cUrlEncode::doUrlSaveDecode(mRequest.substr(0, mRequest.find('?'))); + *(mLog->log())<< DEBUGPREFIX + << " mMethod= " << mMethod + << " mPath= " << mPath + << " mVer= " << mVersion + << " mQuery= " << mQuery + // << " HexDump= " << endl << cUrlEncode::hexDump(mPath) << endl + << endl; + return OKAY; +} +int cHttpResource::parseHttpHeaderLine (string line) { + string hdr_name = line.substr(0, line.find_first_of(":")); + string hdr_val = line.substr(line.find_first_of(":") +2); + + if (hdr_name.compare("Range") == 0) { + parseRangeHeaderValue(hdr_val); + *(mLog->log()) << DEBUGPREFIX + << " Range: Begin= " << rangeHdr.begin + << " End= " << rangeHdr.end + << endl; + } + if (hdr_name.compare("User-Agent") == 0) { + mUserAgent = hdr_val; + *(mLog->log())<< " User-Agent: " << hdr_val + << endl; + } + if (hdr_name.compare("Content-Length") == 0) { + mReqContentLength = atoll(hdr_val.c_str()); #ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " sendDir: create index.html " << endl; + *(mLog->log())<< " Content-Length: " << mReqContentLength + << endl; #endif - DIR *dir; - struct dirent *de; - - mResponseMessage = new string(); - mResponseMessagePos = 0; - *mResponseMessage = ""; - - string hdr = ""; - snprintf(f, sizeof(f), "Index of %s\r\n", mPath.c_str()); - hdr += f; - snprintf(f, sizeof(f), "

    Index of %s

    \r\n
    \n", mPath.c_str());
    -  hdr += f;
    -  snprintf(f, sizeof(f), "Name                             Last Modified              Size\r\n");
    -  hdr += f;
    -  snprintf(f, sizeof(f), "
    \r\n"); - hdr += f; - - *mResponseMessage += hdr; - hdr = ""; - - if (len > 1) { - snprintf(f, sizeof(f), "..\r\n"); - hdr += f; } - *mResponseMessage += hdr; - - dir = opendir(mPath.c_str()); - while ((de = readdir(dir)) != NULL) { - char timebuf[32]; - struct tm *tm; - strcpy(pathbuf, mPath.c_str()); - // printf (" -Entry: %s\n", de->d_name); - strcat(pathbuf, de->d_name); - - stat(pathbuf, statbuf); - tm = gmtime(&(statbuf->st_mtime)); - strftime(timebuf, sizeof(timebuf), "%d-%b-%Y %H:%M:%S", tm); - - hdr = ""; - snprintf(f, sizeof(f), "", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : ""); - hdr += f; - - snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : " "); - hdr += f; - - if (strlen(de->d_name) < 32) { - snprintf(f, sizeof(f), "%*s", 32 - strlen(de->d_name), ""); - hdr += f; - } - if (S_ISDIR(statbuf->st_mode)) { - snprintf(f, sizeof(f), "%s\r\n", timebuf); - hdr += f; - } - else { - snprintf(f, sizeof(f), "%s\r\n", timebuf); - hdr += f; - } + return 0; +} - *mResponseMessage += hdr; - } - closedir(dir); - snprintf(f, sizeof(f), "
    \r\n
    \r\n
    %s
    \r\n\r\n", SERVER); - *mResponseMessage += f; +int cHttpResource::parseRangeHeaderValue(string val) { + rangeHdr.isRangeRequest = true; + size_t pos_equal = val.find_first_of('='); + size_t pos_minus = val.find_first_of('-'); - sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), statbuf->st_mtime); + string range_type = val.substr(0, pos_equal); + string first_val= val.substr(pos_equal+1, pos_minus -(pos_equal+1)); + rangeHdr.begin = atoll(first_val.c_str()); - mRemLength = 0; - return OKAY; + string sec_val = ""; + if ((pos_minus +1)< val.size()){ + sec_val = val.substr(pos_minus+1); + rangeHdr.end = atoll(sec_val.c_str()); + } + return 0; } -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]; - hdr += "\n"; - // snprintf(f, sizeof(f), "%s - %s", ); - hdr += "" + name +"\n"; - hdr += "" +link + "\n"; - hdr += "\n"; - - hdr += "" + guid + "\n"; - - snprintf(f, sizeof(f), "%d", no); - hdr += ""; - hdr += f; - hdr += "\n"; - - hdr += "" + programme +"\n"; - hdr += "" + desc + "\n"; - - snprintf(f, sizeof(f), "%ld", start); - hdr += ""; - hdr += f; - hdr += "\n"; - - snprintf(f, sizeof(f), "%d", dur); - hdr += ""; - hdr += f; - hdr += "\n"; - - if (fps != -1) - snprintf(f, sizeof(f), "%.2f\n", fps); - else - snprintf(f, sizeof(f), "unknown\n"); - hdr += f; - - switch (is_pes){ - case -1: - // unknown - hdr += "unknown\n"; - break; - case 0: - // true - hdr += "true\n"; - break; - case 1: - // false - hdr += "false\n"; +string cHttpResource::getConnStateName() { + string state_string; + switch (mConnState) { + case WAITING: + state_string = "WAITING"; break; - default: + case READHDR: + state_string = "READ Req HDR"; break; - } - - switch (is_new){ - case -1: - // unknown - hdr += "unknown\n"; + case READPAYLOAD: + state_string = "READ Req Payload"; break; - case 0: - // true - hdr += "true\n"; + case SERVING: + state_string = "SERVING"; break; - case 1: - // false - hdr += "false\n"; + case TOCLOSE: + state_string = "TOCLOSE"; break; default: + state_string = "UNKNOWN"; break; } - - hdr += "\n"; - - *mResponseMessage += hdr; - - + return state_string; +} - // return writeToClient(hdr.c_str(), hdr.size()); - return OKAY; -} int cHttpResource::parseQueryLine (vector *avps) { bool done = false; @@ -1072,6 +714,8 @@ int cHttpResource::parseQueryLine (vector *avps) { return OKAY; } + + int cHttpResource::getQueryAttributeValue(vector *avps, string attr, string &val) { int found = ERROR; for (uint i = 0; i < avps->size(); i++) { @@ -1090,2127 +734,18 @@ int cHttpResource::getQueryAttributeValue(vector *avps, string attr, return found; } -int cHttpResource::parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf) { - char pathbuf[4096]; - string link; - DIR *dir; - struct dirent *de; - string dir_comp; - dir_comp = dir_base + dir_name + "/"; - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << " parseFiles: Prefix= " << prefix - << " base= " << dir_base - << " dir= " << dir_name - << " comp= " << dir_comp - << endl; -#endif - - dir = opendir(dir_comp.c_str()); - if (stat(dir_comp.c_str(), statbuf) < 0) - return ERROR; - - while ((de = readdir(dir)) != NULL) { - if ((strcmp(de->d_name, ".") == 0) or (strcmp(de->d_name, "..") == 0)) { - continue; - } - - strcpy(pathbuf, dir_comp.c_str()); - strcat(pathbuf, de->d_name); - - stat(pathbuf, statbuf); - - if (S_ISDIR(statbuf->st_mode)) { - if (strcmp(&(pathbuf[strlen(pathbuf)-4]), ".rec") == 0) { - // vdr folder - time_t now = time(NULL); - struct tm tm_r; - struct tm t = *localtime_r(&now, &tm_r); - t.tm_isdst = -1; - // char [20] rest; - int start = -1; - sscanf(de->d_name, "%4d-%02d-%02d.%02d.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); - - // sscanf(de->d_name, "%4d-%02d-%02d.%02d%.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); - t.tm_year -= 1900; - t.tm_mon--; - t.tm_sec = 0; - start = mktime(&t); - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << " Vdr Folder Found: " << pathbuf << " start= " << start << endl; -#endif - - entries->push_back(sFileEntry(dir_name, pathbuf, start)); - } - else { - // regular file - parseFiles(entries, prefix + de->d_name + "~", dir_comp, de->d_name, statbuf); - } - } - else { - if ((de->d_name)[0] != '.' ) - entries->push_back(sFileEntry(prefix+de->d_name, pathbuf, 1)); - } - } - closedir(dir); - return OKAY; -} - -int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) { -#ifndef STANDALONE - - size_t pos = mPath.find_last_of ("/"); - mDir = mPath.substr(0, pos); - string mpd_name = mPath.substr(pos+1); - - float seg_dur = mFactory->getConfig()->getSegmentDuration() *1.0; - - cRecordings* recordings = &Recordings; - cRecording* rec = recordings->GetByName(mDir.c_str()); - double duration = rec->NumFrames() / rec->FramesPerSecond(); - int bitrate = (int)((getVdrFileSize() *8.0 * mFactory->getConfig()->getHasBitrateCorrection()/ duration) +0.5); - - time_t now = time(NULL); - - if (rec->Info() != NULL){ - if (rec->Info()->GetEvent() != NULL) { - if (rec->Info()->GetEvent()->EndTime() > now) { - - float corr = (now - rec->Info()->GetEvent()->StartTime()) - duration; - duration = rec->Info()->GetEvent()->Duration() -int(corr); - *(mLog->log()) << DEBUGPREFIX - << " is Recording: Duration= " << duration << " sec" - << " correction: " << int(corr) - << endl; - } - } - else - *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info()->GetEvent() is NULL " << endl; - } - else - *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info() is NULL " << endl; - - // duration is now either the actual duration of the asset or the target duration of the asset - int end_seg = int (duration / seg_dur) +1; - - *(mLog->log()) << DEBUGPREFIX - << " Manifest for mDir= " << mDir - << " duration= " << duration - << " seg_dur= " << seg_dur - << " end_seg= " << end_seg - << endl; - - - - if (is_hls) { - writeM3U8(duration, bitrate, seg_dur, end_seg); - } - else { - writeMPD(duration, bitrate, seg_dur, end_seg); - } - -#endif - return OKAY; -} - -int cHttpResource::receiveYtUrl() { - vector 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; - *mResponseMessage = ""; - mContentType = MEMBLOCK; - - mConnState = SERVING; - char buf[30]; - - string hdr = ""; - - - *mResponseMessage += "#EXTM3U\n"; - // snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", (seg_dur-1)); - snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", int(seg_dur)); - hdr = buf; - *mResponseMessage += hdr; - - *mResponseMessage += "#EXT-X-MEDIA-SEQUENCE:1\n"; - *mResponseMessage += "#EXT-X-KEY:METHOD=NONE\n"; - - for (int i = 1; i < end_seg; i++){ - // snprintf(buf, sizeof(buf), "#EXTINF:%.1f,\n", (seg_dur-0.5)); - snprintf(buf, sizeof(buf), "#EXTINF:%.2f,\n", seg_dur); - hdr = buf; - *mResponseMessage += hdr; - - snprintf(buf, sizeof(buf), "%d-seg.ts\n", i); - hdr = buf; - *mResponseMessage += hdr; - } - *mResponseMessage += "#EXT-X-ENDLIST\n"; - - sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); -} - - -void cHttpResource::writeMPD(double duration, int bitrate, float seg_dur, int end_seg) { - mResponseMessage = new string(); - mResponseMessagePos = 0; - *mResponseMessage = ""; - mContentType = MEMBLOCK; - - mConnState = SERVING; - char buf[30]; - char line[400]; - - string hdr = ""; - - *mResponseMessage += "\n"; - - snprintf(line, sizeof(line), "getConfig()->getHasMinBufferTime(), duration); - *mResponseMessage = *mResponseMessage + line; - - *mResponseMessage += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\" "; - *mResponseMessage += "xsi:schemaLocation=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\">\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - // SD: 720x 576 - // HD: 1280x 720 - // snprintf(line, sizeof(line), "\n", mFactory->getConfig()->getHasBitrate()); - snprintf(line, sizeof(line), "\n", - bitrate, ((bitrate < 10000000)? "width=\"720\" height=\"576\"" : "width=\"1280\" height=\"720\"")); - *mResponseMessage = *mResponseMessage + line; - - hdr = "\n"; - *mResponseMessage += hdr; - - snprintf(buf, sizeof(buf), "\"%d\"", end_seg); - *mResponseMessage += "\n"; - - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += ""; - - sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); -} - -int cHttpResource::sendMediaSegment (struct stat *statbuf) { -#ifndef STANDALONE - - *(mLog->log()) << DEBUGPREFIX << " sendMediaSegment " << mPath << endl; - size_t pos = mPath.find_last_of ("/"); - - mDir = mPath.substr(0, pos); - string seg_name = mPath.substr(pos+1); - int seg_number; - - int seg_dur = mFactory->getConfig()->getSegmentDuration(); - int frames_per_seg = 0; - - sscanf(seg_name.c_str(), "%d-seg.ts", &seg_number); - - //FIXME: Do some consistency checks on the seg_number - //* Does the segment exist - - cRecordings* recordings = &Recordings; - cRecording* rec = recordings->GetByName(mDir.c_str()); - if (rec != NULL) { - frames_per_seg = seg_dur * rec->FramesPerSecond(); - } - else { - *(mLog->log()) << DEBUGPREFIX << " ERROR: Ooops, rec is NULL, assuming 25 fps " << endl; - frames_per_seg = seg_dur * 25; - } - //FIXME: HD Fix - // frames_per_seg = seg_dur * 25; - - *(mLog->log()) << DEBUGPREFIX - << " mDir= " << mDir - << " seg_name= " << seg_name - << " seg_number= "<< seg_number - << " fps= " << rec->FramesPerSecond() - << " frames_per_seg= " << frames_per_seg - << endl; - int start_frame_count = (seg_number -1) * frames_per_seg; - - FILE* idx_file = fopen((mDir +"/index").c_str(), "r"); - if (idx_file == NULL){ - *(mLog->log()) << DEBUGPREFIX - << " failed to open idx file = "<< (mDir +"/index").c_str() - << endl; - sendError(404, "Not Found", NULL, "Failed to open Index file"); - return OKAY; - } - - char *index_buf = new char[(frames_per_seg +3) *8]; - - // fseek to start_frame_count * sizeof(in_read) - fseek(idx_file, start_frame_count * 8, SEEK_SET); - - // read to (seg_number * frames_per_seg +1) * sizeof(in_read) - // buffersize is frames_per_seg * seg_number * sizeof(in_read) - int buffered_indexes = fread(index_buf, 8, (frames_per_seg +2), idx_file); - - fclose(idx_file); - - if(buffered_indexes <= 0 ) { - *(mLog->log())<log()) << DEBUGPREFIX - << " GenSegment: start (no/idx)= " << start_idx << " / " << start_offset - << " to (no/idx)= " << end_idx << " / " << end_offset - << endl; - /*#endif*/ - - delete[] index_buf; - - int rem_len = 0; - bool error = false; - if (start_idx == end_idx){ - mRemLength = (end_offset - start_offset); - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << " start_idx == end_idx: mRemLength= " <log()) << DEBUGPREFIX - << " start_idx < end_idx " - << endl; -#endif - snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); - if (stat(seg_fn, statbuf) < 0) { - *(mLog->log()) << DEBUGPREFIX - << " file= " <st_size - start_offset; // remaining length of the first segment - - // loop over all idx files between start_idx and end_idx - for (int idx = (start_idx+1); idx < end_idx; idx ++) { - snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), idx); - if (stat(seg_fn, statbuf) < 0) { - *(mLog->log()) << DEBUGPREFIX - << " for loop file= " <st_size; // remaining length of the first segment - } - rem_len += end_offset; // - mRemLength = rem_len; - snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << " start_idx= " << start_idx << " != end_idx= "<< end_idx <<": mRemLength= " <log())<< DEBUGPREFIX << " Failed to open file= " << seg_fn - << " mRemLength= " << mRemLength<< endl; - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - fseek(mFile, start_offset, SEEK_SET); - - sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); - -#endif - 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 = "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - - for (uint i = 0; i < urls->size(); i++) { - *mResponseMessage += "\n"; - *mResponseMessage += (urls->getEntry(i))->mEntry; - *mResponseMessage += "\n\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 += "\n"; - *mResponseMessage += value; - *mResponseMessage += "\n\n"; - continue; - } - - *mResponseMessage += "\n"; - *mResponseMessage += "Unknown: " + line + " type=" + type; - - *mResponseMessage += "\n\n"; - } - myfile.close(); -*/ - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); - return OKAY; -} - -int cHttpResource::sendMediaXml (struct stat *statbuf) { - char pathbuf[4096]; - string link; - string media_folder = mFactory->getConfig()->getMediaFolder(); - - mResponseMessage = new string(); - mResponseMessagePos = 0; - *mResponseMessage = ""; - mContentType = MEMBLOCK; - - mConnState = SERVING; - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl; -#endif - - string own_ip = getOwnIp(mFd); - *(mLog->log()) << " OwnIP= " << own_ip << endl; - - vector entries; - - if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) { - sendError(404, "Not Found", NULL, "Media Folder likely not configured."); - return OKAY; - } - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - - *mResponseMessage += hdr; - - hdr = ""; - - 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", own_ip.c_str(), mServerPort, - cUrlEncode::doUrlSaveEncode(entries[i].sPath).c_str()); - if (writeXmlItem(cUrlEncode::doXmlSaveEncode(entries[i].sName), pathbuf, "NA", "NA", "-", - -1, entries[i].sStart, -1, -1, -1, -1) == ERROR) - return ERROR; - - } - - hdr = "\n"; - hdr += "\n"; - *mResponseMessage += hdr; - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); - - return OKAY; -} - - -void cHttpResource::handleClients() { - vector 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 - - char f[400]; - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - mContentType = MEMBLOCK; - - mConnState = SERVING; - - int free; - int used; - int percent; - - percent = VideoDiskSpace(&free, &used); - - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - - *mResponseMessage += "\n"; - snprintf(f, sizeof(f), "%d", free); - *mResponseMessage += f; - - snprintf(f, sizeof(f), "%d", used); - *mResponseMessage += f; - snprintf(f, sizeof(f), "%d", percent); - *mResponseMessage += f; - *mResponseMessage += "\n"; - - *mResponseMessage += "\n"; - - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); - -#endif - return OKAY; -} - -int cHttpResource::sendYtBookmarkletJs() { - *(mLog->log()) << DEBUGPREFIX - << " sendYtBookmarkletJs" << endl; - - vector 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 = "" - "SmartTVWeb Bookmarklets" - "" - "
    " - "

    Bookmarklet for collecting YouTube Pages

    " - "
    " - "
    " - "

    Installation

    " - "Drag the link below to your Bookmarks toolbar" - "

    or

    " - "

    Right click and select “Bookmark This Link”

    " - "

    YT SaveNPlay: Save the video and also Play it.

    " - "

    YT Play: Play the video without saving.

    " - "
    " - "
    " - "

    Usage

    " - "

    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.

    " - "
    " - "
    " - "

    Have fun...

    " - ""; - - sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), -1); - return OKAY; -} - -int cHttpResource::sendEpgXml (struct stat *statbuf) { -#ifndef STANDALONE - - char f[400]; - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - mContentType = MEMBLOCK; - - mConnState = SERVING; - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " generating /epg.xml" - << DEBUGHDR << endl; -#endif - vector avps; - parseQueryLine(&avps); - string id = "S19.2E-1-1107-17500"; - string mode = ""; - bool add_desc = true; - - if (getQueryAttributeValue(&avps, "id", id) == ERROR){ - *(mLog->log())<< DEBUGPREFIX - << " ERROR: id not found" - << DEBUGHDR << endl; - delete mResponseMessage; - sendError(400, "Bad Request", NULL, "no id in query line"); - return OKAY; - } - - if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ - if (mode == "nodesc") { - add_desc = false; - *(mLog->log())<< DEBUGPREFIX - << " **** Mode: No Description ****" - << endl; - } - } - - /* for (int i = 0; i < avps.size(); i++) { - if (avps[i].attribute == "id") - id = avps[i].value; - *(mLog->log())<< DEBUGPREFIX - << " a= " - << avps[i].attribute - << " v= " << avps[i].value - << endl; - }*/ - tChannelID chan_id = tChannelID::FromString (id.c_str()); - if ( chan_id == tChannelID::InvalidID) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: Not possible to get the ChannelId from the string" - << DEBUGHDR << endl; - delete mResponseMessage; - sendError(400, "Bad Request", NULL, "Invalid Channel ID."); - return OKAY; - } - - cSchedulesLock * lock = new cSchedulesLock(false, 500); - const cSchedules *schedules = cSchedules::Schedules(*lock); - - const cSchedule *schedule = schedules->GetSchedule(chan_id); - if (schedule == NULL) { - *(mLog->log())<< DEBUGPREFIX - << "ERROR: Schedule is zero for guid= " << id - << endl; - delete mResponseMessage; - sendError(500, "Internal Server Error", NULL, "Schedule is zero."); - return OKAY; - } - - time_t now = time(NULL); - const cEvent *ev = NULL; - for(cEvent* e = schedule->Events()->First(); e; e = schedule->Events()->Next(e)) { - if ( (e->StartTime() <= now) && (e->EndTime() > now)) { - ev = e; - - } else if (e->StartTime() > now + 3600) { - break; - } - } - - // const cEvent * ev = schedule->GetPresentEvent(); - - if (ev == NULL) { - *(mLog->log())<< DEBUGPREFIX - << "ERROR: Event is zero for guid= " << id - << endl; - delete mResponseMessage; - sendError(500, "Internal Server Error", NULL, "Event is zero."); - return OKAY; - } - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - - *mResponseMessage += hdr; - // Payload here - - hdr = ""; - if (ev->Title() != NULL) { - string title = cUrlEncode::doXmlSaveEncode(ev->Title()); - hdr += "" + title +"\n"; - } - else { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: title is zero for guid= " << id << endl; - hdr += "Empty\n"; - - delete mResponseMessage; - sendError(500, "Internal Server Error", NULL, "Title is zero."); - return OKAY; - } - - hdr += "" + id + "\n"; - - *(mLog->log())<< DEBUGPREFIX - << " guid= " << id - << " title= " << ev->Title() - << " start= " << ev->StartTime() - << " end= " << ev->EndTime() - << " now= " << now - << endl; - if (add_desc) { - hdr += ""; - if (ev->Description() != NULL) { - hdr += cUrlEncode::doXmlSaveEncode(ev->Description()); - } - else { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: description is zero for guid= " << id << endl; - - delete mResponseMessage; - sendError(500, "Internal Server Error", NULL, "Description is zero."); - return OKAY; - } - hdr += "\n"; - } - else { - hdr += "No Description Available\n"; - } - snprintf(f, sizeof(f), "%ld\n", ev->StartTime()); - hdr += f ; - - snprintf(f, sizeof(f), "%ld\n", ev->EndTime()); - hdr += f; - - snprintf(f, sizeof(f), "%d\n", ev->Duration()); - hdr += f; - *mResponseMessage += hdr; - - hdr = "\n"; - hdr += "\n"; - - *mResponseMessage += hdr; - - delete lock; - - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); - -#endif - return OKAY; -} - -int cHttpResource::sendChannelsXml (struct stat *statbuf) { -#ifndef STANDALONE - - char f[400]; - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - mContentType = MEMBLOCK; - - mConnState = SERVING; - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " generating /channels.xml" - << DEBUGHDR << endl; -#endif - string own_ip = getOwnIp(mFd); - *(mLog->log()) << " OwnIP= " << own_ip << endl; - - vector avps; - parseQueryLine(&avps); - string mode = ""; - bool add_desc = true; - - string no_channels_str = ""; - int no_channels = -1; - string group_sep = ""; - - if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ - if (mode == "nodesc") { - add_desc = false; - *(mLog->log())<< DEBUGPREFIX - << " Mode: No Description" - << endl; - } - else { - *(mLog->log())<< DEBUGPREFIX - << " Mode: Unknown" - << endl; - } - } - if (getQueryAttributeValue(&avps, "channels", no_channels_str) == OKAY){ - no_channels = atoi(no_channels_str.c_str()) ; - } - - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - hdr+= "VDR Channels List\n"; - - - *mResponseMessage += hdr; - - int count = mFactory->getConfig()->getLiveChannels(); - if (no_channels > 0) - count = no_channels +1; - - cSchedulesLock * lock = new cSchedulesLock(false, 500); - const cSchedules *schedules = cSchedules::Schedules(*lock); - - for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { - if (channel->GroupSep()) { - 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", own_ip.c_str(), *(channel->GetChannelID()).ToString()); - string link = f; - - const cSchedule *schedule = schedules->GetSchedule(channel->GetChannelID()); - string desc = "No description available"; - string title = "Not available"; - time_t start_time = 0; - int duration = 0; - - if (schedule != NULL) { - const cEvent *ev = schedule->GetPresentEvent(); - if (ev != NULL) { - if ((ev->Description() != NULL) && add_desc) - desc = cUrlEncode::doXmlSaveEncode(ev->Description()); - - if ((ev->Title() != NULL) && add_desc) - title = cUrlEncode::doXmlSaveEncode(ev->Title()); - start_time = ev->StartTime(); - duration = ev->Duration(); - } - else { - *(mLog->log())<< DEBUGPREFIX - << " Event Info is Zero for Count= " - << count - << " Name= " << channel->Name() << endl; - } - } - else { - *(mLog->log())<< DEBUGPREFIX - << " Schedule is Zero for Count= " - << count - << " Name= " << channel->Name() << endl; - } - - 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(), channel->Number(), start_time, duration, -1, -1, -1) == ERROR) - return ERROR; - - } - - hdr = "\n"; - hdr += "\n"; - - *mResponseMessage += hdr; - delete lock; - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); - -#endif - return OKAY; -} - -int cHttpResource::receiveResume() { - string dev_id; - cResumeEntry entry; - if (parseResume(entry, dev_id) == ERROR) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR parsing resume" - << endl; - } - - *(mLog->log())<< DEBUGPREFIX - << " Resume: id= " << dev_id - << " resume= " << entry << endl; - - vector avps; - parseQueryLine(&avps); - string guid; - string resume_str; - - if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){ - // entry.mFilename = guid; - entry.mFilename = cUrlEncode::doUrlSaveDecode(guid); - - *(mLog->log())<< DEBUGPREFIX - << " Found a id Parameter: " << guid - << endl; - } - if (getQueryAttributeValue(&avps, "resume", resume_str) == OKAY){ - entry.mResume = atof(resume_str.c_str()); - *(mLog->log())<< DEBUGPREFIX - << " Found a resume Parameter: " << entry.mResume - << endl; - } - - - - -#ifndef STANDALONE - cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); - if (rec == NULL) { - //Error 404 - sendError(404, "Not Found", NULL, "Failed to find recording."); - return OKAY; - } - - cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); - *(mLog->log())<< DEBUGPREFIX - << " Resume: " << entry.mFilename - << " saving Index= " << int(entry.mResume * rec->FramesPerSecond() ) - << " mResume= " < avps; - parseQueryLine(&avps); - string guid; - - if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){ - // entry.mFilename = guid; - entry.mFilename = cUrlEncode::doUrlSaveDecode(guid); - - *(mLog->log())<< DEBUGPREFIX - << " Found a id Parameter: " << guid - << endl; - } - - - cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); - if (rec == NULL) { - //Error 404 - *(mLog->log())<< DEBUGPREFIX - << " sendResume: File Not Found filename= " << entry.mFilename << endl; - sendError(404, "Not Found", NULL, "Failed to find recording."); - return OKAY; - } - if (rec->IsNew()) { - *(mLog->log())<< DEBUGPREFIX - << " sendResume: file is new " << endl; - sendError(400, "Bad Request", NULL, "File is new."); - return OKAY; - } - cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); - - *(mLog->log())<< DEBUGPREFIX - << " resume request for " << entry.mFilename - << " resume= " << resume.Read() - << " (" << resume.Read() *1.0 / rec->FramesPerSecond() << "sec)" - << endl; - - *mResponseMessage += "\n"; - *mResponseMessage += ""; - snprintf(f, sizeof(f), "%.02f", resume.Read() *1.0 / rec->FramesPerSecond()); - *mResponseMessage += f; - *mResponseMessage += "\n"; - - - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); -#endif - - return OKAY; -} - -int cHttpResource::deleteRecording() { -#ifndef STANDALONE - - vector avps; - parseQueryLine(&avps); - string id = ""; - - if (getQueryAttributeValue(&avps, "id", id) == ERROR){ - *(mLog->log())<< DEBUGPREFIX - << " ERROR: id not found in query." - << DEBUGHDR << endl; - sendError(400, "Bad Request", NULL, "no id in query line"); - return OKAY; - } - mPath = cUrlEncode::doUrlSaveDecode(id); - - 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; - } - if ( rec->Delete() ) { - Recordings.DelByName(rec->FileName()); - // 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); - #endif - return OKAY; -} - - -int cHttpResource::sendRecordingsXml(struct stat *statbuf) { -#ifndef STANDALONE - - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - mContentType = MEMBLOCK; - - mConnState = SERVING; - - string own_ip = getOwnIp(mFd); - *(mLog->log()) << " OwnIP= " << own_ip << endl; - - vector avps; - parseQueryLine(&avps); - string model = ""; - string link_ext = ""; - string type = ""; - string has_4_hd_str = ""; - bool has_4_hd = true; - string mode = ""; - bool add_desc = true; - - if (getQueryAttributeValue(&avps, "model", model) == OKAY){ - *(mLog->log())<< DEBUGPREFIX - << " Found a Model Parameter: " << model - << endl; - } - - if (getQueryAttributeValue(&avps, "type", type) == OKAY){ - *(mLog->log())<< DEBUGPREFIX - << " Found a Type Parameter: " << type - << endl; - if (type == "hls") { - if (model == "samsung") - link_ext = "/manifest-seg.m3u8|COMPONENT=HLS"; - else - link_ext = "/manifest-seg.m3u8"; - } - if (type == "has") { - if (model == "samsung") - link_ext = "/manifest-seg.mpd|COMPONENT=HAS"; - else - link_ext = "/manifest-seg.mpd"; - } - } - - if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ - if (mode == "nodesc") { - add_desc = false; - *(mLog->log())<< DEBUGPREFIX - << " Mode: No Description" - << endl; - } - } - - if (getQueryAttributeValue(&avps, "has4hd", has_4_hd_str) == OKAY){ - *(mLog->log())<< DEBUGPREFIX - << " Found a Has4Hd Parameter: " << has_4_hd_str - << endl; - if (has_4_hd_str == "false") - has_4_hd = false; - } - - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " generating /recordings.xml" - << DEBUGHDR << endl; -#endif - sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - hdr+= "VDR Recordings List\n"; - - *mResponseMessage += hdr; - - /* - if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", - "-", 0, 0) == ERROR) - return ERROR; - - if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", - "-", 0, 0) == ERROR) - return ERROR; - if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122:8000/hd2/mpeg/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS from own Server", - "-", 0, 0) == ERROR) - return ERROR; -*/ - //-------------------- - cRecordings* recordings = &Recordings; - char f[600]; - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " recordings->Count()= " << recordings->Count() - << DEBUGHDR << endl; -#endif - - // List of recording timer - time_t now = time(NULL); - - vector act_rec; - /*#ifndef DEBUG*/ - *(mLog->log())<< DEBUGPREFIX - << " checking avtive timer" - << endl; - /*#endif*/ - for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ - ti->Matches(); - - /* *(mLog->log()) << DEBUGPREFIX - << " Active Timer: " << ti->File() - << " EvStart= " << ti->Event()->StartTime() - << " EvDuration= " << ti->Event()->Duration() - << endl;*/ - /* *(mLog->log()) << DEBUGPREFIX - << " TiStart= " << ti->StartTime() - << " TiDuration= " << (ti->StopTime() - ti->StartTime()) - << 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() - << " Duration= " << ti->Event()->Duration() - << endl; -*/ - *(mLog->log()) << DEBUGPREFIX - << " Active Timer: " << ti->File() - << " Start= " << ti->StartTime() - << " Duration= " << (ti->StopTime() - ti->StartTime()) - << endl; - // act_rec.push_back(sTimerEntry(ti->File(), ti->Event()->StartTime(), ti->Event()->Duration())); - act_rec.push_back(sTimerEntry(ti->File(), ti->StartTime(), (ti->StopTime() - ti->StartTime()))); - } - } - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " Found " << act_rec.size() - << " running timers" - << endl; -#endif - - int rec_dur = 0; - for (cRecording *recording = recordings->First(); recording; recording = recordings->Next(recording)) { - 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", 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", own_ip.c_str(), mServerPort, - cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), link_ext.c_str()); - - string link = f; - string desc = "No description available"; - rec_dur = recording->LengthInSeconds(); - - string name = recording->Name(); - - for (uint x = 0; x < act_rec.size(); x++) { - if (act_rec[x].name == name) { - - /* *(mLog->log())<< DEBUGPREFIX - << " !!!!! Found active Recording !!! " - << endl; -*/ - rec_dur += (act_rec[x].startTime + act_rec[x].duration - now); - - - } - } // for - - if (recording->Info() != NULL) { - if ((recording->Info()->Description() != NULL) && add_desc) { - desc = cUrlEncode::doXmlSaveEncode(recording->Info()->Description()); - } - } - - if (writeXmlItem(cUrlEncode::doXmlSaveEncode(recording->Name()), link, "NA", desc, - cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), - -1, - recording->Start(), rec_dur, recording->FramesPerSecond(), - (recording->IsPesRecording() ? 0: 1), (recording->IsNew() ? 0: 1)) == ERROR) - // Better Internal Server Error - return ERROR; - - } - - hdr = "\n"; - hdr += "\n"; - - *mResponseMessage += hdr; - - -#endif - return OKAY; -} - -bool cHttpResource::isTimeRequest(struct stat *statbuf) { - -#ifndef STANDALONE - vector avps; - parseQueryLine(&avps); - string time_str = ""; - string mode = ""; - float time = 0.0; - - if (getQueryAttributeValue(&avps, "time", time_str) != OKAY){ - return false; - } - - if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ - if (mode.compare ("streamtoend") ==0) { - mStreamToEnd = true; - } - } - - if (mIsRecording) - mStreamToEnd = true; - - time = atof(time_str.c_str()); - *(mLog->log())<< DEBUGPREFIX - << " Found a Time Parameter: " << time - << " streamToEnd= " << ((mStreamToEnd) ? "true" : "false") - << endl; - - mDir = mPath; - cRecording *rec = Recordings.GetByName(mPath.c_str()); - if (rec == NULL) { - *(mLog->log())<< DEBUGPREFIX - << " Error: Did not find recording= " << mPath << endl; - sendError(404, "Not Found", NULL, "File not found."); - return true; - } - - double fps = rec->FramesPerSecond(); - double dur = rec->NumFrames() * fps; - bool is_pes = rec->IsPesRecording(); - if (dur < time) { - sendError(400, "Bad Request", NULL, "Time to large."); - return true; - } - - int start_frame = int(time * fps) -25; - - FILE* idx_file= NULL; - - if (is_pes){ - idx_file = fopen((mDir +"/index.vdr").c_str(), "r"); - // sendError(400, "Bad Request", NULL, "PES not yet supported."); - // return true; - } - else { - idx_file = fopen((mDir +"/index").c_str(), "r"); - } - - if (idx_file == NULL){ - *(mLog->log()) << DEBUGPREFIX - << " failed to open idx file = "<< (mDir +"/index").c_str() - << endl; - sendError(404, "Not Found", NULL, "Failed to open Index file"); - return OKAY; - } - - int buffered_frames = 50; - char *index_buf = new char[8 *buffered_frames]; // 50 indexes - - *(mLog->log()) << DEBUGPREFIX - << " seeking to start_frame= " << start_frame - << " fps= " << fps << endl; - fseek(idx_file, start_frame * 8, SEEK_SET); - - int buffered_indexes = fread(index_buf, 8, (buffered_frames), idx_file); - - fclose(idx_file); - - if(buffered_indexes <= 0 ) { - *(mLog->log())<log()) << DEBUGPREFIX - << " Finding I-Frame now" << endl; - - - bool found_it = false; - int i = 0; - - uint32_t offset = 0; - int idx =0; - int type =0; - - for (i= 0; i < buffered_indexes; i++){ - if (is_pes) { - tIndexPes in_read_pes; - memcpy (&in_read_pes, &(index_buf[i*8]), 8); - type = in_read_pes.type == 1; - idx = in_read_pes.number; - offset = in_read_pes.offset; - } - else{ - tIndexTs in_read_ts; - memcpy (&in_read_ts, &(index_buf[i*8]), 8); - type = in_read_ts.independent; - idx = in_read_ts.number; - offset = in_read_ts.offset; - } - - *(mLog->log()) << DEBUGPREFIX - << " Frame= " << i - << " idx= "<< idx - << " offset= " << offset - << " type= " << type - << endl; - if (type){ - found_it = true; - break; - } - } - - if (!found_it) { - delete[] index_buf; - sendError(404, "Not Found", NULL, "Failed to read Index file"); - return OKAY; - } - - mVdrIdx = idx; - - *(mLog->log()) << DEBUGPREFIX - << " idx= "<< mVdrIdx - << " offset= " << offset - << endl; - - delete[] index_buf; - - char pathbuf[4096]; - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), mVdrIdx); - - *(mLog->log()) << DEBUGPREFIX - << " Opening Path= " - << pathbuf << endl; - if (openFile(pathbuf) != OKAY) { - sendError(403, "Forbidden", NULL, "Access denied."); - return true; - } - - fseek(mFile, offset, SEEK_SET); - - if (mStreamToEnd) { - sendHeaders(200, "OK", NULL, "video/mpeg", -1, -1); - return true; - } - - uint64_t file_size = 0; - bool more_to_go = true; - int vdr_idx = mVdrIdx; - while (more_to_go) { - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), vdr_idx); - if (stat(pathbuf, statbuf) >= 0) { - *(mLog->log())<< " found for " << pathbuf << endl; - file_size += statbuf->st_size; - } - else { - more_to_go = false; - } - vdr_idx ++; - } - mRemLength = file_size - offset; - - *(mLog->log()) << DEBUGPREFIX - << " Done. Start Streaming " - << endl; - - if (rangeHdr.isRangeRequest) { - snprintf(pathbuf, sizeof(pathbuf), "Content-Range: bytes 0-%lld/%lld", (mRemLength -1), mRemLength); - sendHeaders(206, "Partial Content", pathbuf, "video/mpeg", mRemLength, statbuf->st_mtime); - } - else { - sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); - } - return true; -#else - return false; -#endif -} - -int cHttpResource::sendVdrDir(struct stat *statbuf) { - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " *** sendVdrDir mPath= " << mPath << endl; -#endif - char pathbuf[4096]; - char f[400]; - int vdr_idx = 0; - uint64_t total_file_size = 0; - // int ret = OKAY; - string vdr_dir = mPath; - vector file_sizes; - bool more_to_go = true; - - checkRecording(); - - mVdrIdx = 1; - mFileStructure = "%s/%03d.vdr"; - - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), 1); - if (stat(pathbuf, statbuf) < 0) { - mFileStructure = "%s/%05d.ts"; -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " using dir format: " << mFileStructure.c_str() << endl; -#endif - } - - // The range request functions are activated, when a time header is detected - if (isTimeRequest(statbuf)) { - *(mLog->log())<< DEBUGPREFIX - << " isTimeRequest is true" - << endl; - return OKAY; - } - - // --- looup all vdr files in the dir --- - while (more_to_go) { - vdr_idx ++; - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), vdr_idx); - if (stat(pathbuf, statbuf) >= 0) { -#ifndef DEBUG - *(mLog->log())<< " found for " << pathbuf << endl; -#endif - file_sizes.push_back(sVdrFileEntry(statbuf->st_size, total_file_size, vdr_idx)); - total_file_size += statbuf->st_size; - } - else { - more_to_go = false; - } - } - if (file_sizes.size() < 1) { - // There seems to be vdr video file in the directory - *(mLog->log())<< DEBUGPREFIX - << " No video file in the directory" - << DEBUGHDR << endl; - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " vdr filesize list " - << DEBUGHDR << endl; - for (uint i = 0; i < file_sizes.size(); i++) - *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; - *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; -#endif - - // total_file_size (on disk) - - // ---------------- file sizes --------------------- - - uint cur_idx = 0; - -#ifndef DEBUG - if (mIsRecording) { - snprintf(f, sizeof(f), " CurFileSize= %lld mRecProgress= %f ExtFileSize= %lld", total_file_size, mRecProgress, (long long int)(mRecProgress * total_file_size)); - *(mLog->log()) << DEBUGPREFIX - << endl << " isRecording: " << f - << endl; - } -#endif - - if (!rangeHdr.isRangeRequest) { - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), file_sizes[cur_idx].sIdx); - - if (openFile(pathbuf) != OKAY) { - sendError(403, "Forbidden", NULL, "Access denied."); - return OKAY; - } - - mRemLength = total_file_size; - - sendHeaders(200, "OK", NULL, "video/mpeg", ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size ), statbuf->st_mtime); - } - else { // Range request - // idenify the first file -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << endl <<" --- Range Request Handling ---" - << DEBUGHDR << endl; -#endif - if (mIsRecording && (rangeHdr.begin > total_file_size)) { - *(mLog->log()) << DEBUGPREFIX - << " ERROR: Not yet available" << endl; - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - cur_idx = file_sizes.size() -1; - for (uint i = 1; i < file_sizes.size(); i++) { - if (rangeHdr.begin < file_sizes[i].sFirstOffset ) { - cur_idx = i -1; - break; - } - } - -#ifndef DEBUG - *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " - << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin - << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; -#endif - - mVdrIdx = file_sizes[cur_idx].sIdx; - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), file_sizes[cur_idx].sIdx); -#ifndef DEBUG - *(mLog->log())<< " file identified= " << pathbuf << endl; -#endif - if (openFile(pathbuf) != OKAY) { - *(mLog->log())<< "----- fopen failed dump ----------" << endl; - *(mLog->log())<< DEBUGPREFIX - << " vdr filesize list " - << DEBUGHDR << endl; - for (uint i = 0; i < file_sizes.size(); i++) - *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; - *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; - - *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " - << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin - << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; - *(mLog->log())<< "---------------" << endl; - sendError(403, "Forbidden", NULL, "Access denied."); - return OKAY; - } - mDir = mPath; - mPath = pathbuf; -#ifndef DEBUG - *(mLog->log())<< " Seeking into file= " << (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset) - << " cur_idx= " << cur_idx - << " file_sizes[cur_idx].sFirstOffset= " << file_sizes[cur_idx].sFirstOffset - << endl; -#endif - fseek(mFile, (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset), SEEK_SET); - if (rangeHdr.end == 0) - rangeHdr.end = ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size); - - mRemLength = (rangeHdr.end-rangeHdr.begin); - - snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), - ((mIsRecording) ? (long long int)(mRecProgress * total_file_size): total_file_size)); - - sendHeaders(206, "Partial Content", f, "video/mpeg", (rangeHdr.end-rangeHdr.begin), statbuf->st_mtime); - } - -#ifndef DEBUG - *(mLog->log())<< " ***** Yes, vdr dir found ***** mPath= " << mPath<< endl; -#endif - - return OKAY; // handleRead() done -} - -void cHttpResource::sendHeaders(int status, const char *title, const char *extra, const char *mime, - long long int length, time_t date) { - - time_t now; - char timebuf[128]; - char f[400]; - - string hdr = ""; - snprintf(f, sizeof(f), "%s %d %s\r\n", PROTOCOL, status, title); - hdr += f; - snprintf(f, sizeof(f), "Server: %s\r\n", SERVER); - hdr += f; - now = time(NULL); - strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&now)); - snprintf(f, sizeof(f), "Date: %s\r\n", timebuf); - hdr += f; - if (extra) { - snprintf(f, sizeof(f), "%s\r\n", extra); - *(mLog->log())<< DEBUGPREFIX << " " << f; - hdr += f; - } - if (mime) { - snprintf(f, sizeof(f), "Content-Type: %s\r\n", mime); - hdr += f; - } - if (length >= 0) { - snprintf(f, sizeof(f), "Content-Length: %lld\r\n", length); - // *(mLog->log())<< DEBUGPREFIX << " " << f << endl; - hdr += f; - } - if (date != -1) { - strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date)); - snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf); - hdr += f; - } - if (mAcceptRanges) { - snprintf(f, sizeof(f), "Accept-Ranges: bytes\r\n"); - hdr += f; - } - snprintf(f, sizeof(f), "Connection: close\r\n"); - hdr += f; - - snprintf(f, sizeof(f), "\r\n"); - hdr += f; - - - if (mBlkLen != 0) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR in SendHeader: mBlkLen != 0!!! --> Overwriting" << endl; - } - mBlkLen = hdr.size(); - strcpy(mBlkData, hdr.c_str()); - -} - -int cHttpResource::sendFile(struct stat *statbuf32) { - // Send the First Datachunk, incl all headers - struct stat64 statbuf; - - if (stat64(mPath.c_str(), &statbuf) < 0) { - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - *(mLog->log())<< DEBUGPREFIX - << mReqId << " SendFile mPath= " << mPath - << endl; - - char f[400]; - - if (openFile(mPath.c_str()) == ERROR) { - sendError(403, "Forbidden", NULL, "Access denied."); - return OKAY; - } - mFile = fopen(mPath.c_str(), "r"); - // int ret = OKAY; - - if (!mFile) { - sendError(403, "Forbidden", NULL, "Access denied."); - return OKAY; - } - - mFileSize = S_ISREG(statbuf.st_mode) ? statbuf.st_size : -1; - - *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId - << " mFileSize= " <log())<< "fd= " << mFd << " mReqId= "<< mReqId - << " rangeHdr.begin= " <log())<< "fd= " << mFd << " mReqId= "<< mReqId - << ": Done mRemLength= "<< mRemLength << " mContentType= " << mContentType - << endl; -#endif - mConnState = SERVING; - - return OKAY; - -} - -const char *cHttpResource::getMimeType(const char *name) { - char *ext = strrchr((char*)name, '.'); - if (!ext) - return NULL; - // if (ext.compare(".html") || ext.compare(".htm")) return "text/html"; - if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; - if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; - if (strcmp(ext, ".gif") == 0) return "image/gif"; - if (strcmp(ext, ".png") == 0) return "image/png"; - if (strcmp(ext, ".xml") == 0) return "application/xml"; - if (strcmp(ext, ".css") == 0) return "text/css"; - if (strcmp(ext, ".js") == 0) return "text/javascript"; - if (strcmp(ext, ".au") == 0) return "audio/basic"; - if (strcmp(ext, ".wav") == 0) return "audio/wav"; - if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; - if (strcmp(ext, ".mp4") == 0) return "video/mp4"; - if (strcmp(ext, ".vdr") == 0) return "video/mpeg"; - if (strcmp(ext, ".ts") == 0) return "video/mpeg"; - if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; - if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; - if (strcmp(ext, ".mpd") == 0) return "application/dash+xml"; - if (strcmp(ext, ".m3u8") == 0) return "application/x-mpegURL"; - - return NULL; -} - - -string cHttpResource::getConnStateName() { - string state_string; - switch (mConnState) { - case WAITING: - state_string = "WAITING"; - break; - case READHDR: - state_string = "READ Req HDR"; - break; - case READPAYLOAD: - state_string = "READ Req Payload"; - break; - case SERVING: - state_string = "SERVING"; - break; - case TOCLOSE: - state_string = "TOCLOSE"; - break; - default: - state_string = "UNKNOWN"; - break; - } - return state_string; -} - - -string cHttpResource::getOwnIp(int fd) { +// common for all mem functions +//string cHttpResource::getOwnIp(int fd) { +string cHttpResource::getOwnIp() { struct sockaddr_in sock; socklen_t len_inet = sizeof(sock); - int ret = getsockname(fd, (struct sockaddr *)&sock, &len_inet); + int ret = getsockname(mFd, (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)); } -uint64_t cHttpResource::getVdrFileSize() { - // iter over all vdr files and get file size - struct stat statbuf; - string file_structure = "%s/%05d.ts"; // Only ts supported for HLS and HAS - char pathbuf[4096]; - int vdr_idx = 0; - uint64_t total_file_size = 0; - bool more_to_go = true; - - while (more_to_go) { - vdr_idx ++; - snprintf(pathbuf, sizeof(pathbuf), file_structure.c_str(), mDir.c_str(), vdr_idx); - if (stat(pathbuf, &statbuf) >= 0) { - total_file_size += statbuf.st_size; - } - else { - more_to_go = false; - } - } - return total_file_size; -} - -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)); - mVersion = line.substr(line.find_last_of(" ") +1); -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " ReqLine= " << line << endl; -#endif - if (mVersion.compare(0, 4, "HTTP") != 0) { -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " ERROR: No HTTP request -> Closing Connection" << line << endl; -#endif - return ERROR; - } - - size_t pos = mRequest.find('?'); - if (pos != string::npos) - mQuery = mRequest.substr (pos+1, string::npos); - mPath = cUrlEncode::doUrlSaveDecode(mRequest.substr(0, mRequest.find('?'))); - *(mLog->log())<< DEBUGPREFIX - << " mMethod= " << mMethod - << " mPath= " << mPath - << " mVer= " << mVersion - << " mQuery= " << mQuery - // << " HexDump= " << endl << cUrlEncode::hexDump(mPath) << endl - << endl; - return OKAY; -} - -int cHttpResource::parseHttpHeaderLine (string line) { - string hdr_name = line.substr(0, line.find_first_of(":")); - string hdr_val = line.substr(line.find_first_of(":") +2); - - if (hdr_name.compare("Range") == 0) { - parseRangeHeaderValue(hdr_val); - *(mLog->log()) << DEBUGPREFIX - << " Range: Begin= " << rangeHdr.begin - << " End= " << rangeHdr.end - << endl; - } - if (hdr_name.compare("User-Agent") == 0) { - mUserAgent = hdr_val; - *(mLog->log())<< " User-Agent: " << hdr_val - << endl; - } - if (hdr_name.compare("Content-Length") == 0) { - mReqContentLength = atoll(hdr_val.c_str()); - //#ifndef DEBUG - *(mLog->log())<< " Content-Length: " << mReqContentLength - << endl; - //#endif - } - return 0; -} - -void cHttpResource::checkRecording() { - // sets mIsRecording to true when the recording is still on-going - mIsRecording = false; - -#ifndef STANDALONE - time_t now = time(NULL); - - // cRecordings* recordings = mFactory->getRecordings(); - cRecordings* recordings = &Recordings; -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " GetByName(" <GetByName(mPath.c_str()); - if (rec != NULL) { - const cEvent *ev = rec->Info()->GetEvent(); - if (ev != NULL) { - if (now < ev->EndTime()) { - // still recording - mIsRecording = true; - - // mRecProgress * curFileSize = estimated File Size - mRecProgress = (ev->EndTime() - ev->StartTime()) *1.1 / (rec->NumFrames() / rec->FramesPerSecond()); - // mRecProgress = (ev->EndTime() - ev->StartTime()) *1.0/ (now - ev->StartTime()); -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " **** is still recording for mIsRecording= " - << mIsRecording - << " mRecProgress= " << mRecProgress << endl; -#endif - } - } - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " checking, whether recording is on-going" - << " now= " << now << " start= " << rec->Start() - << " curDur= " << rec->LengthInSeconds() - << endl; -#endif - } -#ifndef DEBUG - else { - *(mLog->log())<< DEBUGPREFIX - << " **** Recording Entry Not found **** " << endl; - } -#endif -#endif -} - -int cHttpResource::parseRangeHeaderValue(string val) { - rangeHdr.isRangeRequest = true; - size_t pos_equal = val.find_first_of('='); - size_t pos_minus = val.find_first_of('-'); - - string range_type = val.substr(0, pos_equal); - string first_val= val.substr(pos_equal+1, pos_minus -(pos_equal+1)); - rangeHdr.begin = atoll(first_val.c_str()); - - string sec_val = ""; - if ((pos_minus +1)< val.size()){ - sec_val = val.substr(pos_minus+1); - rangeHdr.end = atoll(sec_val.c_str()); - } - return 0; -} - -int cHttpResource::openFile(const char *name) { - mFile = fopen(name, "r"); - if (!mFile) { - *(mLog->log())<< DEBUGPREFIX - << " fopen failed pathbuf= " << name - << endl; - // sendError(403, "Forbidden", NULL, "Access denied."); - return ERROR; - } - return OKAY; -} - diff --git a/httpresource.h b/httpresource.h index c563b3c..e170b4f 100755 --- a/httpresource.h +++ b/httpresource.h @@ -25,10 +25,14 @@ #include #include +#include #include + #include "log.h" #include "httpresource_base.h" +#define MAXLEN 32768 + using namespace std; struct cRange { @@ -53,24 +57,9 @@ enum eConnState { TOCLOSE }; -enum eContentType { - NYD, // Not Yet Defined - VDRDIR, - SINGLEFILE, - MEMBLOCK -}; - -struct sFileEntry { - string sName; - string sPath; - int sStart; - -sFileEntry(string n, string l, int s) : sName(n), sPath(l), sStart(s) { - }; -}; - class SmartTvServer; -class cResumeEntry; +class cResponseBase; + class cHttpResource : public cHttpResourceBase { @@ -82,34 +71,24 @@ class cHttpResource : public cHttpResourceBase { int handleWrite(); int checkStatus(); - int readFromClient(); - void threadLoop(); - int run(); + public: - private: - // SmartTvServer* mFactory; Log* mLog; - // int mServerPort; - // int mFd; - // int mReqId; - time_t mConnTime; int mHandleReadCount; bool mConnected; - eConnState mConnState; - eContentType mContentType; + eConnState mConnState; string mReadBuffer; string mMethod; - string *mResponseMessage; - int mResponseMessagePos; + /* char* mBlkData; int mBlkPos; int mBlkLen; - +*/ string mRequest; string mQuery; string mPath; @@ -122,17 +101,10 @@ class cHttpResource : public cHttpResourceBase { bool mAcceptRanges; cRange rangeHdr; - unsigned long long mFileSize; - bool mStreamToEnd; - uint64_t mRemLength; - FILE *mFile; - int mVdrIdx; - string mFileStructure; - bool mIsRecording; - float mRecProgress; + + cResponseBase* mResponse; void setNonBlocking(); - int fillDataBlk(); int handlePost(); int handleHeadRequest(); @@ -140,56 +112,14 @@ class cHttpResource : public cHttpResourceBase { int processHttpHeaderNew(); int readRequestPayload(); - void sendError(int status, const char *title, const char *extra, const char *text); - int sendDir(struct stat *statbuf); - int sendVdrDir(struct stat *statbuf); - int sendRecordingsXml (struct stat *statbuf); - 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); - - - int sendMediaSegment (struct stat *statbuf); - void sendHeaders(int status, const char *title, const char *extra, const char *mime, - long long int length, time_t date); - - int sendFile(struct stat *statbuf); - - // Helper Functions - const char *getMimeType(const char *name); string getConnStateName(); - string getOwnIp(int fd); - uint64_t getVdrFileSize(); - void checkRecording(); - bool isTimeRequest(struct stat *statbuf); int parseRangeHeaderValue(string); int parseHttpRequestLine(string); int parseHttpHeaderLine (string); int parseQueryLine (vector *avps); - int parseResume(cResumeEntry &entry, string &id); - - int parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf); - int getQueryAttributeValue(vector *avps, string id, string &val); - int openFile(const char *name); - 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); + + string getOwnIp(); }; #endif diff --git a/httpresource_base.h b/httpresource_base.h index 81a62c6..6494443 100644 --- a/httpresource_base.h +++ b/httpresource_base.h @@ -35,12 +35,10 @@ class cHttpResourceBase { 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 index e9f18d0..dabcfe7 100644 --- a/mngurls.c +++ b/mngurls.c @@ -39,6 +39,7 @@ cManageUrls::~cManageUrls() { //TODO: delete entries }; + //called from outside to add an entry void cManageUrls::appendEntry(string type, string url) { // iter through entries @@ -61,6 +62,27 @@ void cManageUrls::appendEntry(string type, string url) { } } + +void cManageUrls::deleteEntry(string type, string url) { + *(mLog->log()) << " cManageUrls::deleteEntry: type= " << type << "guid= " << 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) { + // delete the entry here + *(mLog->log()) << " cManageUrls::deleteEntry ... " << endl; + found = true; + break; + } + } + +} + + + size_t cManageUrls::size() { return mEntries.size(); } diff --git a/mngurls.h b/mngurls.h index b247808..d500f61 100644 --- a/mngurls.h +++ b/mngurls.h @@ -22,12 +22,6 @@ //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__ @@ -47,6 +41,8 @@ class cManageUrls { virtual ~cManageUrls(); void appendEntry (string type, string guid); + void deleteEntry(string type, string guid); + size_t size(); sUrlEntry* getEntry(int index); diff --git a/responsebase.c b/responsebase.c new file mode 100644 index 0000000..77bca0a --- /dev/null +++ b/responsebase.c @@ -0,0 +1,162 @@ +/* + * responsebase.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 "responsebase.h" +#include "httpresource.h" +#include "smarttvfactory.h" +#include "log.h" + +#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd +#define OKAY 0 +#define ERROR (-1) +#define DEBUG + +#define PROTOCOL "HTTP/1.1" +#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" + + + +cResponseBase::cResponseBase(cHttpResource* req): mBlkData(NULL), mBlkPos(0), mBlkLen(0), mLog(NULL), mRequest(req), mRemLength(0), mError (false) { + mLog = Log::getInstance(); + + mBlkData = new char[MAXLEN]; + +} + +cResponseBase::~cResponseBase() { + delete[] mBlkData; +} + +bool cResponseBase::isHeadRequest() { + if (mRequest->mMethod.compare("HEAD") == 0) { + *(mLog->log())<< DEBUGPREFIX << " HEAD Request" << endl; + mError= true; + sendHeaders(200, "OK", NULL, NULL, -1, -1); + return true; + } + return false; +} + +void cResponseBase::sendError(int status, const char *title, const char *extra, const char *text) { + char f[400]; + + mError = true; + string hdr = ""; + sendHeaders(status, title, extra, "text/plain", -1, -1); + + snprintf(f, sizeof(f), "%s\r\n", text); + hdr += f; + + // strcpy(&(mRequest->mBlkData[mRequest->mBlkLen]), hdr.c_str()); + strcpy(&mBlkData[mBlkLen], hdr.c_str()); + mBlkLen += hdr.size(); +} + +void cResponseBase::sendHeaders(int status, const char *title, const char *extra, const char *mime, + long long int length, time_t date) { + + time_t now; + char timebuf[128]; + char f[400]; + + string hdr = ""; + snprintf(f, sizeof(f), "%s %d %s\r\n", PROTOCOL, status, title); + hdr += f; + snprintf(f, sizeof(f), "Server: %s\r\n", SERVER); + hdr += f; + now = time(NULL); + strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&now)); + snprintf(f, sizeof(f), "Date: %s\r\n", timebuf); + hdr += f; + if (extra) { + snprintf(f, sizeof(f), "%s\r\n", extra); + *(mLog->log())<< DEBUGPREFIX << " " << f; + hdr += f; + } + if (mime) { + snprintf(f, sizeof(f), "Content-Type: %s\r\n", mime); + hdr += f; + } + if (length >= 0) { + snprintf(f, sizeof(f), "Content-Length: %lld\r\n", length); + hdr += f; + } + if (date != -1) { + strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date)); + snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf); + hdr += f; + } + snprintf(f, sizeof(f), "Accept-Ranges: bytes\r\n"); + hdr += f; + snprintf(f, sizeof(f), "Connection: close\r\n"); + hdr += f; + + snprintf(f, sizeof(f), "\r\n"); + hdr += f; + + if (mBlkLen != 0) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR in SendHeader: mBlkLen != 0!!! --> Overwriting" << endl; + } + mBlkLen = hdr.size(); + strcpy(mBlkData, hdr.c_str()); +} + + +int cResponseBase::fillDataBlk() { + + if (mError) { + mRequest->mConnState = TOCLOSE; + return ERROR; + } +} + +bool cResponseBase::isBlkWritten() { + return ((mBlkLen == mBlkPos) ? true : false); +} + +int cResponseBase::writeData(int fd) { + int this_write = write(fd, &mBlkData[mBlkPos], mBlkLen - mBlkPos); + mBlkPos += this_write; + return this_write; +} + + +cResponseOk::cResponseOk (cHttpResource* req, int status, const char *title, const char *extra, const char *mime, long long int length, time_t date) : cResponseBase(req) { + + if (isHeadRequest()) + return; + sendHeaders(status, title, extra, mime, length, date); +} + +cResponseOk::~cResponseOk() { +} + + +cResponseError::cResponseError(cHttpResource* req, int status, const char *title, const char *extra, const char *text) : cResponseBase(req) { + + sendError(status, title, extra, text); +} + +cResponseError::~cResponseError() { +} diff --git a/responsebase.h b/responsebase.h new file mode 100644 index 0000000..18ea439 --- /dev/null +++ b/responsebase.h @@ -0,0 +1,77 @@ +/* + * responsebase.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 __HTTPRESPONSEBASE_H__ +#define __HTTPRESPONSEBASE_H__ + +#include +#include + +class cHttpResource; +class Log; + +class cResponseBase { + public: + cResponseBase(cHttpResource* req); + virtual ~cResponseBase(); + + virtual int fillDataBlk(); + + bool isBlkWritten(); + int writeData(int fd); + + char* mBlkData; + int mBlkPos; + int mBlkLen; + + protected: + bool isHeadRequest(); + + void sendError(int status, const char *title, const char *extra, const char *text); + void sendHeaders(int status, const char *title, const char *extra, const char *mime, + long long int length, time_t date); + + Log* mLog; + cHttpResource* mRequest; + uint64_t mRemLength; + bool mError; +}; + + +class cResponseOk : public cResponseBase { + public: + cResponseOk(cHttpResource*, int status, const char *title, const char *extra, const char *mime, + long long int length, time_t date ); + virtual ~cResponseOk(); + + // int fillDataBlk(); +}; + +class cResponseError : public cResponseBase { + public: + cResponseError(cHttpResource* req, int status, const char *title, const char *extra, const char *text ); + virtual ~cResponseError(); + + // int fillDataBlk(); +}; + +#endif diff --git a/responsefile.c b/responsefile.c new file mode 100644 index 0000000..9724957 --- /dev/null +++ b/responsefile.c @@ -0,0 +1,174 @@ +/* + * responsefile.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 "responsefile.h" +#include "httpresource.h" + +#include +#include + +//#define MAXLEN 4096 +#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd +#define OKAY 0 +#define ERROR (-1) +#define DEBUG + + +cResponseFile::cResponseFile(cHttpResource* req) : cResponseBase(req), mFile(NULL), mFileSize(0) { +} + +cResponseFile::~cResponseFile() { + if (mFile != NULL) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: mFile still open. Closing now..." << endl; + fclose(mFile); + mFile = NULL; + } +} + + +const char *cResponseFile::getMimeType(const char *name) { + char *ext = strrchr((char*)name, '.'); + if (!ext) + return NULL; + // if (ext.compare(".html") || ext.compare(".htm")) return "text/html"; + if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; + if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; + if (strcmp(ext, ".gif") == 0) return "image/gif"; + if (strcmp(ext, ".png") == 0) return "image/png"; + if (strcmp(ext, ".xml") == 0) return "application/xml"; + if (strcmp(ext, ".css") == 0) return "text/css"; + if (strcmp(ext, ".js") == 0) return "text/javascript"; + if (strcmp(ext, ".au") == 0) return "audio/basic"; + if (strcmp(ext, ".wav") == 0) return "audio/wav"; + if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; + if (strcmp(ext, ".mp4") == 0) return "video/mp4"; + if (strcmp(ext, ".vdr") == 0) return "video/mpeg"; + if (strcmp(ext, ".ts") == 0) return "video/mpeg"; + if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; + if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; + if (strcmp(ext, ".mpd") == 0) return "application/dash+xml"; + if (strcmp(ext, ".m3u8") == 0) return "application/x-mpegURL"; + + return NULL; +} + + +int cResponseFile::sendFile() { + // Send the First Datachunk, incl all headers + + if (isHeadRequest()) + return OKAY; + + struct stat64 statbuf; + + if (stat64((mRequest->mPath).c_str(), &statbuf) < 0) { + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + *(mLog->log())<< DEBUGPREFIX + << " SendFile mPath= " << mRequest->mPath + << endl; + + char f[400]; + + if (openFile((mRequest->mPath).c_str()) == ERROR) { + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + + mFile = fopen((mRequest->mPath).c_str(), "r"); + + if (!mFile) { + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + + mFileSize = S_ISREG(statbuf.st_mode) ? statbuf.st_size : -1; + + *(mLog->log())<< "fd= " << mRequest->mFd << " mReqId= "<< mRequest->mReqId + << " mFileSize= " <rangeHdr).isRangeRequest) { + mRemLength = mFileSize; + sendHeaders(200, "OK", NULL, getMimeType((mRequest->mPath).c_str()), mFileSize, statbuf.st_mtime); + } + else { // Range request + fseeko64(mFile, (mRequest->rangeHdr).begin, SEEK_SET); + if ((mRequest->rangeHdr).end == 0) + (mRequest->rangeHdr).end = mFileSize; + mRemLength = ((mRequest->rangeHdr).end-(mRequest->rangeHdr).begin); + *(mLog->log())<< "fd= " << mRequest->mFd << " mReqId= "<< mRequest->mReqId + << " rangeHdr.begin= " <<(mRequest->rangeHdr).begin << " rangeHdr.end= " << (mRequest->rangeHdr).end + << " Content-Length= " << mRemLength << endl; + + snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", (mRequest->rangeHdr).begin, ((mRequest->rangeHdr).end -1), mFileSize); + sendHeaders(206, "Partial Content", f, getMimeType((mRequest->mPath).c_str()), ((mRequest->rangeHdr).end-(mRequest->rangeHdr).begin), statbuf.st_mtime); + } + +#ifndef DEBUG + *(mLog->log())<< "fd= " << mRequest->mFd << " mReqId= "<< mRequest->mReqId + << ": Done mRemLength= "<< mRemLength + << endl; +#endif + mRequest->mConnState = SERVING; + + return OKAY; + +} + + +int cResponseFile::openFile(const char *name) { + mFile = fopen(name, "r"); + if (!mFile) { + *(mLog->log())<< DEBUGPREFIX + << " fopen failed pathbuf= " << name + << endl; + // sendError(403, "Forbidden", NULL, "Access denied."); + return ERROR; + } + return OKAY; +} + + +int cResponseFile::fillDataBlk() { + // char pathbuf[4096]; + mBlkPos = 0; + int to_read = 0; + + if (mError) + return ERROR; + + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + mBlkLen = fread(mBlkData, 1, to_read, mFile); + mRemLength -= mBlkLen; + if (mBlkLen == 0) { + + // read until EOF + fclose(mFile); + mFile = NULL; + return ERROR; + } + return OKAY; +} + diff --git a/responsefile.h b/responsefile.h new file mode 100644 index 0000000..b4a1b36 --- /dev/null +++ b/responsefile.h @@ -0,0 +1,51 @@ +/* + * responsefile.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 __RESPONSE_FILE_H__ +#define __RESPONSE_FILE_H__ + +#include +#include "responsebase.h" + +class cHttpResource; + +// create the complete response for the file request +// info on range request should be provided. +class cResponseFile : public cResponseBase { + public: + cResponseFile(cHttpResource* ); + virtual ~cResponseFile(); // same as sendFile + + int fillDataBlk(); + + int sendFile(); + + private: + const char *getMimeType(const char *name) ; + + int openFile(const char *name); + + FILE *mFile; + unsigned long long mFileSize; +}; + +#endif diff --git a/responsememblk.c b/responsememblk.c new file mode 100644 index 0000000..a695340 --- /dev/null +++ b/responsememblk.c @@ -0,0 +1,1689 @@ +/* + * responsememblk.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 "responsememblk.h" +#include "httpresource.h" +#include "url.h" +#include "smarttvfactory.h" + +#include + +#ifndef STANDALONE +#include +#include +#include +#include +#include + +#else +//standalone +#include +#include + +#include +//#include +//#include +#include + +#endif + + +//#define MAXLEN 32768 +#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd +#define OKAY 0 +#define ERROR (-1) +#define DEBUG + +class cResumeEntry { + public: + string mFilename; + float mResume; + + friend ostream& operator<<(ostream& out, const cResumeEntry& o) { + out << "mFilename= " << o.mFilename << " mResume= " << o.mResume << endl; + return out; + }; + cResumeEntry():mFilename(), mResume(-1.0) {}; +}; + + +struct sTimerEntry { + string name; + time_t startTime; + int duration; +sTimerEntry(string t, time_t s, int d) : name(t), startTime(s), duration(d) {}; +}; + + +cResponseMemBlk::cResponseMemBlk(cHttpResource* req) : cResponseBase(req), mResponseMessage(NULL), mResponseMessagePos(0) { + + gettimeofday(&mResponseStart,0); + +} + +cResponseMemBlk::~cResponseMemBlk() { + + timeval now; + gettimeofday(&now, 0); + + // long diff; // in ms + // diff = (now.tv_sec - mResponseStart.tv_sec) *1000; + // diff += (now.tv_usec - mResponseStart.tv_usec) /1000; + + long long diff; // in us + diff = (now.tv_sec - mResponseStart.tv_sec) *1000000; + diff += (now.tv_usec - mResponseStart.tv_usec) ; + + *(mLog->log())<< DEBUGPREFIX + << " cResponseMemBlk: Response duration= " << diff/1000.0 << " ms" + << endl; + +} + +int cResponseMemBlk::receiveYtUrl() { + vector avps; + mRequest->parseQueryLine(&avps); + string line; + string store_str; + bool store = true; + + if (isHeadRequest()) + return OKAY; + + if (mRequest->getQueryAttributeValue(&avps, "store", store_str) == OKAY){ + if (store_str.compare("false")==0) { + store = false; + *(mLog->log()) << DEBUGPREFIX + << " receiveYtUrl: set store to false " + << endl; + + } + } + if (mRequest->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; + } + + mRequest->mFactory->pushYtVideoId(line, store); + + if (store) + mRequest->mFactory->storeYtVideoId(line); + + sendHeaders(200, "OK", NULL, NULL, 0, -1); + return OKAY; + + } + + sendError(400, "Bad Request", NULL, "Mandatory Line attribute not present."); + return OKAY; + +} + +void cResponseMemBlk::receiveClientInfo() { + vector avps; + mRequest->parseQueryLine(&avps); + + string mac = ""; + string ip = ""; + string state = ""; + + if (isHeadRequest()) + return; + + mRequest->getQueryAttributeValue(&avps, "mac", mac) ; + mRequest->getQueryAttributeValue(&avps, "ip", ip); + mRequest->getQueryAttributeValue(&avps, "state", state); + + // state: started, running, stopped + *(mLog->log())<< DEBUGPREFIX + << " receiveClientInfo 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) { + mRequest->mFactory->removeTvClient(ip, mac, time(NULL)); + } + else { + mRequest->mFactory->updateTvClient(ip, mac, time(NULL)); + } + sendHeaders(200, "OK", NULL, NULL, 0, -1); + *(mLog->log())<< DEBUGPREFIX + << " receiveClientInfo -done " + << endl; + +} + + + +int cResponseMemBlk::parseResume(cResumeEntry &entry, string &id) { + bool done = false; + size_t cur_pos = 0; + + bool have_filename = false; + bool have_resume = false; + + if (isHeadRequest()) + return OKAY; + + while (!done) { + size_t pos = mRequest->mPayload.find('\n', cur_pos); + if (pos == string::npos) { + done = true; + continue; + } + size_t pos_col = mRequest->mPayload.find(':', cur_pos); + string attr= mRequest->mPayload.substr(cur_pos, (pos_col- cur_pos)); + string val = mRequest->mPayload.substr(pos_col +1, (pos - pos_col-1)); + + if (attr == "filename") { + have_filename = true; + entry.mFilename = cUrlEncode::doUrlSaveDecode(val); + *(mLog->log())<< DEBUGPREFIX + << " filename= " << entry.mFilename + << endl; + } + else if (attr == "resume") { + have_resume = true; + entry.mResume = atof(val.c_str()); + *(mLog->log())<< DEBUGPREFIX + << " mResume= " << entry.mResume + << endl; + } + else { + *(mLog->log())<< DEBUGPREFIX + << " parseResume: ERROR: Unknown attr= " << attr + << " with val= " << val + << endl; + } + cur_pos = pos +1; + if (cur_pos >= mRequest->mPayload.size()) + done= true; + } + if (have_resume && have_filename ) + return OKAY; + else + return ERROR; +} + +int cResponseMemBlk::receiveResume() { + string dev_id; + cResumeEntry entry; + + if (isHeadRequest()) + return OKAY; + + if (parseResume(entry, dev_id) == ERROR) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR parsing resume" + << endl; + } + + *(mLog->log())<< DEBUGPREFIX + << " Resume: id= " << dev_id + << " resume= " << entry << endl; + + vector avps; + mRequest->parseQueryLine(&avps); + string guid; + string resume_str; + + if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY){ + entry.mFilename = cUrlEncode::doUrlSaveDecode(guid); + + *(mLog->log())<< DEBUGPREFIX + << " Found a id Parameter: " << guid + << endl; + } + if (mRequest->getQueryAttributeValue(&avps, "resume", resume_str) == OKAY){ + entry.mResume = atof(resume_str.c_str()); + *(mLog->log())<< DEBUGPREFIX + << " Found a resume Parameter: " << entry.mResume + << endl; + } + +#ifndef STANDALONE + cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); + if (rec == NULL) { + //Error 404 + sendError(404, "Not Found", NULL, "Failed to find recording."); + return OKAY; + } + + cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); + *(mLog->log())<< DEBUGPREFIX + << " Resume: " << entry.mFilename + << " saving Index= " << int(entry.mResume * rec->FramesPerSecond() ) + << " mResume= " < avps; + mRequest->parseQueryLine(&avps); + string id = ""; + + if (mRequest->getQueryAttributeValue(&avps, "id", id) == ERROR){ + *(mLog->log())<< DEBUGPREFIX + << " ERROR: id not found in query." + << endl; + sendError(400, "Bad Request", NULL, "no id in query line"); + return OKAY; + } + mRequest->mPath = cUrlEncode::doUrlSaveDecode(id); + + cRecording* rec = Recordings.GetByName(mRequest->mPath.c_str()); + if (rec == NULL) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Recording not found. Deletion failed: mPath= " << mRequest->mPath + << endl; + sendError(404, "Not Found.", NULL, "Recording not found. Deletion failed!"); + return OKAY; + } + if ( rec->Delete() ) { + Recordings.DelByName(rec->FileName()); + // Recordings.DelByName(mPath.c_str()); + } + else { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: rec->Delete() returns false. mPath= " << mRequest->mPath + << endl; + sendError(500, "Internal Server Error", NULL, "deletion failed!"); + return OKAY; + } + + *(mLog->log())<< DEBUGPREFIX + << " Deleted." + << endl; + sendHeaders(200, "OK", NULL, NULL, -1, -1); + #endif + return OKAY; +} + + +//*************************** +//**** Creade index.html **** +//*************************** +int cResponseMemBlk::sendDir(struct stat *statbuf) { + char pathbuf[4096]; + char f[400]; + int len; + + if (isHeadRequest()) + return OKAY; + + mRequest->mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendDir: mPath= " << mRequest->mPath << endl; +#endif + len = mRequest->mPath.length(); + // int ret = OKAY; + + if (len == 0 || mRequest->mPath[len - 1] != '/') { + snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mRequest->mPath.c_str()); + sendError(302, "Found", pathbuf, "Directories must end with a slash."); + return OKAY; + } + + /* thlo: TODO + snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mRequest->mPath.c_str()); + if (stat(pathbuf, statbuf) >= 0) { + mRequest->mPath = pathbuf; + // mFileSize = statbuf->st_size; + mRemLength = statbuf->st_size; + mRequest->mContentType = SINGLEFILE; + return sendFile(statbuf); + } +*/ +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendDir: create index.html " << endl; +#endif + DIR *dir; + struct dirent *de; + + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + + string hdr = ""; + snprintf(f, sizeof(f), "Index of %s\r\n", mRequest->mPath.c_str()); + hdr += f; + snprintf(f, sizeof(f), "

    Index of %s

    \r\n
    \n", mRequest->mPath.c_str());
    +  hdr += f;
    +  snprintf(f, sizeof(f), "Name                             Last Modified              Size\r\n");
    +  hdr += f;
    +  snprintf(f, sizeof(f), "
    \r\n"); + hdr += f; + + *mResponseMessage += hdr; + hdr = ""; + + if (len > 1) { + snprintf(f, sizeof(f), "..\r\n"); + hdr += f; + } + *mResponseMessage += hdr; + + dir = opendir(mRequest->mPath.c_str()); + while ((de = readdir(dir)) != NULL) { + char timebuf[32]; + struct tm *tm; + strcpy(pathbuf, mRequest->mPath.c_str()); + // printf (" -Entry: %s\n", de->d_name); + strcat(pathbuf, de->d_name); + + stat(pathbuf, statbuf); + tm = gmtime(&(statbuf->st_mtime)); + strftime(timebuf, sizeof(timebuf), "%d-%b-%Y %H:%M:%S", tm); + + hdr = ""; + snprintf(f, sizeof(f), "", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : ""); + hdr += f; + + snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : " "); + hdr += f; + + if (strlen(de->d_name) < 32) { + snprintf(f, sizeof(f), "%*s", 32 - strlen(de->d_name), ""); + hdr += f; + } + if (S_ISDIR(statbuf->st_mode)) { + snprintf(f, sizeof(f), "%s\r\n", timebuf); + hdr += f; + } + else { + snprintf(f, sizeof(f), "%s\r\n", timebuf); + hdr += f; + } + + *mResponseMessage += hdr; + } + closedir(dir); + snprintf(f, sizeof(f), "
    \r\n
    \r\n
    %s
    \r\n\r\n", SERVER); + *mResponseMessage += f; + + sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), statbuf->st_mtime); + + mRemLength = 0; + return OKAY; +} + + + + +// ------------------------------------------- +// Manifest +// ------------------------------------------- + + +int cResponseMemBlk::sendManifest (struct stat *statbuf, bool is_hls) { + if (isHeadRequest()) + return OKAY; + +#ifndef STANDALONE + + size_t pos = mRequest->mPath.find_last_of ("/"); + + mRequest->mDir = mRequest->mPath.substr(0, pos); + string mpd_name = mRequest->mPath.substr(pos+1); + + float seg_dur = mRequest->mFactory->getConfig()->getSegmentDuration() *1.0; + + cRecordings* recordings = &Recordings; + cRecording* rec = recordings->GetByName(mRequest->mDir.c_str()); + double duration = rec->NumFrames() / rec->FramesPerSecond(); + + int bitrate = (int)((getVdrFileSize() *8.0 * mRequest->mFactory->getConfig()->getHasBitrateCorrection()/ duration) +0.5); + + time_t now = time(NULL); + + if (rec->Info() != NULL){ + if (rec->Info()->GetEvent() != NULL) { + if (rec->Info()->GetEvent()->EndTime() > now) { + + float corr = (now - rec->Info()->GetEvent()->StartTime()) - duration; + duration = rec->Info()->GetEvent()->Duration() -int(corr); + *(mLog->log()) << DEBUGPREFIX + << " is Recording: Duration= " << duration << " sec" + << " correction: " << int(corr) + << endl; + } + } + else + *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info()->GetEvent() is NULL " << endl; + } + else + *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info() is NULL " << endl; + + // duration is now either the actual duration of the asset or the target duration of the asset + int end_seg = int (duration / seg_dur) +1; + + *(mLog->log()) << DEBUGPREFIX + << " Manifest for mRequest->mDir= " << mRequest->mDir + << " duration= " << duration + << " seg_dur= " << seg_dur + << " end_seg= " << end_seg + << endl; + + + + if (is_hls) { + writeM3U8(duration, bitrate, seg_dur, end_seg); + } + else { + writeMPD(duration, bitrate, seg_dur, end_seg); + } + +#endif + return OKAY; +} + + +void cResponseMemBlk::writeM3U8(double duration, int bitrate, float seg_dur, int end_seg) { + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + char buf[30]; + + string hdr = ""; + + + *mResponseMessage += "#EXTM3U\n"; + // snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", (seg_dur-1)); + snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", int(seg_dur)); + hdr = buf; + *mResponseMessage += hdr; + + *mResponseMessage += "#EXT-X-MEDIA-SEQUENCE:1\n"; + *mResponseMessage += "#EXT-X-KEY:METHOD=NONE\n"; + + for (int i = 1; i < end_seg; i++){ + // snprintf(buf, sizeof(buf), "#EXTINF:%.1f,\n", (seg_dur-0.5)); + snprintf(buf, sizeof(buf), "#EXTINF:%.2f,\n", seg_dur); + hdr = buf; + *mResponseMessage += hdr; + + snprintf(buf, sizeof(buf), "%d-seg.ts\n", i); + hdr = buf; + *mResponseMessage += hdr; + } + *mResponseMessage += "#EXT-X-ENDLIST\n"; + + sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); +} + + +void cResponseMemBlk::writeMPD(double duration, int bitrate, float seg_dur, int end_seg) { + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + char buf[30]; + char line[400]; + + string hdr = ""; + + *mResponseMessage += "\n"; + + snprintf(line, sizeof(line), "mFactory->getConfig()->getHasMinBufferTime(), duration); + *mResponseMessage = *mResponseMessage + line; + + *mResponseMessage += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\" "; + *mResponseMessage += "xsi:schemaLocation=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\">\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + // SD: 720x 576 + // HD: 1280x 720 + // snprintf(line, sizeof(line), "\n", mRequest->mFactory->getConfig()->getHasBitrate()); + snprintf(line, sizeof(line), "\n", + bitrate, ((bitrate < 10000000)? "width=\"720\" height=\"576\"" : "width=\"1280\" height=\"720\"")); + *mResponseMessage = *mResponseMessage + line; + + hdr = "\n"; + *mResponseMessage += hdr; + + snprintf(buf, sizeof(buf), "\"%d\"", end_seg); + *mResponseMessage += "\n"; + + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += ""; + + sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); +} + + +uint64_t cResponseMemBlk::getVdrFileSize() { + // iter over all vdr files and get file size + struct stat statbuf; + string file_structure = "%s/%05d.ts"; // Only ts supported for HLS and HAS + char pathbuf[4096]; + int vdr_idx = 0; + uint64_t total_file_size = 0; + bool more_to_go = true; + + while (more_to_go) { + vdr_idx ++; + snprintf(pathbuf, sizeof(pathbuf), file_structure.c_str(), mRequest->mDir.c_str(), vdr_idx); + if (stat(pathbuf, &statbuf) >= 0) { + total_file_size += statbuf.st_size; + } + else { + more_to_go = false; + } + } + return total_file_size; +} + +// - Manifest End + + + +// -------------------- +// GET Resources +// -------------------- + + +// common for all create xml file modules +int cResponseMemBlk::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]; + + hdr += "\n"; + // snprintf(f, sizeof(f), "%s - %s", ); + hdr += "" + name +"\n"; + hdr += "" +link + "\n"; + hdr += "\n"; + + hdr += "" + guid + "\n"; + + snprintf(f, sizeof(f), "%d", no); + hdr += ""; + hdr += f; + hdr += "\n"; + + hdr += "" + programme +"\n"; + hdr += "" + desc + "\n"; + + snprintf(f, sizeof(f), "%ld", start); + hdr += ""; + hdr += f; + hdr += "\n"; + + snprintf(f, sizeof(f), "%d", dur); + hdr += ""; + hdr += f; + hdr += "\n"; + + if (fps != -1) + snprintf(f, sizeof(f), "%.2f\n", fps); + else + snprintf(f, sizeof(f), "unknown\n"); + hdr += f; + + switch (is_pes){ + case -1: + // unknown + hdr += "unknown\n"; + break; + case 0: + // true + hdr += "true\n"; + break; + case 1: + // false + hdr += "false\n"; + break; + default: + break; + } + + switch (is_new){ + case -1: + // unknown + hdr += "unknown\n"; + break; + case 0: + // true + hdr += "true\n"; + break; + case 1: + // false + hdr += "false\n"; + break; + default: + break; + } + + hdr += "\n"; + + *mResponseMessage += hdr; + + + // return writeToClient(hdr.c_str(), hdr.size()); + return OKAY; +} + + + + + +int cResponseMemBlk::sendUrlsXml () { + // read urls file and generate XML + string type; + string value; + string line; + if (isHeadRequest()) + return OKAY; + + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + + cManageUrls* urls = mRequest->mFactory->getUrlsObj(); + + // ifstream myfile ((mRequest->mFactory->getConfigDir() +"/urls.txt").c_str()); + // An empty xml is provided, if the file does not exist. + + //thlo: here to continue + *mResponseMessage = "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + for (uint i = 0; i < urls->size(); i++) { + *mResponseMessage += "\n"; + *mResponseMessage += (urls->getEntry(i))->mEntry; + *mResponseMessage += "\n\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 += "\n"; + *mResponseMessage += value; + *mResponseMessage += "\n\n"; + continue; + } + + *mResponseMessage += "\n"; + *mResponseMessage += "Unknown: " + line + " type=" + type; + + *mResponseMessage += "\n\n"; + } + myfile.close(); +*/ + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); + return OKAY; +} + + + + +// mediaXML +int cResponseMemBlk::parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf) { + char pathbuf[4096]; + string link; + DIR *dir; + struct dirent *de; + string dir_comp; + dir_comp = dir_base + dir_name + "/"; + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " parseFiles: Prefix= " << prefix + << " base= " << dir_base + << " dir= " << dir_name + << " comp= " << dir_comp + << endl; +#endif + + dir = opendir(dir_comp.c_str()); + if (stat(dir_comp.c_str(), statbuf) < 0) + return ERROR; + + while ((de = readdir(dir)) != NULL) { + if ((strcmp(de->d_name, ".") == 0) or (strcmp(de->d_name, "..") == 0)) { + continue; + } + + strcpy(pathbuf, dir_comp.c_str()); + strcat(pathbuf, de->d_name); + + stat(pathbuf, statbuf); + + if (S_ISDIR(statbuf->st_mode)) { + if (strcmp(&(pathbuf[strlen(pathbuf)-4]), ".rec") == 0) { + // vdr folder + time_t now = time(NULL); + struct tm tm_r; + struct tm t = *localtime_r(&now, &tm_r); + t.tm_isdst = -1; + // char [20] rest; + int start = -1; + sscanf(de->d_name, "%4d-%02d-%02d.%02d.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); + + // sscanf(de->d_name, "%4d-%02d-%02d.%02d%.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); + t.tm_year -= 1900; + t.tm_mon--; + t.tm_sec = 0; + start = mktime(&t); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " Vdr Folder Found: " << pathbuf << " start= " << start << endl; +#endif + + entries->push_back(sFileEntry(dir_name, pathbuf, start)); + } + else { + // regular file + parseFiles(entries, prefix + de->d_name + "~", dir_comp, de->d_name, statbuf); + } + } + else { + if ((de->d_name)[0] != '.' ) + entries->push_back(sFileEntry(prefix+de->d_name, pathbuf, 1)); + } + } + closedir(dir); + return OKAY; +} + +int cResponseMemBlk::sendMediaXml (struct stat *statbuf) { + char pathbuf[4096]; + string link; + string media_folder = mRequest->mFactory->getConfig()->getMediaFolder(); + + if (isHeadRequest()) + return OKAY; + + + mResponseMessage = new string(); + mResponseMessagePos = 0; + + *mResponseMessage = ""; + + mRequest->mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl; +#endif + + string own_ip = mRequest->getOwnIp(); + *(mLog->log()) << " OwnIP= " << own_ip << endl; + + vector entries; + + if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) { + sendError(404, "Not Found", NULL, "Media Folder likely not configured."); + return OKAY; + } + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + *mResponseMessage += hdr; + + hdr = ""; + + for (uint i=0; i < entries.size(); i++) { + + snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", own_ip.c_str(), mRequest->mServerPort, + cUrlEncode::doUrlSaveEncode(entries[i].sPath).c_str()); + if (writeXmlItem(cUrlEncode::doXmlSaveEncode(entries[i].sName), pathbuf, "NA", "NA", "-", + -1, entries[i].sStart, -1, -1, -1, -1) == ERROR) + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + *mResponseMessage += hdr; + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); + + return OKAY; +} + + + + + +int cResponseMemBlk::sendVdrStatusXml (struct stat *statbuf) { + if (isHeadRequest()) + return OKAY; + +#ifndef STANDALONE + + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + + mResponseMessagePos = 0; + + mRequest->mConnState = SERVING; + + int free; + int used; + int percent; + + percent = VideoDiskSpace(&free, &used); + + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + *mResponseMessage += "\n"; + snprintf(f, sizeof(f), "%d", free); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%d", used); + *mResponseMessage += f; + snprintf(f, sizeof(f), "%d", percent); + *mResponseMessage += f; + *mResponseMessage += "\n"; + + *mResponseMessage += "\n"; + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); +#else + sendHeaders(200, "OK", NULL, NULL, 0, -1); +#endif + return OKAY; +} + +int cResponseMemBlk::sendYtBookmarkletJs() { + *(mLog->log()) << DEBUGPREFIX + << " sendYtBookmarkletJs" << endl; + + vector avps; + mRequest->parseQueryLine(&avps); + string store_str = ""; + bool store= true; + + if (isHeadRequest()) + return OKAY; + + if (mRequest->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; + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + + stringstream own_host ; + own_host << "http://" + << mRequest->getOwnIp() + << ":" << mRequest->mServerPort; + + // string own_host = "http://"+mRequest->getOwnIp()+ ":" + 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 cResponseMemBlk::sendBmlInstHtml() { + *(mLog->log()) << DEBUGPREFIX + << " sendBmlInstHtml" << endl; + + if (isHeadRequest()) + return OKAY; + + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + + stringstream own_host ; + own_host << "http://" + << mRequest->getOwnIp() + << ":" << mRequest->mServerPort; + + *(mLog->log()) << " Ownhost= " << own_host << endl; + + *mResponseMessage = "" + "SmartTVWeb Bookmarklets" + "" + "
    " + "

    Bookmarklet for collecting YouTube Pages

    " + "
    " + "
    " + "

    Installation

    " + "Drag the link below to your Bookmarks toolbar" + "

    or

    " + "

    Right click and select “Bookmark This Link”

    " + "

    YT SaveNPlay: Save the video and also Play it.

    " + "

    YT Play: Play the video without saving.

    " + "
    " + "
    " + "

    Usage

    " + "

    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.

    " + "
    " + "
    " + "

    Have fun...

    " + ""; + + sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), -1); + return OKAY; +} + + + +int cResponseMemBlk::sendChannelsXml (struct stat *statbuf) { + if (isHeadRequest()) + return OKAY; + +#ifndef STANDALONE + + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /channels.xml" + << endl; +#endif + string own_ip = mRequest->getOwnIp(); + *(mLog->log()) << " OwnIP= " << own_ip << endl; + + vector avps; + mRequest->parseQueryLine(&avps); + string mode = ""; + bool add_desc = true; + + string no_channels_str = ""; + int no_channels = -1; + string group_sep = ""; + + if (mRequest->getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode == "nodesc") { + add_desc = false; + *(mLog->log())<< DEBUGPREFIX + << " Mode: No Description" + << endl; + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Mode: Unknown" + << endl; + } + } + if (mRequest->getQueryAttributeValue(&avps, "channels", no_channels_str) == OKAY){ + no_channels = atoi(no_channels_str.c_str()) ; + } + + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + hdr+= "VDR Channels List\n"; + + + *mResponseMessage += hdr; + + int count = mRequest->mFactory->getConfig()->getLiveChannels(); + if (no_channels > 0) + count = no_channels +1; + + cSchedulesLock * lock = new cSchedulesLock(false, 500); + const cSchedules *schedules = cSchedules::Schedules(*lock); + + for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { + if (channel->GroupSep()) { + if (mRequest->mFactory->getConfig()->getGroupSep() != IGNORE) { + // if emtpyFolderDown, always. + // otherwise, only when not empty + if (!((strcmp(channel->Name(), "") == 0) && (mRequest->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", own_ip.c_str(), *(channel->GetChannelID()).ToString()); + string link = f; + + const cSchedule *schedule = schedules->GetSchedule(channel->GetChannelID()); + string desc = "No description available"; + string title = "Not available"; + time_t start_time = 0; + int duration = 0; + + if (schedule != NULL) { + const cEvent *ev = schedule->GetPresentEvent(); + if (ev != NULL) { + if ((ev->Description() != NULL) && add_desc) + desc = cUrlEncode::doXmlSaveEncode(ev->Description()); + + if ((ev->Title() != NULL) && add_desc) + title = cUrlEncode::doXmlSaveEncode(ev->Title()); + start_time = ev->StartTime(); + duration = ev->Duration(); + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Event Info is Zero for Count= " + << count + << " Name= " << channel->Name() << endl; + } + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Schedule is Zero for Count= " + << count + << " Name= " << channel->Name() << endl; + } + + 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(), channel->Number(), start_time, duration, -1, -1, -1) == ERROR) + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + delete lock; + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); + +#endif + return OKAY; +} + +int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { + if (isHeadRequest()) + return OKAY; + +#ifndef STANDALONE + + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /epg.xml" + << endl; +#endif + vector avps; + mRequest->parseQueryLine(&avps); + string id = "S19.2E-1-1107-17500"; + string mode = ""; + bool add_desc = true; + + if (mRequest->getQueryAttributeValue(&avps, "id", id) == ERROR){ + *(mLog->log())<< DEBUGPREFIX + << " ERROR: id not found" + << endl; + delete mResponseMessage; + mResponseMessage = NULL; + sendError(400, "Bad Request", NULL, "no id in query line"); + return OKAY; + } + + if (mRequest->getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode == "nodesc") { + add_desc = false; + *(mLog->log())<< DEBUGPREFIX + << " **** Mode: No Description ****" + << endl; + } + } + + tChannelID chan_id = tChannelID::FromString (id.c_str()); + if ( chan_id == tChannelID::InvalidID) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Not possible to get the ChannelId from the string" + << endl; + delete mResponseMessage; + mResponseMessage = NULL; + sendError(400, "Bad Request", NULL, "Invalid Channel ID."); + return OKAY; + } + + cSchedulesLock * lock = new cSchedulesLock(false, 500); + const cSchedules *schedules = cSchedules::Schedules(*lock); + + const cSchedule *schedule = schedules->GetSchedule(chan_id); + if (schedule == NULL) { + *(mLog->log())<< DEBUGPREFIX + << "ERROR: Schedule is zero for guid= " << id + << endl; + delete mResponseMessage; + mResponseMessage = NULL; + sendError(500, "Internal Server Error", NULL, "Schedule is zero."); + return OKAY; + } + + time_t now = time(NULL); + const cEvent *ev = NULL; + for(cEvent* e = schedule->Events()->First(); e; e = schedule->Events()->Next(e)) { + if ( (e->StartTime() <= now) && (e->EndTime() > now)) { + ev = e; + + } else if (e->StartTime() > now + 3600) { + break; + } + } + + // const cEvent * ev = schedule->GetPresentEvent(); + + if (ev == NULL) { + *(mLog->log())<< DEBUGPREFIX + << "ERROR: Event is zero for guid= " << id + << endl; + delete mResponseMessage; + mResponseMessage = NULL; + sendError(500, "Internal Server Error", NULL, "Event is zero."); + return OKAY; + } + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + *mResponseMessage += hdr; + // Payload here + + hdr = ""; + if (ev->Title() != NULL) { + string title = cUrlEncode::doXmlSaveEncode(ev->Title()); + hdr += "" + title +"\n"; + } + else { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: title is zero for guid= " << id << endl; + hdr += "Empty\n"; + + delete mResponseMessage; + mResponseMessage = NULL; + sendError(500, "Internal Server Error", NULL, "Title is zero."); + return OKAY; + } + + hdr += "" + id + "\n"; + + *(mLog->log())<< DEBUGPREFIX + << " guid= " << id + << " title= " << ev->Title() + << " start= " << ev->StartTime() + << " end= " << ev->EndTime() + << " now= " << now + << endl; + if (add_desc) { + hdr += ""; + if (ev->Description() != NULL) { + hdr += cUrlEncode::doXmlSaveEncode(ev->Description()); + } + else { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: description is zero for guid= " << id << endl; + + delete mResponseMessage; + mResponseMessage = NULL; + sendError(500, "Internal Server Error", NULL, "Description is zero."); + return OKAY; + } + hdr += "\n"; + } + else { + hdr += "No Description Available\n"; + } + snprintf(f, sizeof(f), "%ld\n", ev->StartTime()); + hdr += f ; + + snprintf(f, sizeof(f), "%ld\n", ev->EndTime()); + hdr += f; + + snprintf(f, sizeof(f), "%d\n", ev->Duration()); + hdr += f; + *mResponseMessage += hdr; + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + + delete lock; + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); + +#endif + return OKAY; +} + + +int cResponseMemBlk::sendResumeXml () { + if (isHeadRequest()) + return OKAY; +#ifndef STANDALONE + + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + + char f[400]; + + cResumeEntry entry; + string id; + + parseResume(entry, id); + + vector avps; + mRequest->parseQueryLine(&avps); + string guid; + + if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY){ + // entry.mFilename = guid; + entry.mFilename = cUrlEncode::doUrlSaveDecode(guid); + + *(mLog->log() )<< DEBUGPREFIX + << " Found guid: " << guid + << " filename: " << entry.mFilename + << endl; + } + + + cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); + if (rec == NULL) { + //Error 404 + *(mLog->log())<< DEBUGPREFIX + << " sendResume: File Not Found filename= " << entry.mFilename << endl; + sendError(404, "Not Found", NULL, "Failed to find recording."); + return OKAY; + } + if (rec->IsNew()) { + *(mLog->log())<< DEBUGPREFIX + << " sendResume: file is new " << endl; + sendError(400, "Bad Request", NULL, "File is new."); + return OKAY; + } + cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); + + *(mLog->log())<< DEBUGPREFIX + << " resume request for " << entry.mFilename + << " resume= " << resume.Read() + << " (" << resume.Read() *1.0 / rec->FramesPerSecond() << "sec)" + << endl; + + *mResponseMessage += "\n"; + *mResponseMessage += ""; + snprintf(f, sizeof(f), "%.02f", resume.Read() *1.0 / rec->FramesPerSecond()); + *mResponseMessage += f; + *mResponseMessage += "\n"; + + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); +#endif + + return OKAY; +} + +int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { + if (isHeadRequest()) + return OKAY; +#ifndef STANDALONE + + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + // mRequest->mContentType = MEMBLOCK; + + mRequest->mConnState = SERVING; + + string own_ip = mRequest->getOwnIp(); + *(mLog->log()) << " OwnIP= " << own_ip << endl; + + vector avps; + mRequest->parseQueryLine(&avps); + string model = ""; + string link_ext = ""; + string type = ""; + string has_4_hd_str = ""; + bool has_4_hd = true; + string mode = ""; + bool add_desc = true; + + if (mRequest->getQueryAttributeValue(&avps, "model", model) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Model Parameter: " << model + << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "type", type) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Type Parameter: " << type + << endl; + if (type == "hls") { + if (model == "samsung") + link_ext = "/manifest-seg.m3u8|COMPONENT=HLS"; + else + link_ext = "/manifest-seg.m3u8"; + } + if (type == "has") { + if (model == "samsung") + link_ext = "/manifest-seg.mpd|COMPONENT=HAS"; + else + link_ext = "/manifest-seg.mpd"; + } + } + + if (mRequest->getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode == "nodesc") { + add_desc = false; + *(mLog->log())<< DEBUGPREFIX + << " Mode: No Description" + << endl; + } + } + + if (mRequest->getQueryAttributeValue(&avps, "has4hd", has_4_hd_str) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Has4Hd Parameter: " << has_4_hd_str + << endl; + if (has_4_hd_str == "false") + has_4_hd = false; + } + + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /recordings.xml" + << endl; +#endif + sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + hdr+= "VDR Recordings List\n"; + + *mResponseMessage += hdr; + + /* + if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", + "-", 0, 0) == ERROR) + return ERROR; + + if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", + "-", 0, 0) == ERROR) + return ERROR; + if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122:8000/hd2/mpeg/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS from own Server", + "-", 0, 0) == ERROR) + return ERROR; +*/ + //-------------------- + cRecordings* recordings = &Recordings; + char f[600]; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " recordings->Count()= " << recordings->Count() + << endl; +#endif + + // List of recording timer + time_t now = time(NULL); + + vector act_rec; + /*#ifndef DEBUG*/ + *(mLog->log())<< DEBUGPREFIX + << " checking avtive timer" + << endl; + /*#endif*/ + for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ + ti->Matches(); + + if (ti->HasFlags(tfRecording) ) { + *(mLog->log()) << DEBUGPREFIX + << " Active Timer: " << ti->File() + << " Start= " << ti->StartTime() + << " Duration= " << (ti->StopTime() - ti->StartTime()) + << endl; + act_rec.push_back(sTimerEntry(ti->File(), ti->StartTime(), (ti->StopTime() - ti->StartTime()))); + } + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " Found " << act_rec.size() + << " running timers" + << endl; +#endif + + int rec_dur = 0; + for (cRecording *recording = recordings->First(); recording; recording = recordings->Next(recording)) { + 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", own_ip.c_str(), mRequest->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", own_ip.c_str(), mRequest->mServerPort, + cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), link_ext.c_str()); + + string link = f; + string desc = "No description available"; + rec_dur = recording->LengthInSeconds(); + + string name = recording->Name(); + + for (uint x = 0; x < act_rec.size(); x++) { + if (act_rec[x].name == name) { + + /* *(mLog->log())<< DEBUGPREFIX + << " !!!!! Found active Recording !!! " + << endl; +*/ + rec_dur += (act_rec[x].startTime + act_rec[x].duration - now); + + + } + } // for + + if (recording->Info() != NULL) { + if ((recording->Info()->Description() != NULL) && add_desc) { + desc = cUrlEncode::doXmlSaveEncode(recording->Info()->Description()); + } + } + + if (writeXmlItem(cUrlEncode::doXmlSaveEncode(recording->Name()), link, "NA", desc, + cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), + -1, + recording->Start(), rec_dur, recording->FramesPerSecond(), + (recording->IsPesRecording() ? 0: 1), (recording->IsNew() ? 0: 1)) == ERROR) + // Better Internal Server Error + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + + +#endif + return OKAY; +} + + +int cResponseMemBlk::fillDataBlk() { + mBlkPos = 0; + + if (mError) { + *(mLog->log())<< DEBUGPREFIX << " mError == true -> Done" << endl; + mRequest->mConnState = TOCLOSE; + return ERROR; + } + + if (mResponseMessage == NULL) { + *(mLog->log())<< DEBUGPREFIX << " mResponseMessage == NULL -> Done" << endl; + mRequest->mConnState = TOCLOSE; + return ERROR; + } + + int rem_len = mResponseMessage->size() - mResponseMessagePos; + if (rem_len == 0) { + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " fillDataBlock: MEMBLOCK done" << endl; +#endif + delete mResponseMessage; + mResponseMessage = NULL; + mResponseMessagePos = 0; + mRequest->mConnState = TOCLOSE; + return ERROR; + } + + if (rem_len > MAXLEN) + rem_len = MAXLEN; + + string sub_msg = mResponseMessage->substr(mResponseMessagePos, rem_len); + mResponseMessagePos += rem_len; + mBlkLen = sub_msg.size(); + + memcpy(mBlkData, sub_msg.c_str(), rem_len); + + return OKAY; +} diff --git a/responsememblk.h b/responsememblk.h new file mode 100644 index 0000000..84491e2 --- /dev/null +++ b/responsememblk.h @@ -0,0 +1,92 @@ +/* + * responsememblk.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 __RESPONSE_MEMBLK_H__ +#define __RESPONSE_MEMBLK_H__ + +#include +#include +#include +#include +#include +#include "responsebase.h" + +using namespace std; + +struct sFileEntry { + string sName; + string sPath; + int sStart; + +sFileEntry(string n, string l, int s) : sName(n), sPath(l), sStart(s) { + }; +}; + + +class cResumeEntry; +class cHttpResource; + +// create the complete response for the file request +// info on range request should be provided. +class cResponseMemBlk : public cResponseBase { + public: + cResponseMemBlk(cHttpResource* ); + virtual ~cResponseMemBlk(); // same as sendFile + + int fillDataBlk(); + + int sendRecordingsXml (struct stat *statbuf); + 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); + int sendManifest (struct stat *statbuf, bool is_hls = true); + + void receiveClientInfo(); + + int receiveResume(); + int receiveDelRecReq(); + + 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); + + int parseResume(cResumeEntry &entry, string &id); + int parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf); + int sendDir(struct stat *statbuf); + 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); + uint64_t getVdrFileSize(); + + private: + string *mResponseMessage; + int mResponseMessagePos; + + timeval mResponseStart; +}; + +#endif diff --git a/responsevdrdir.c b/responsevdrdir.c new file mode 100644 index 0000000..50949d5 --- /dev/null +++ b/responsevdrdir.c @@ -0,0 +1,781 @@ +/* + * responsevdrdir.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 "responsevdrdir.h" +#include "httpresource.h" +#include "smarttvfactory.h" +#include +#include + +#ifndef STANDALONE +#include +//#include +//#include +//#include +//#include +#else +#include + +#endif + +//#define MAXLEN 4096 +#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd +#define OKAY 0 +#define ERROR (-1) +#define DEBUG + +struct sVdrFileEntry { + uint64_t sSize; + uint64_t sFirstOffset; + int sIdx; + + sVdrFileEntry () {}; + sVdrFileEntry (uint64_t s, uint64_t t, int i) + : sSize(s), sFirstOffset(t), sIdx(i) {}; +}; + + + +// 8 Byte Per Entry +struct tIndexPes { + uint32_t offset; + uint8_t type; // standalone + uint8_t number; // standalone + uint16_t reserved; + }; + + +// 8 Byte per entry +struct tIndexTs { + uint64_t offset:40; // up to 1TB per file (not using long long int here - must definitely be exactly 64 bit!) + int reserved:7; // reserved for future use + int independent:1; // marks frames that can be displayed by themselves (for trick modes) + uint16_t number:16; // up to 64K files per recording + }; + + +cResponseVdrDir::cResponseVdrDir(cHttpResource* req) : cResponseBase(req), mIsRecording(false), + mStreamToEnd(false), mRecProgress(0.0), mVdrIdx(0), mFile(NULL), mFileStructure() { +} + +cResponseVdrDir::~cResponseVdrDir() { + if (mFile != NULL) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: mFile still open. Closing now..." << endl; + fclose(mFile); + mFile = NULL; + } +} + + +bool cResponseVdrDir::isTimeRequest(struct stat *statbuf) { + +#ifndef STANDALONE + vector avps; + mRequest->parseQueryLine(&avps); + string time_str = ""; + string mode = ""; + float time = 0.0; + + if (mRequest->getQueryAttributeValue(&avps, "time", time_str) != OKAY){ + return false; + } + + if (mRequest->getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode.compare ("streamtoend") ==0) { + mStreamToEnd = true; + } + } + + if (mIsRecording) + mStreamToEnd = true; + + time = atof(time_str.c_str()); + *(mLog->log())<< DEBUGPREFIX + << " Found a Time Parameter: " << time + << " streamToEnd= " << ((mStreamToEnd) ? "true" : "false") + << endl; + + mRequest->mDir = mRequest->mPath; + cRecording *rec = Recordings.GetByName(mRequest->mPath.c_str()); + if (rec == NULL) { + *(mLog->log())<< DEBUGPREFIX + << " Error: Did not find recording= " << mRequest->mPath << endl; + sendError(404, "Not Found", NULL, "File not found."); + return true; + } + + double fps = rec->FramesPerSecond(); + double dur = rec->NumFrames() * fps; + bool is_pes = rec->IsPesRecording(); + if (dur < time) { + sendError(400, "Bad Request", NULL, "Time to large."); + return true; + } + + int start_frame = int(time * fps) -25; + + FILE* idx_file= NULL; + + if (is_pes){ + idx_file = fopen(((mRequest->mDir) +"/index.vdr").c_str(), "r"); + // sendError(400, "Bad Request", NULL, "PES not yet supported."); + // return true; + } + else { + idx_file = fopen(((mRequest->mDir) +"/index").c_str(), "r"); + } + + if (idx_file == NULL){ + *(mLog->log()) << DEBUGPREFIX + << " failed to open idx file = "<< (mRequest->mDir +"/index").c_str() + << endl; + sendError(404, "Not Found", NULL, "Failed to open Index file"); + return OKAY; + } + + int buffered_frames = 50; + char *index_buf = new char[8 *buffered_frames]; // 50 indexes + + *(mLog->log()) << DEBUGPREFIX + << " seeking to start_frame= " << start_frame + << " fps= " << fps << endl; + fseek(idx_file, start_frame * 8, SEEK_SET); + + int buffered_indexes = fread(index_buf, 8, (buffered_frames), idx_file); + + fclose(idx_file); + + if(buffered_indexes <= 0 ) { + *(mLog->log())<log()) << DEBUGPREFIX + << " Finding I-Frame now" << endl; + + + bool found_it = false; + int i = 0; + + uint32_t offset = 0; + int idx =0; + int type =0; + + for (i= 0; i < buffered_indexes; i++){ + if (is_pes) { + tIndexPes in_read_pes; + memcpy (&in_read_pes, &(index_buf[i*8]), 8); + type = in_read_pes.type == 1; + idx = in_read_pes.number; + offset = in_read_pes.offset; + } + else{ + tIndexTs in_read_ts; + memcpy (&in_read_ts, &(index_buf[i*8]), 8); + type = in_read_ts.independent; + idx = in_read_ts.number; + offset = in_read_ts.offset; + } + + *(mLog->log()) << DEBUGPREFIX + << " Frame= " << i + << " idx= "<< idx + << " offset= " << offset + << " type= " << type + << endl; + if (type){ + found_it = true; + break; + } + } + + if (!found_it) { + delete[] index_buf; + sendError(404, "Not Found", NULL, "Failed to read Index file"); + return OKAY; + } + + mVdrIdx = idx; + + *(mLog->log()) << DEBUGPREFIX + << " idx= "<< mVdrIdx + << " offset= " << offset + << endl; + + delete[] index_buf; + + char pathbuf[4096]; + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->mPath).c_str(), mVdrIdx); + + *(mLog->log()) << DEBUGPREFIX + << " Opening Path= " + << pathbuf << endl; + if (openFile(pathbuf) != OKAY) { + sendError(403, "Forbidden", NULL, "Access denied."); + return true; + } + + fseek(mFile, offset, SEEK_SET); + + if (mStreamToEnd) { + sendHeaders(200, "OK", NULL, "video/mpeg", -1, -1); + return true; + } + + uint64_t file_size = 0; + bool more_to_go = true; + int vdr_idx = mVdrIdx; + + while (more_to_go) { + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->mPath).c_str(), vdr_idx); + if (stat(pathbuf, statbuf) >= 0) { + *(mLog->log())<< " found for " << pathbuf << endl; + file_size += statbuf->st_size; + } + else { + more_to_go = false; + } + vdr_idx ++; + } + mRemLength = file_size - offset; + + *(mLog->log()) << DEBUGPREFIX + << " Done. Start Streaming " + << endl; + + if ((mRequest->rangeHdr).isRangeRequest) { + snprintf(pathbuf, sizeof(pathbuf), "Content-Range: bytes 0-%lld/%lld", (mRemLength -1), mRemLength); + sendHeaders(206, "Partial Content", pathbuf, "video/mpeg", mRemLength, statbuf->st_mtime); + } + else { + sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); + } + return true; +#else + return false; +#endif +} + +// Needed by createRecordings.xml and byCreateManifest +void cResponseVdrDir::checkRecording() { + // sets mIsRecording to true when the recording is still on-going + mIsRecording = false; + +#ifndef STANDALONE + time_t now = time(NULL); + + // cRecordings* recordings = mFactory->getRecordings(); + cRecordings* recordings = &Recordings; +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " GetByName(" <<(mRequest->mPath).c_str() << ")" + << endl; +#endif + cRecording* rec = recordings->GetByName((mRequest->mPath).c_str()); + if (rec != NULL) { + const cEvent *ev = rec->Info()->GetEvent(); + if (ev != NULL) { + if (now < ev->EndTime()) { + // still recording + mIsRecording = true; + + // mRecProgress * curFileSize = estimated File Size + mRecProgress = (ev->EndTime() - ev->StartTime()) *1.1 / (rec->NumFrames() / rec->FramesPerSecond()); + // mRecProgress = (ev->EndTime() - ev->StartTime()) *1.0/ (now - ev->StartTime()); +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " **** is still recording for mIsRecording= " + << mIsRecording + << " mRecProgress= " << mRecProgress << endl; +#endif + } + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " checking, whether recording is on-going" + << " now= " << now << " start= " << rec->Start() + << " curDur= " << rec->LengthInSeconds() + << endl; +#endif + } +#ifndef DEBUG + else { + *(mLog->log())<< DEBUGPREFIX + << " **** Recording Entry Not found **** " << endl; + } +#endif +#endif +} + + + +int cResponseVdrDir::sendVdrDir(struct stat *statbuf) { + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " *** sendVdrDir mPath= " << mRequest->mPath << endl; +#endif + + char pathbuf[4096]; + char f[400]; + int vdr_idx = 0; + uint64_t total_file_size = 0; + // int ret = OKAY; + string vdr_dir = mRequest->mPath; + vector file_sizes; + bool more_to_go = true; + + if (isHeadRequest()) + return OKAY; + + checkRecording(); + + mVdrIdx = 1; + mFileStructure = "%s/%03d.vdr"; + + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->mPath).c_str(), 1); + if (stat(pathbuf, statbuf) < 0) { + mFileStructure = "%s/%05d.ts"; +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " using dir format: " << mFileStructure.c_str() << endl; +#endif + } + + // The range request functions are activated, when a time header is detected + if (isTimeRequest(statbuf)) { + *(mLog->log())<< DEBUGPREFIX + << " isTimeRequest is true" + << endl; + return OKAY; + } + + // --- looup all vdr files in the dir --- + while (more_to_go) { + vdr_idx ++; + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->mPath).c_str(), vdr_idx); + if (stat(pathbuf, statbuf) >= 0) { +#ifndef DEBUG + *(mLog->log())<< " found for " << pathbuf << endl; +#endif + file_sizes.push_back(sVdrFileEntry(statbuf->st_size, total_file_size, vdr_idx)); + total_file_size += statbuf->st_size; + } + else { + more_to_go = false; + } + } + if (file_sizes.size() < 1) { + // There seems to be vdr video file in the directory + *(mLog->log())<< DEBUGPREFIX + << " No video file in the directory" + << endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " vdr filesize list " + << DEBUGHDR << endl; + for (uint i = 0; i < file_sizes.size(); i++) + *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; + *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; +#endif + + // total_file_size (on disk) + + // ---------------- file sizes --------------------- + + uint cur_idx = 0; + +#ifndef DEBUG + if (mIsRecording) { + snprintf(f, sizeof(f), " CurFileSize= %lld mRecProgress= %f ExtFileSize= %lld", total_file_size, mRecProgress, (long long int)(mRecProgress * total_file_size)); + *(mLog->log()) << DEBUGPREFIX + << endl << " isRecording: " << f + << endl; + } +#endif + + if (!(mRequest->rangeHdr).isRangeRequest) { + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->mPath).c_str(), file_sizes[cur_idx].sIdx); + + if (openFile(pathbuf) != OKAY) { + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + + mRemLength = total_file_size; + + sendHeaders(200, "OK", NULL, "video/mpeg", ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size ), statbuf->st_mtime); + } + else { // Range request + // idenify the first file +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << endl <<" --- Range Request Handling ---" + << DEBUGHDR << endl; +#endif + if (mIsRecording && ((mRequest->rangeHdr).begin > total_file_size)) { + *(mLog->log()) << DEBUGPREFIX + << " ERROR: Not yet available" << endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + cur_idx = file_sizes.size() -1; + for (uint i = 1; i < file_sizes.size(); i++) { + if ((mRequest->rangeHdr).begin < file_sizes[i].sFirstOffset ) { + cur_idx = i -1; + break; + } + } + +#ifndef DEBUG + *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " + << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin + << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; +#endif + + mVdrIdx = file_sizes[cur_idx].sIdx; + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->mPath).c_str(), file_sizes[cur_idx].sIdx); +#ifndef DEBUG + *(mLog->log())<< " file identified= " << pathbuf << endl; +#endif + if (openFile(pathbuf) != OKAY) { + *(mLog->log())<< "----- fopen failed dump ----------" << endl; + *(mLog->log())<< DEBUGPREFIX + << " vdr filesize list " + << endl; + for (uint i = 0; i < file_sizes.size(); i++) + *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; + *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; + + *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " + << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << (mRequest->rangeHdr).begin + << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; + *(mLog->log())<< "---------------" << endl; + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + mRequest->mDir = mRequest->mPath; + mRequest->mPath = pathbuf; +#ifndef DEBUG + *(mLog->log())<< " Seeking into file= " << (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset) + << " cur_idx= " << cur_idx + << " file_sizes[cur_idx].sFirstOffset= " << file_sizes[cur_idx].sFirstOffset + << endl; +#endif + fseek(mFile, ((mRequest->rangeHdr).begin - file_sizes[cur_idx].sFirstOffset), SEEK_SET); + if ((mRequest->rangeHdr).end == 0) + (mRequest->rangeHdr).end = ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size); + + mRemLength = ((mRequest->rangeHdr).end-(mRequest->rangeHdr).begin); + + snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", (mRequest->rangeHdr).begin, ((mRequest->rangeHdr).end -1), + ((mIsRecording) ? (long long int)(mRecProgress * total_file_size): total_file_size)); + + sendHeaders(206, "Partial Content", f, "video/mpeg", ((mRequest->rangeHdr).end-(mRequest->rangeHdr).begin), statbuf->st_mtime); + } + +#ifndef DEBUG + *(mLog->log())<< " ***** Yes, vdr dir found ***** mPath= " << mRequest->mPath << endl; +#endif + + return OKAY; // handleRead() done +} + + + + +//----------------------------------------------------------------- +// ----- Send Segment ----- +//----------------------------------------------------------------- + +int cResponseVdrDir::sendMediaSegment (struct stat *statbuf) { + if (isHeadRequest()) + return OKAY; + +#ifndef STANDALONE + + *(mLog->log()) << DEBUGPREFIX << " sendMediaSegment " << mRequest->mPath << endl; + size_t pos = (mRequest->mPath).find_last_of ("/"); + + mRequest->mDir = (mRequest->mPath).substr(0, pos); + string seg_name = (mRequest->mPath).substr(pos+1); + int seg_number; + + int seg_dur = mRequest->mFactory->getConfig()->getSegmentDuration(); + int frames_per_seg = 0; + + sscanf(seg_name.c_str(), "%d-seg.ts", &seg_number); + + //FIXME: Do some consistency checks on the seg_number + //* Does the segment exist + + cRecordings* recordings = &Recordings; + cRecording* rec = recordings->GetByName((mRequest->mDir).c_str()); + if (rec != NULL) { + frames_per_seg = seg_dur * rec->FramesPerSecond(); + } + else { + *(mLog->log()) << DEBUGPREFIX << " ERROR: Ooops, rec is NULL, assuming 25 fps " << endl; + frames_per_seg = seg_dur * 25; + } + //FIXME: HD Fix + // frames_per_seg = seg_dur * 25; + + *(mLog->log()) << DEBUGPREFIX + << " mDir= " << mRequest->mDir + << " seg_name= " << seg_name + << " seg_number= "<< seg_number + << " fps= " << rec->FramesPerSecond() + << " frames_per_seg= " << frames_per_seg + << endl; + int start_frame_count = (seg_number -1) * frames_per_seg; + + FILE* idx_file = fopen((mRequest->mDir +"/index").c_str(), "r"); + if (idx_file == NULL){ + *(mLog->log()) << DEBUGPREFIX + << " failed to open idx file = "<< (mRequest->mDir +"/index").c_str() + << endl; + sendError(404, "Not Found", NULL, "Failed to open Index file"); + return OKAY; + } + + char *index_buf = new char[(frames_per_seg +3) *8]; + + // fseek to start_frame_count * sizeof(in_read) + fseek(idx_file, start_frame_count * 8, SEEK_SET); + + // read to (seg_number * frames_per_seg +1) * sizeof(in_read) + // buffersize is frames_per_seg * seg_number * sizeof(in_read) + int buffered_indexes = fread(index_buf, 8, (frames_per_seg +2), idx_file); + + fclose(idx_file); + + if(buffered_indexes <= 0 ) { + *(mLog->log())<mDir).c_str(), mVdrIdx); + (mRequest->mPath) = seg_fn; + + /* + * Now we determine the end of the segment + */ + memcpy (&in_read_ts, &(index_buf[(frames_per_seg)*8]), 8); + + int end_idx = in_read_ts.number; + int end_offset = in_read_ts.offset; + + /*#ifndef DEBUG*/ + *(mLog->log()) << DEBUGPREFIX + << " GenSegment: start (no/idx)= " << start_idx << " / " << start_offset + << " to (no/idx)= " << end_idx << " / " << end_offset + << endl; + /*#endif*/ + + delete[] index_buf; + + int rem_len = 0; + bool error = false; + if (start_idx == end_idx){ + mRemLength = (end_offset - start_offset); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " start_idx == end_idx: mRemLength= " <log()) << DEBUGPREFIX + << " start_idx < end_idx " + << endl; +#endif + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), (mRequest->mDir).c_str(), mVdrIdx); + if (stat(seg_fn, statbuf) < 0) { + *(mLog->log()) << DEBUGPREFIX + << " file= " <st_size - start_offset; // remaining length of the first segment + + // loop over all idx files between start_idx and end_idx + for (int idx = (start_idx+1); idx < end_idx; idx ++) { + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), (mRequest->mDir).c_str(), idx); + if (stat(seg_fn, statbuf) < 0) { + *(mLog->log()) << DEBUGPREFIX + << " for loop file= " <st_size; // remaining length of the first segment + } + rem_len += end_offset; // + mRemLength = rem_len; + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), (mRequest->mDir).c_str(), mVdrIdx); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " start_idx= " << start_idx << " != end_idx= "<< end_idx <<": mRemLength= " <log())<< DEBUGPREFIX << " Failed to open file= " << seg_fn + << " mRemLength= " << mRemLength<< endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + fseek(mFile, start_offset, SEEK_SET); + + sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); + +#endif + return OKAY; +} + +int cResponseVdrDir::openFile(const char *name) { + mFile = fopen(name, "r"); + if (!mFile) { + *(mLog->log())<< DEBUGPREFIX + << " fopen failed pathbuf= " << name + << endl; + // sendError(403, "Forbidden", NULL, "Access denied."); + return ERROR; + } + return OKAY; +} + + +int cResponseVdrDir::fillDataBlk() { + char pathbuf[4096]; + + if (mError) + return ERROR; + + mBlkPos = 0; + int to_read = 0; + + + // Range requests are assumed to be all open + if (mFile == NULL) { + *(mLog->log()) << DEBUGPREFIX << " no open file anymore " + << "--> Done " << endl; + return ERROR; + } + if ((mRemLength == 0) && !mStreamToEnd){ +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " mRemLength is zero " + << "--> Done " << endl; +#endif + fclose(mFile); + mFile = NULL; + return ERROR; + } + if (!mStreamToEnd) + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + else + to_read = MAXLEN; + + mBlkLen = fread(mBlkData, 1, to_read, mFile); + if (!mStreamToEnd) + mRemLength -= mBlkLen; + + if ((mRemLength == 0) && (!mStreamToEnd)) { +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " last Block read " + << "--> Almost Done " << endl; +#endif + return OKAY; + } + + // if (mBlkLen != MAXLEN) { // thlo verify + if (mBlkLen != to_read) { + fclose(mFile); + mFile = NULL; + mVdrIdx ++; + + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->mDir).c_str(), mVdrIdx); + mRequest->mPath = pathbuf; + + if (openFile(pathbuf) != OKAY) { + *(mLog->log())<< DEBUGPREFIX << " Failed to open file= " << pathbuf << " mRemLength= " << mRemLength<< endl; + mFile = NULL; + if (mBlkLen == 0) { + *(mLog->log()) << DEBUGPREFIX << " mBlkLen is zero --> Done " << endl; + return ERROR; + } + else + *(mLog->log()) << DEBUGPREFIX << " Still data to send mBlkLen= " << mBlkLen <<" --> continue " << endl; + return OKAY; + } // Error: Open next file failed + + if (mBlkLen == 0) { + if (!mStreamToEnd) + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + else + to_read = MAXLEN; + mBlkLen = fread(mBlkData, 1, to_read, mFile); + if (!mStreamToEnd) + mRemLength -= mBlkLen ; + } + } + return OKAY; +} diff --git a/responsevdrdir.h b/responsevdrdir.h new file mode 100644 index 0000000..9fba86f --- /dev/null +++ b/responsevdrdir.h @@ -0,0 +1,64 @@ +/* + * responsevdrdir.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 __HTTPRESPONSE_VDRDIR_H__ +#define __HTTPRESPONSE_VDRDIR_H__ + +#include +#include +#include +#include "responsebase.h" + +using namespace std; + +//class cHttpResource; + +class cResponseVdrDir : public cResponseBase{ + public: + cResponseVdrDir(cHttpResource*); + virtual ~cResponseVdrDir(); + + int fillDataBlk(); + + int sendVdrDir(struct stat *statbuf); + int sendMediaSegment (struct stat *statbuf); + protected: + + // cHttpResource* mRequest; + + //range + bool mIsRecording; + bool mStreamToEnd; + float mRecProgress; + + int mVdrIdx; + FILE* mFile; + string mFileStructure; + + bool isTimeRequest(struct stat *statbuf); + void checkRecording() ; + + int openFile(const char *name); + +}; + +#endif -- cgit v1.2.3 From 5a8bf27d89d82d0bb1ea0939c74c1f122f6f6ae3 Mon Sep 17 00:00:00 2001 From: thlo Date: Mon, 29 Apr 2013 20:26:53 +0200 Subject: Added Delete YT Urls, Get notification for starting and finishing recordings, resume fix, query recording info of a single item. --- httpclient.c | 30 ++++++ httpclient.h | 22 +++++ httpresource.c | 11 ++- mngurls.c | 33 ++++++- mngurls.h | 3 +- responsememblk.c | 292 +++++++++++++++++++++++++++++++++++-------------------- responsememblk.h | 3 + smarttvfactory.c | 125 +++++++++++++++++++++++- smarttvfactory.h | 22 ++++- 9 files changed, 417 insertions(+), 124 deletions(-) diff --git a/httpclient.c b/httpclient.c index cdf7645..f1dd793 100644 --- a/httpclient.c +++ b/httpclient.c @@ -359,3 +359,33 @@ string cHttpYtPushClient::getMsgBody(int) { return "{\"type\":\"YT\",payload:{\"id\":\"" + mVideoId +"\", \"store\":"+((mStore)?"true":"false")+"}}"; } +//------------------------------ +//----- cHttpCfgPushClient ------ +//------------------------------ + +cHttpCfgPushClient::cHttpCfgPushClient(int f, int id, int port, SmartTvServer* fac, string peer) : cHttpClientBase(f, id, port, fac, peer) { + + createRequestMessage(""); +} + +cHttpCfgPushClient::~cHttpCfgPushClient() { +} + +string cHttpCfgPushClient::getMsgBody(int) { + return "{\"type\":\"CFGADD\",payload:{\"serverAddr\":\"" + mPeer +"\"" +"}}"; +} + +//------------------------------ +//----- cHttpInfoClient ------ +//------------------------------ +cHttpInfoClient::cHttpInfoClient(int f, int id, int port, SmartTvServer* fac, string peer, string bdy) : cHttpClientBase(f, id, port, fac, peer), mBody(bdy) { + + createRequestMessage(""); +} + +cHttpInfoClient::~cHttpInfoClient() { +} + +string cHttpInfoClient::getMsgBody(int) { + return "{\"type\":\"INFO\",payload:" + mBody +"}";; +} diff --git a/httpclient.h b/httpclient.h index b5a39ea..000eaa8 100644 --- a/httpclient.h +++ b/httpclient.h @@ -87,4 +87,26 @@ class cHttpYtPushClient : public cHttpClientBase { }; +class cHttpCfgPushClient : public cHttpClientBase { + public: + cHttpCfgPushClient(int, int, int, SmartTvServer*, string peer); + virtual ~cHttpCfgPushClient(); + + protected: + string getMsgBody(int ); + + string mServerAddress; +}; + +class cHttpInfoClient : public cHttpClientBase { + public: + cHttpInfoClient(int, int, int, SmartTvServer*, string peer, string body); + virtual ~cHttpInfoClient(); + + protected: + string getMsgBody(int ); + + string mBody; +}; + #endif diff --git a/httpresource.c b/httpresource.c index e20bf00..f718816 100755 --- a/httpresource.c +++ b/httpresource.c @@ -92,10 +92,10 @@ cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): c setNonBlocking(); // mBlkData = new char[MAXLEN]; - //#ifndef DEBUG +#ifndef DEBUG *(mLog->log()) << DEBUGPREFIX << " cHttpResource created" << endl; - //#endif +#endif } @@ -580,6 +580,13 @@ int cHttpResource::handlePost() { return OKAY; } + if (mPath.compare("/deleteYtUrl") == 0) { + + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveDelYtUrl(); + return OKAY; + } + if (mPath.compare("/deleteRecording.xml") == 0) { mResponse = new cResponseMemBlk(this); diff --git a/mngurls.c b/mngurls.c index dabcfe7..7e7d886 100644 --- a/mngurls.c +++ b/mngurls.c @@ -23,11 +23,12 @@ #include "mngurls.h" -cManageUrls::cManageUrls(string dir): mLog(), mFile(NULL), mEntries() { +cManageUrls::cManageUrls(string dir): mLog(), mFilename(), mFile(NULL), mEntries() { mLog = Log::getInstance(); loadEntries(dir); - mFile = new ofstream((dir +"/urls.txt").c_str(), ios::out | ios::app); + mFilename = dir +"/urls.txt"; + mFile = new ofstream(mFilename.c_str(), ios::out | ios::app); mFile->seekp(ios_base::end); }; @@ -63,22 +64,46 @@ void cManageUrls::appendEntry(string type, string url) { } -void cManageUrls::deleteEntry(string type, string url) { +bool cManageUrls::deleteEntry(string type, string url) { *(mLog->log()) << " cManageUrls::deleteEntry: type= " << type << "guid= " << url << endl; bool found = false; if (type.compare("YT") !=0) { - return; + *(mLog->log()) << " cManageUrls::deleteEntry: Not a YT Url " << endl; + return false; } + for (int i = 0; i < mEntries.size(); i ++) { if (url.compare(mEntries[i]->mEntry) == 0) { // delete the entry here *(mLog->log()) << " cManageUrls::deleteEntry ... " << endl; + mEntries.erase(mEntries.begin() +i); found = true; break; } } + if (found) { + *(mLog->log()) << " cManageUrls::deleteEntry - rewriting urls.txt file ... " << endl; + if (mFile != NULL) { + mFile->close(); + delete mFile; + } + mFile = new ofstream(mFilename.c_str(), ios::out); + + for (int i = 0; i < mEntries.size(); i ++) { + appendToFile(mEntries[i]->mType+"|"+mEntries[i]->mEntry); + } + + // close the file + mFile->close(); + delete mFile; + + // open for append + mFile = new ofstream(mFilename.c_str(), ios::out | ios::app); + mFile->seekp(ios_base::end); + } + return found; } diff --git a/mngurls.h b/mngurls.h index d500f61..5c58d3e 100644 --- a/mngurls.h +++ b/mngurls.h @@ -41,7 +41,7 @@ class cManageUrls { virtual ~cManageUrls(); void appendEntry (string type, string guid); - void deleteEntry(string type, string guid); + bool deleteEntry(string type, string guid); size_t size(); sUrlEntry* getEntry(int index); @@ -52,6 +52,7 @@ class cManageUrls { Log* mLog; + string mFilename; ofstream* mFile; vector mEntries; diff --git a/responsememblk.c b/responsememblk.c index a695340..843894d 100644 --- a/responsememblk.c +++ b/responsememblk.c @@ -143,6 +143,63 @@ int cResponseMemBlk::receiveYtUrl() { } +int cResponseMemBlk::receiveDelYtUrl() { + if (isHeadRequest()) + return OKAY; + + vector avps; + mRequest->parseQueryLine(&avps); + string id = ""; + + if (mRequest->getQueryAttributeValue(&avps, "guid", id) == ERROR){ + *(mLog->log())<< DEBUGPREFIX + << " ERROR in cResponseMemBlk::receiveDelYtUrl: guid not found in query." + << endl; + sendError(400, "Bad Request", NULL, "No guid in query line"); + return OKAY; + } + + if (mRequest->mFactory->deleteYtVideoId(id)) { + sendHeaders(200, "OK", NULL, NULL, -1, -1); + } + else { + sendError(400, "Bad Request.", NULL, "Entry not found. Deletion failed!"); + } + + return OKAY; + +} + +int cResponseMemBlk::receiveCfgServerAddrs() { + vector avps; + mRequest->parseQueryLine(&avps); + string addr; + + if (isHeadRequest()) + return OKAY; + + if (mRequest->getQueryAttributeValue(&avps, "addr", addr) == OKAY){ + if (addr.compare ("") == 0) { + *(mLog->log())<< DEBUGPREFIX + << " receiveCfgServerAddrs: no server address" + << endl; + + sendHeaders(400, "Bad Request", NULL, "TV address field empty", 0, -1); + return OKAY; + } + + // mRequest->mFactory->pushYtVideoId(line, store); + + sendHeaders(200, "OK", NULL, NULL, 0, -1); + return OKAY; + + } + + sendError(400, "Bad Request", NULL, "Mandatory TV address attribute not present."); + return OKAY; + +} + void cResponseMemBlk::receiveClientInfo() { vector avps; mRequest->parseQueryLine(&avps); @@ -176,10 +233,11 @@ void cResponseMemBlk::receiveClientInfo() { mRequest->mFactory->updateTvClient(ip, mac, time(NULL)); } sendHeaders(200, "OK", NULL, NULL, 0, -1); +#ifndef DEBUG *(mLog->log())<< DEBUGPREFIX << " receiveClientInfo -done " << endl; - +#endif } @@ -248,7 +306,8 @@ int cResponseMemBlk::receiveResume() { } *(mLog->log())<< DEBUGPREFIX - << " Resume: id= " << dev_id + << " Resume:" + // << " id= " << dev_id << " resume= " << entry << endl; vector avps; @@ -259,9 +318,10 @@ int cResponseMemBlk::receiveResume() { if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY){ entry.mFilename = cUrlEncode::doUrlSaveDecode(guid); - *(mLog->log())<< DEBUGPREFIX + /* *(mLog->log())<< DEBUGPREFIX << " Found a id Parameter: " << guid << endl; +*/ } if (mRequest->getQueryAttributeValue(&avps, "resume", resume_str) == OKAY){ entry.mResume = atof(resume_str.c_str()); @@ -273,8 +333,13 @@ int cResponseMemBlk::receiveResume() { #ifndef STANDALONE cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); if (rec == NULL) { - //Error 404 - sendError(404, "Not Found", NULL, "Failed to find recording."); + //Error 400 + *(mLog->log())<< DEBUGPREFIX + << " ERROR in receiveResume: recording not found - filename= " << entry.mFilename + << " resume= " << entry.mResume + << endl; + + sendError(400, "Bad Request", NULL, "Failed to find the recording."); return OKAY; } @@ -287,12 +352,83 @@ int cResponseMemBlk::receiveResume() { << endl; resume.Save(int(entry.mResume * rec->FramesPerSecond() )); + rec->ResetResume(); #endif sendHeaders(200, "OK", NULL, NULL, -1, -1); return OKAY; } + +int cResponseMemBlk::sendResumeXml () { + if (isHeadRequest()) + return OKAY; +#ifndef STANDALONE + + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + mRequest->mConnState = SERVING; + + char f[400]; + + cResumeEntry entry; + string id; + + parseResume(entry, id); + + vector avps; + mRequest->parseQueryLine(&avps); + string guid; + + if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY){ + entry.mFilename = cUrlEncode::doUrlSaveDecode(guid); + + *(mLog->log() )<< DEBUGPREFIX + << " Found guid: " << guid + << " filename: " << entry.mFilename + << endl; + } + + + cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); + if (rec == NULL) { + //Error 404 + *(mLog->log())<< DEBUGPREFIX + << " ERROR in sendResume: recording not found - filename= " << entry.mFilename << endl; + sendError(400, "Bad Request", NULL, "Failed to find the recording."); + return OKAY; + } + if (rec->IsNew()) { + *(mLog->log())<< DEBUGPREFIX + << " sendResume: file is new " << endl; + sendError(400, "Bad Request", NULL, "File is new."); + return OKAY; + } + cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); + + *(mLog->log())<< DEBUGPREFIX + << " resume request for " << entry.mFilename + << " resume= " << resume.Read() + << " (" << resume.Read() *1.0 / rec->FramesPerSecond() << "sec)" + << endl; + + *mResponseMessage += "\n"; + *mResponseMessage += ""; + snprintf(f, sizeof(f), "%.02f", resume.Read() *1.0 / rec->FramesPerSecond()); + *mResponseMessage += f; + *mResponseMessage += "\n"; + + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); +#endif + + return OKAY; +} + + + int cResponseMemBlk::receiveDelRecReq() { if (isHeadRequest()) return OKAY; @@ -1387,74 +1523,6 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { } -int cResponseMemBlk::sendResumeXml () { - if (isHeadRequest()) - return OKAY; -#ifndef STANDALONE - - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - // mRequest->mContentType = MEMBLOCK; - - mRequest->mConnState = SERVING; - - char f[400]; - - cResumeEntry entry; - string id; - - parseResume(entry, id); - - vector avps; - mRequest->parseQueryLine(&avps); - string guid; - - if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY){ - // entry.mFilename = guid; - entry.mFilename = cUrlEncode::doUrlSaveDecode(guid); - - *(mLog->log() )<< DEBUGPREFIX - << " Found guid: " << guid - << " filename: " << entry.mFilename - << endl; - } - - - cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); - if (rec == NULL) { - //Error 404 - *(mLog->log())<< DEBUGPREFIX - << " sendResume: File Not Found filename= " << entry.mFilename << endl; - sendError(404, "Not Found", NULL, "Failed to find recording."); - return OKAY; - } - if (rec->IsNew()) { - *(mLog->log())<< DEBUGPREFIX - << " sendResume: file is new " << endl; - sendError(400, "Bad Request", NULL, "File is new."); - return OKAY; - } - cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); - - *(mLog->log())<< DEBUGPREFIX - << " resume request for " << entry.mFilename - << " resume= " << resume.Read() - << " (" << resume.Read() *1.0 / rec->FramesPerSecond() << "sec)" - << endl; - - *mResponseMessage += "\n"; - *mResponseMessage += ""; - snprintf(f, sizeof(f), "%.02f", resume.Read() *1.0 / rec->FramesPerSecond()); - *mResponseMessage += f; - *mResponseMessage += "\n"; - - - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); -#endif - - return OKAY; -} int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { if (isHeadRequest()) @@ -1480,6 +1548,8 @@ int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { bool has_4_hd = true; string mode = ""; bool add_desc = true; + string guid = ""; + bool single_item = false; if (mRequest->getQueryAttributeValue(&avps, "model", model) == OKAY){ *(mLog->log())<< DEBUGPREFIX @@ -1487,6 +1557,13 @@ int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { << endl; } + if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a guid Parameter: " << guid + << endl; + single_item = true; + } + if (mRequest->getQueryAttributeValue(&avps, "type", type) == OKAY){ *(mLog->log())<< DEBUGPREFIX << " Found a Type Parameter: " << type @@ -1528,30 +1605,8 @@ int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { << " generating /recordings.xml" << endl; #endif - sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - hdr+= "VDR Recordings List\n"; - - *mResponseMessage += hdr; - - /* - if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", - "-", 0, 0) == ERROR) - return ERROR; - if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", - "-", 0, 0) == ERROR) - return ERROR; - if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122:8000/hd2/mpeg/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS from own Server", - "-", 0, 0) == ERROR) - return ERROR; -*/ //-------------------- - cRecordings* recordings = &Recordings; char f[600]; #ifndef DEBUG @@ -1589,12 +1644,31 @@ int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { << endl; #endif + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + hdr+= "VDR Recordings List\n"; + + *mResponseMessage += hdr; + + int item_count = 0; int rec_dur = 0; - for (cRecording *recording = recordings->First(); recording; recording = recordings->Next(recording)) { + cRecording *recording = NULL; + if (single_item) { + recording = Recordings.GetByName(guid.c_str()); + if (recording == NULL) + *(mLog->log())<< DEBUGPREFIX << " WARNING in sendRecordingsXml: recording " << guid << " not found" << endl; + } + else { + recording = Recordings.First(); + } + // for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { + while (recording != NULL) { 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", own_ip.c_str(), mRequest->mServerPort, cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str()); else @@ -1611,13 +1685,8 @@ int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { for (uint x = 0; x < act_rec.size(); x++) { if (act_rec[x].name == name) { - /* *(mLog->log())<< DEBUGPREFIX - << " !!!!! Found active Recording !!! " - << endl; -*/ + // *(mLog->log())<< DEBUGPREFIX << " !!!!! Found active Recording !!! " << endl; rec_dur += (act_rec[x].startTime + act_rec[x].duration - now); - - } } // for @@ -1631,10 +1700,15 @@ int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), -1, recording->Start(), rec_dur, recording->FramesPerSecond(), - (recording->IsPesRecording() ? 0: 1), (recording->IsNew() ? 0: 1)) == ERROR) - // Better Internal Server Error - return ERROR; - + (recording->IsPesRecording() ? 0: 1), (recording->IsNew() ? 0: 1)) == ERROR) { + *mResponseMessage = ""; + sendError(500, "Internal Server Error", NULL, "writeXMLItem returned an error"); + return OKAY; + } + item_count ++; + // if (!single_item) + // recording = Recordings.Next(recording); + recording = (!single_item) ? Recordings.Next(recording) : NULL; } hdr = "\n"; @@ -1642,8 +1716,10 @@ int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { *mResponseMessage += hdr; + *(mLog->log())<< DEBUGPREFIX << " Recording Count= " <size(), statbuf->st_mtime); return OKAY; } @@ -1658,7 +1734,9 @@ int cResponseMemBlk::fillDataBlk() { } if (mResponseMessage == NULL) { +#ifndef DEBUG *(mLog->log())<< DEBUGPREFIX << " mResponseMessage == NULL -> Done" << endl; +#endif mRequest->mConnState = TOCLOSE; return ERROR; } diff --git a/responsememblk.h b/responsememblk.h index 84491e2..091d9ae 100644 --- a/responsememblk.h +++ b/responsememblk.h @@ -72,6 +72,9 @@ class cResponseMemBlk : public cResponseBase { int receiveDelRecReq(); int receiveYtUrl(); + int receiveDelYtUrl(); + + int receiveCfgServerAddrs(); 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/smarttvfactory.c b/smarttvfactory.c index aeee438..f721658 100755 --- a/smarttvfactory.c +++ b/smarttvfactory.c @@ -45,11 +45,12 @@ #include #include - +#include #include "smarttvfactory.h" #include "httpresource.h" #include "httpclient.h" +#include "url.h" #ifndef STANDALONE #define PORT 8000 @@ -72,9 +73,9 @@ void SmartTvServerStartThread(void* arg) { pthread_exit(NULL); } -SmartTvServer::SmartTvServer(): mRequestCount(0), isInited(false), serverPort(PORT), mServerFd(-1), +SmartTvServer::SmartTvServer(): cStatus(), mRequestCount(0), isInited(false), serverPort(PORT), mServerFd(-1), mSegmentDuration(10), mHasMinBufferTime(40), mLiveChannels(20), - clientList(), mConTvClients(), mActiveSessions(0), mHttpClients(0), mConfig(NULL), mMaxFd(0), + clientList(), mConTvClients(), mActiveSessions(0), mHttpClientId(0), mConfig(NULL), mMaxFd(0), mManagedUrls(NULL){ } @@ -85,6 +86,101 @@ SmartTvServer::~SmartTvServer() { delete mConfig; } +// Status methods +void SmartTvServer::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On) { +#ifndef DEBUG + *(mLog.log()) << "SmartTvServer::Recording: Recording" + << ((On) ? " started" : " stopped") + << endl; +#endif + stringstream msg; + string name ; + + if (Name == NULL) { + if (FileName == NULL) { + *(mLog.log()) << "WARNING in SmartTvServer::Recording: Name and FileName are NULL. Return. " << endl; + return; + } + cRecording* rec = Recordings.GetByName(FileName); + if (rec == NULL) { + *(mLog.log()) << "WARNING in SmartTvServer::Recording: No Recording Entry found. Return. " << endl; + return; + } + name = rec->Name(); + } + else + name = Name; + + string method = (On) ? "RECSTART" : "RECSTOP"; + string guid = (FileName == NULL) ? "" : cUrlEncode::doUrlSaveEncode(FileName); + + msg << "{\"type\":\""+method+"\",\"name\":\"" << name << "\",\"guid\":\""+guid+"\"}"; + + *(mLog.log()) << "SmartTvServer::Recording: Recording" + << ((On) ? " started" : " stopped") + << " Msg= " << msg.str() + << endl; + + for (uint i = 0; i < mConTvClients.size(); i ++) { + if ((mConTvClients[i]->ip).compare("") != 0) { + + int cfd= connectToClient(mConTvClients[i]->ip); + if (cfd < 0) + continue; + addHttpResource(cfd, new cHttpInfoClient(cfd, mHttpClientId, serverPort, this, mConTvClients[i]->ip, msg.str())); + } + } +}; + +void SmartTvServer::TimerChange(const cTimer *Timer, eTimerChange Change) { + #ifndef DEBUG + *(mLog.log()) << "SmartTvServer::TimerChange" + << endl; +#endif + + stringstream msg; + string method = ""; + switch (Change) { + case tcMod: + method = "TCMOD"; + break; + case tcAdd: + method = "TCADD"; + break; + case tcDel: + method = "TCDEL"; + break; + } + + + if (Timer == NULL) { + *(mLog.log()) << "WARNING in SmartTvServer::TimerChange - Timer is NULL. Method= " << method << ", returning" << endl; + return; + } + + string name = Timer->File(); + if (Timer->Event() != NULL) { + if (Timer->Event()->Title() != NULL) + name = Timer->Event()->Title(); + } + + + msg << "{\"type\":\"" << method << "\",\"name\":\"" << name << "\", \"start\":" << Timer->Start() <<"}"; + +#ifndef DEBUG + *(mLog.log()) << "SmartTvServer::TimerChange: Msg= " << msg.str() << endl; +#endif + + for (uint i = 0; i < mConTvClients.size(); i ++) { + if ((mConTvClients[i]->ip).compare("") != 0) { + int cfd= connectToClient(mConTvClients[i]->ip); + if (cfd < 0) + continue; + addHttpResource(cfd, new cHttpInfoClient(cfd, mHttpClientId, serverPort, this, mConTvClients[i]->ip, msg.str())); + } + } +} + void SmartTvServer::cleanUp() { // close listening ports for (uint idx= 0; idx < clientList.size(); idx++) { @@ -166,6 +262,15 @@ void SmartTvServer::storeYtVideoId(string guid) { mManagedUrls->appendEntry("YT", guid); } +bool SmartTvServer::deleteYtVideoId(string guid) { + if (mManagedUrls == NULL) + mManagedUrls = new cManageUrls(mConfigDir); + + return mManagedUrls->deleteEntry("YT", guid); +} + + + void SmartTvServer::pushYtVideoId(string vid_id, bool store) { for (uint i = 0; i < mConTvClients.size(); i ++) { if ((mConTvClients[i]->ip).compare("") != 0) @@ -214,7 +319,7 @@ void SmartTvServer::addHttpResource(int rfd, cHttpResourceBase* resource) { FD_SET(rfd, &mWriteState); clientList[rfd] = resource; - mHttpClients++; + mHttpClientId++; if (rfd > mMaxFd) { mMaxFd = rfd; } @@ -233,8 +338,18 @@ void SmartTvServer::pushYtVideoIdToClient(string vid_id, string peer, bool store int cfd= connectToClient(peer); if (cfd < 0) return; - addHttpResource(cfd, new cHttpYtPushClient(cfd, mHttpClients, serverPort, this, peer, vid_id, store)); + addHttpResource(cfd, new cHttpYtPushClient(cfd, mHttpClientId, serverPort, this, peer, vid_id, store)); + +} +void SmartTvServer::pushCfgServerAddressToTv( string tv_addr) { + *(mLog.log()) << " SmartTvServer::pushCfgServerAddressToTv TV= " << tv_addr + << endl; + + int cfd= connectToClient(tv_addr); + if (cfd < 0) + return; + addHttpResource(cfd, new cHttpCfgPushClient(cfd, mHttpClientId, serverPort, this, tv_addr)); } int SmartTvServer::runAsThread() { diff --git a/smarttvfactory.h b/smarttvfactory.h index 261f2ea..02c03ec 100644 --- a/smarttvfactory.h +++ b/smarttvfactory.h @@ -38,12 +38,17 @@ #ifndef STANDALONE #include +#include + +#else +class cStatus { +}; #endif using namespace std; -#define PLG_VERSION "0.9.7" -#define SERVER "SmartTvWeb/0.9.7" +#define PLG_VERSION "0.9.8-pre" +#define SERVER "SmartTvWeb/0.9.8-pre" struct sClientEntry { string mac; @@ -52,7 +57,8 @@ struct sClientEntry { sClientEntry(string m, string i, time_t t ): mac(m), ip(i), lastKeepAlive(t) {}; }; -class SmartTvServer { +class SmartTvServer : public cStatus { + public: SmartTvServer(); virtual ~SmartTvServer(); @@ -74,17 +80,23 @@ class SmartTvServer { void updateTvClient(string ip, string mac, time_t upd); void removeTvClient(string ip, string mac, time_t upd); - void storeYtVideoId(string); + void storeYtVideoId(string guid); + bool deleteYtVideoId(string guid); cManageUrls* getUrlsObj(); void pushYtVideoId(string, bool); void pushYtVideoIdToClient(string vid_id, string peer, bool); + void pushCfgServerAddressToTv( string tv_addr); private: void addHttpResource(int fd, cHttpResourceBase* resource); int connectToClient(string peer); void setNonBlocking(int fd); + + // status callbacks + void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On); + void TimerChange(const cTimer *Timer, eTimerChange Change); pthread_t mThreadId; int mRequestCount; @@ -99,7 +111,7 @@ class SmartTvServer { vector mConTvClients; int mActiveSessions; - int mHttpClients; + int mHttpClientId; string mConfigDir; cSmartTvConfig *mConfig; -- cgit v1.2.3 From 46e8fb5d5016b10bc578cd4d3b931538bb400f62 Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 1 May 2013 10:44:19 +0200 Subject: Fiy in Urls Datei. --- mngurls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mngurls.c b/mngurls.c index 7e7d886..70696fd 100644 --- a/mngurls.c +++ b/mngurls.c @@ -144,7 +144,7 @@ void cManageUrls::appendToFile(string s_line) { return; } *(mLog->log()) << " cManageUrls::appendToFile: writing " << s_line << endl; - *mFile << s_line; + *mFile << s_line << endl; // mFile->write(s_line.c_str(), s_line.size()); mFile->flush(); -- cgit v1.2.3 From d9ca8bd72350526bf92334c77ea90a8475f36014 Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 1 May 2013 17:55:14 +0200 Subject: cleanups --- responsebase.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/responsebase.c b/responsebase.c index 77bca0a..633b922 100644 --- a/responsebase.c +++ b/responsebase.c @@ -75,6 +75,7 @@ void cResponseBase::sendError(int status, const char *title, const char *extra, void cResponseBase::sendHeaders(int status, const char *title, const char *extra, const char *mime, long long int length, time_t date) { + time_t now; char timebuf[128]; char f[400]; @@ -97,10 +98,12 @@ void cResponseBase::sendHeaders(int status, const char *title, const char *extra snprintf(f, sizeof(f), "Content-Type: %s\r\n", mime); hdr += f; } + if (length >= 0) { snprintf(f, sizeof(f), "Content-Length: %lld\r\n", length); hdr += f; } + if (date != -1) { strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date)); snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf); -- cgit v1.2.3 From b8f2a73bd7ccdd6fca49ac7137da5012d33fc2e5 Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 1 May 2013 17:56:39 +0200 Subject: Plugin v 0.9.8 --- smarttvfactory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smarttvfactory.h b/smarttvfactory.h index 02c03ec..c35d2f6 100644 --- a/smarttvfactory.h +++ b/smarttvfactory.h @@ -47,8 +47,8 @@ class cStatus { using namespace std; -#define PLG_VERSION "0.9.8-pre" -#define SERVER "SmartTvWeb/0.9.8-pre" +#define PLG_VERSION "0.9.8" +#define SERVER "SmartTvWeb/0.9.8" struct sClientEntry { string mac; -- cgit v1.2.3 From 881a2ec980f2368c038ad25fad21583fef73dfa0 Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 1 May 2013 18:21:27 +0200 Subject: Widget v0.94 isNew indicator delete YT URLs Notification, when a recording is started / stopped Notification, when a timer is added / deleted HTTP based configuration. --- smarttv-client/CSS/Main.css | 21 ++++---- smarttv-client/Javascript/Buttons.js | 11 +++- smarttv-client/Javascript/Comm.js | 63 ++++++++++++++++++---- smarttv-client/Javascript/Config.js | 18 +++---- smarttv-client/Javascript/Data.js | 69 +++++++++++++++++++++--- smarttv-client/Javascript/Display.js | 32 +++++------ smarttv-client/Javascript/Main.js | 46 ++++++++++++---- smarttv-client/Javascript/Player.js | 1 + smarttv-client/Javascript/Server.js | 102 ++++++++++++++++++++++++++++++++++- smarttv-client/config.xml | 2 +- 10 files changed, 302 insertions(+), 63 deletions(-) diff --git a/smarttv-client/CSS/Main.css b/smarttv-client/CSS/Main.css index 3b23da8..70c01d9 100755 --- a/smarttv-client/CSS/Main.css +++ b/smarttv-client/CSS/Main.css @@ -89,7 +89,7 @@ body { width: 960px; height: 540px; background: url("../Images/selectScreen-bg.png"); background: -webkit-radial-gradient(80% 80%, ellipse cover, rgba(197,222,234,1) 0%,rgba(138,187,215,1) 31%,rgba(6,109,171,1) 100%); - z-index:10; + z-index:15; } #optionsView { @@ -129,25 +129,28 @@ body { #notify { position: absolute; display: none; - left: 70%; top: 10px; - width: 20%; height: 6%; + right: 4%; top: 10px; + max-height: 30%; + max-width: 40%; -webkit-border-radius: 1em; -webkit-box-shadow:3px 3px 7px 4px rgba(0,0,0, 0.5); border-style:solid; border-width:2px; font-size:16px; - - padding-top:20px; - padding-left:20px; + padding-top:12px; + padding-bottom:10px; + padding-left:20px; padding-right:20px; background-color: rgba(0,0,0,0.5); z-index:50; overflow:hidden; - text-overflow: ellipsis; - white-space : nowrap; } +/* + text-overflow: ellipsis; + white-space : nowrap; +*/ #infoOverlay{ position: absolute; display: none; @@ -215,7 +218,7 @@ body { #main { display: none; position: absolute; - z-index:10; + z-index:15; left: 0px; top: 0px; width: 960px; height: 540px; diff --git a/smarttv-client/Javascript/Buttons.js b/smarttv-client/Javascript/Buttons.js index c069670..b881975 100644 --- a/smarttv-client/Javascript/Buttons.js +++ b/smarttv-client/Javascript/Buttons.js @@ -50,7 +50,16 @@ Buttons.ynEnterCallback = function () { break; case 1: Main.logToServer("ynButtons: Yes "+Player.resumePos); - Server.deleteRecording(Player.guid); + + switch (Main.state) { + case Main.eREC: + Server.deleteRecording(Player.guid); + break; + case Main.eURLS: + Server.deleteUrls(Data.getCurrentItem().childs[Main.selectedVideo].payload.guid); + break; + } + break; } Buttons.ynHide(); diff --git a/smarttv-client/Javascript/Comm.js b/smarttv-client/Javascript/Comm.js index d4cdd18..8c87af6 100644 --- a/smarttv-client/Javascript/Comm.js +++ b/smarttv-client/Javascript/Comm.js @@ -76,26 +76,69 @@ Comm.onMessageReceived = function(message, context) { // context -> message context (headers and etc) Main.logToServer("onMessageReceived:" + message); var msg = eval('(' + message + ')'); - if (msg.type == "YT") { - if (msg.payload.id == "undefined") { - Main.logToServer("ERROR: msg.payload.id is not defined"); - return; - } - Main.logToServer("Found type YT " + msg.payload.id); + switch (msg.type) { + case "YT": + if (msg.payload.id == "undefined") { + Main.logToServer("ERROR: msg.payload.id is not defined"); + return; + } + Main.logToServer("Found type YT " + msg.payload.id); if (Main.state == Main.eURLS) { if (msg.payload.id == "" ) { - Main.logToServer("ERROR: msg.payload.id is empty"); - return; + Main.logToServer("ERROR: msg.payload.id is empty"); + return; } Spinner.show(); UrlsFetcher.autoplay = msg.payload.id; UrlsFetcher.removeWhenStopped = ""; if (msg.payload.store == false) { UrlsFetcher.removeWhenStopped = msg.payload.id; - Main.logToServer("removeWhenStopped= " + msg.payload.id); + Main.logToServer("removeWhenStopped= " + msg.payload.id); } UrlsFetcher.appendGuid(msg.payload.id); } - } + break; + case "CFGADD": + if (msg.payload.serverAddr == "undefined") { + Main.logToServer("ERROR: msg.payload.id is not defined"); + return; + } + Config.updateContext(msg.payload.serverAddr); + if (Config.firstLaunch == true) + Main.state = 1; // ensure, that the cursor is on 1st position + + Main.enableKeys(); + Options.hide(); + Main.changeState(0); + + break; + case "INFO": + Main.logToServer("INFO: type= " + msg.payload.type + " val= " + msg.payload.name); + switch(msg.payload.type) { + case "RECSTART": + Notify.showNotify("Recording started: '" + msg.payload.name +"'", true); + if (Main.state == Main.eREC) { + Server.updateEntry(msg.payload.name); + } + // msg.payload.name is the guid. I should now run a query, then update the database and update the screen (if needed) + + break; + case "RECSTOP": + Notify.showNotify("Recording finished: " + msg.payload.name+"'", true); + break; + case "TCADD": + Notify.showNotify("Timer added: '" + msg.payload.name+"'", true); + break; + case "TCMOD": + Notify.showNotify("Timer modified: '" + msg.payload.name+"'", true); + break; + case "TCDEL": + Notify.showNotify("Timer deleted: '" + msg.payload.name+"'", true); + break; + + } + break; + }; // switch + }; diff --git a/smarttv-client/Javascript/Config.js b/smarttv-client/Javascript/Config.js index 027780a..8f0d898 100755 --- a/smarttv-client/Javascript/Config.js +++ b/smarttv-client/Javascript/Config.js @@ -74,8 +74,8 @@ Config.init = function () { } else { Config.doFirstLaunch(); -// Display.showPopup ("WARNING: Cannot create widget folder. Try Config"); - Display.showPopup (Lang[Lang.sel].configInit); + Display.showPopup ("WARNING: Cannot create widget folder. Try Config"); +// Display.showPopup (Lang[Lang.sel].configInit); // Main.logToServer("ERROR: Cannot create widget folder curWidget.id= " +curWidget.id); } @@ -92,7 +92,7 @@ Config.init = function () { Config.doFirstLaunch = function () { Config.firstLaunch = true; - Main.changeState(4); + Main.changeState(Main.eOPT); }; @@ -160,8 +160,8 @@ Config.fetchConfig = function () { }, error : function (XHR, status, error) { Main.log ("Config Server Error"); -// Display.showPopup("Config Server Error " + XHR.status + " " + status); - Display.showPopup(Lang[Lang.sel].configNoServer + " "+ XHR.status + " " + status); + Display.showPopup("Config Server Error " + XHR.status + " " + status); +// Display.showPopup(Lang[Lang.sel].configNoServer + " "+ XHR.status + " " + status); Main.logToServer("Config Server Error " + XHR.status + " " + status); @@ -208,8 +208,8 @@ Config.readContext = function () { Config.serverUrl = "http://" + Config.serverAddr; } else { -// Display.showPopup ("ERROR: Error in Config File. Try widget re-install."); - Display.showPopup (Lang[Lang.sel].configRead1); + Display.showPopup ("ERROR: Error in Config File. Try widget re-install."); +// Display.showPopup (Lang[Lang.sel].configRead1); // TODO: I should re-write the config file } @@ -227,8 +227,8 @@ Config.readContext = function () { else { Main.log("Config.readContext: Widget Folder creation failed"); -// Display.showPopup ("WARNING: ConfigRead Error and WidgetFolder creation failed.
    Launching Config-Menu from here"); - Display.showPopup (Lang[Lang.sel].configRead2); + Display.showPopup ("WARNING: ConfigRead Error and WidgetFolder creation failed.
    Launching Config-Menu from here"); +// Display.showPopup (Lang[Lang.sel].configRead2); // Main.log("-------------- Error: res = false ------------------------"); } diff --git a/smarttv-client/Javascript/Data.js b/smarttv-client/Javascript/Data.js index 112785e..5eee3f2 100755 --- a/smarttv-client/Javascript/Data.js +++ b/smarttv-client/Javascript/Data.js @@ -3,7 +3,8 @@ var Data = assets : new Item, folderList : [], createAccessMap : false, - directAccessMap : {} + directAccessMap : {}, + sortType : 0 }; Array.prototype.remove = function(from, to) { @@ -17,14 +18,18 @@ Data.reset = function() { this.assets = new Item; this.folderList = []; this.createAccessMap = false; - + this.sortType = 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) { - if (sort == true) - this.assets.sortPayload(); + if (sort == true) { + this.assets.sortPayload(Data.sortType); + if (this.createAccessMap == true) { + Main.log("ERROR: access map does not match anymore"); + } + } this.folderList.push({item : this.assets, id: 0}); Main.log("---------- completed ------------"); @@ -32,6 +37,11 @@ Data.completed= function(sort) { Main.log("Data.completed(): createAccessMap= " + ((this.createAccessMap == true) ? "true": "false")); }; +Data.nextSortType = function () { + Data.sortType = (Data.sortType +1) %3; + this.assets.sortPayload(Data.sortType); +}; + 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); @@ -106,7 +116,7 @@ function Item() { this.title = "root"; this.isFolder = true; this.childs = []; - this.payload = ""; // only set, if (isFolder == false) + this.payload = {}; // only set, if (isFolder == false) } Item.prototype.isFolder = function() { @@ -168,6 +178,7 @@ Item.prototype.addChild = function (key, pyld, level) { Data.directAccessMap[pyld.num].push(this.childs.length); // should start from 1 } folder.title = t; + folder.payload['start'] = 0; folder.addChild(key, pyld, level+1); this.childs.push(folder); } @@ -233,7 +244,7 @@ Item.prototype.print = function(level) { prefix += " "; for (var i = 0; i < this.childs.length; i++) { - Main.log(prefix + this.childs[i].title); + Main.log(prefix + this.childs[i].payload.start + "\t" + this.childs[i].title + " isFolder= " + this.childs[i].isFolder); if (this.childs[i].isFolder == true) { Main.log(prefix+"Childs:"); this.childs[i].print(level +1); @@ -241,6 +252,7 @@ Item.prototype.print = function(level) { } }; +/* Item.prototype.sortPayload = function() { for (var i = 0; i < this.childs.length; i++) { if (this.childs[i].isFolder == true) { @@ -256,4 +268,49 @@ Item.prototype.sortPayload = function() { } }); }; +*/ +Item.prototype.sortPayload = function(sel) { + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i].isFolder == true) { + this.childs[i].sortPayload(sel); + } + } + switch (sel) { + case 1: + // Dy Date + this.childs.sort(function(a,b) { + if (a.payload.start == b.payload.start) { + return ((a.title < b.title) ? -1 : 1); + } + else { + return (a.payload.start - b.payload.start); + } + }); + break; + case 2: + // Dy Date + this.childs.sort(function(a,b) { + if (a.payload.start == b.payload.start) { + return (a.title >= b.title); + } + else { + return (b.payload.start - a.payload.start); + } + }); + break; + case 0: + default: + this.childs.sort(function(a,b) { + if (a.title == b.title) { + return (a.payload.start - b.payload.start); + } + else { + return ((a.title < b.title) ? -1 : 1); + + } + }); + + break; + } +}; diff --git a/smarttv-client/Javascript/Display.js b/smarttv-client/Javascript/Display.js index 93a38ef..94743bd 100755 --- a/smarttv-client/Javascript/Display.js +++ b/smarttv-client/Javascript/Display.js @@ -324,6 +324,7 @@ Display.setVideoList = function(selected, first) { if ((first_item+i) <0) { // wrap around idx = (first_item+i) + Data.getVideoCount(); + res = Display.getDisplayTitle (Data.getCurrentItem().childs[(first_item+i) + Data.getVideoCount()]); } else if ((first_item+i) >= Data.getVideoCount()) { @@ -589,7 +590,7 @@ Display.getDisplayTitle = function(item) { var min = Display.getNumString (digi.getMinutes(), 2); var d_str = mon + "/" + day + " " + hour + ":" + min; - res.c1 = d_str; + res.c1 = d_str + ((item.payload.isnew == "true") ? " *" : ""); res.c2 = item.title; } break; @@ -923,27 +924,28 @@ Display.handlerHideProgress = function() { Display.handlerShowProgress = function() { $("#overlay").fadeIn(400); - + Main.log("************************ OL width= " + $("#olProgressBarBG").width()); + var bar_width = $("#olProgressBarBG").width(); if (Player.isRecording == true) { - $("#olRecProgressBar").show(); - var now = Display.GetEpochTime(); -//thlo var remaining = Math.round(((Player.startTime + Player.duration) - now) * 100/ Player.duration); - var remaining = Math.round(((Player.startTime + Player.getDuration()) - now) * 100/ Player.getDuration()); - var elm = document.getElementById("olRecProgressBar"); - elm.style.display="block"; - elm.style.width = remaining + "%"; - elm.style.left = (100 - remaining) + "%"; + if ((Player.startTime + Player.getDuration()) > now) { + // not done + $("#olRecProgressBar").show(); + var now = Display.GetEpochTime(); + var remaining_px = ((Player.startTime + Player.getDuration()) - now) * bar_width/ Player.getDuration(); + var elm = document.getElementById("olRecProgressBar"); + elm.style.display="block"; + elm.style.width = remaining_px + "px"; + elm.style.left = (bar_width - remaining_px) + "px"; + } + } else $("#olRecProgressBar").hide(); -//thlo var timePercent = (Player.curPlayTime *100)/ Player.totalTime; - var timePercent = (Player.curPlayTime *100)/ Player.getDuration(); + var time_px = (Player.curPlayTime *bar_width)/ Player.getDuration(); + document.getElementById("olProgressBar").style.width = time_px + "px"; - document.getElementById("olProgressBar").style.width = timePercent + "%"; - -//thlo $("#olTimeInfo").text(Player.curPlayTimeStr + " / " + Player.totalTimeStr); $("#olTimeInfo").text(Player.curPlayTimeStr + " / " + Player.getDurationStr()); var Digital=new Date(); diff --git a/smarttv-client/Javascript/Main.js b/smarttv-client/Javascript/Main.js index 2581074..aa33217 100755 --- a/smarttv-client/Javascript/Main.js +++ b/smarttv-client/Javascript/Main.js @@ -61,11 +61,21 @@ var Main = { menuKeyHndl : null }; +$(document).unload(function(){ + Main.onUnload (); +}); + +Main.onUnload = function() { + Server.notifyServer("stopped"); + Player.deinit(); +}; + $(document).ready(function(){ Main.onLoad (); }); Main.onLoad = function() { + window.onShow = showHandler; window.onshow = showHandler; @@ -166,7 +176,6 @@ Main.init = function () { HeartbeatHandler.start(); Server.updateVdrStatus(); -// Server.notifyServer("started"); DirectAccess.init(); Config.getWidgetVersion(); @@ -187,11 +196,11 @@ Main.init = function () { Main.logToServer (xmlDoc); } -*/ /* - * - Read widget conf. find the file to log +*/ +// Read widget conf. find the file to log +/* xhttp=new XMLHttpRequest(); - xhttp.open("GET","$MANAGER_WIDGET/Common/webapi/1.0/webapis.js",false); + xhttp.open("GET","$MANAGER_WIDGET/Common/webapi/1.0/deviceapis.js",false); xhttp.send(""); xmlDoc=xhttp.responseText; Main.logToServer (xmlDoc); @@ -220,10 +229,6 @@ Main.logToServer = function (msg) { */ }; -Main.onUnload = function() { - Server.notifyServer("stopped"); - Player.deinit(); -}; Main.testUrls = function () { Main.log("################## Main.testUrls"); @@ -318,11 +323,14 @@ Main.liveSelected = function() { }; Main.recordingsSelected = function() { + Server.retries = 0; Player.stopCallback = function() { Display.show(); - Server.saveResume (); Data.getCurrentItem().childs[Main.selectedVideo].payload.isnew = "false"; + var res = Display.getDisplayTitle (Data.getCurrentItem().childs[Main.selectedVideo]); + Display.setVideoItem(Display.videoList[Display.currentWindow +Display.FIRSTIDX], res); + Server.saveResume (); }; Server.errorCallback = Main.serverError; @@ -370,7 +378,7 @@ Main.urlsSelected = function() { Display.showPopup(msg); Main.changeState(0); }; - Server.setSort(true); + Server.setSort(false); Spinner.show(); UrlsFetcher.fetchUrlList(); }; @@ -1061,6 +1069,22 @@ cMenuKeyHndl.prototype.handleKeyDown = function (event) { } break; + + case tvKey.KEY_YELLOW: + if (Main.state == Main.eURLS) { + Buttons.ynShow(); + } + break; + case tvKey.KEY_BLUE: + if (Main.state == Main.eREC) { + //change sorting + Spinner.show(); + Data.nextSortType(); + Main.selectedVideo = 0; + Display.setVideoList(Main.selectedVideo, Main.selectedVideo); + Spinner.hide(); + } + break; case tvKey.KEY_RIGHT: Main.log("Right"); diff --git a/smarttv-client/Javascript/Player.js b/smarttv-client/Javascript/Player.js index 6708558..b6986f5 100755 --- a/smarttv-client/Javascript/Player.js +++ b/smarttv-client/Javascript/Player.js @@ -225,6 +225,7 @@ Player.init = function() { Player.deinit = function() { Main.log("Player deinit !!! " ); + Main.logToServer("Player deinit !!! " ); if (Player.AVPlayerObj != null) { Player.AVPlayerObj.stop(); diff --git a/smarttv-client/Javascript/Server.js b/smarttv-client/Javascript/Server.js index 77b8cff..fae4e83 100755 --- a/smarttv-client/Javascript/Server.js +++ b/smarttv-client/Javascript/Server.js @@ -3,7 +3,7 @@ var Server = { errorCallback : null, doSort : false, retries : 0, - + curGuid : "", XHRObj : null }; @@ -81,6 +81,79 @@ Server.fetchVideoList = function(url) { }); }; +Server.updateEntry = function(guid) { + + Server.curGuid = Data.getCurrentItem().childs[Main.selectedVideo].payload.guid; + + //url is sufficed with ?guid + var url = Config.serverUrl + "/recordings.xml?guid="+guid; + Main.logToServer(" Server.updateEntry: guid= " + guid); + + $.ajax({ + url: url, + type : "GET", + success : function(data, status, XHR ) { + Main.logToServer("Server.updateEntry Success Response - status= " + status + " mime= " + XHR.responseType + " data= "+ data); + + $(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(); + var num = parseInt($(this).find('number').text()); + Main.logToServer("Server.updateEntry: title= " + title + " start= " + startVal + " dur= " + durVal + " fps= " + fps); + + 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, num : num}); + + }); // each + + Data.assets.sortPayload(Data.sortType); + + // check, whether Main.selectedVideo still points to the entry with guid + if (Data.getCurrentItem().childs[Main.selectedVideo].payload.guid != Server.curGuid) { + Main.logToServer("Server.updateEntry: curGuid has changed: curGuid= " + Server.curGuid); + Main.logToServer("Server.updateEntry: selVid= "+ Data.getCurrentItem().childs[Main.selectedVideo].payload.guid); + Main.selectedVideo = Main.selectedVideo+1; + Main.logToServer("Server.updateEntry: curGuid has changed: selVid+1"+ Data.getCurrentItem().childs[Main.selectedVideo].payload.guid); + } + + var first_item = Main.selectedVideo - Display.currentWindow; + if (first_item < 0 ) + first_item = 0; + + Display.setVideoList(Main.selectedVideo, first_item); + // Main.selectedVideo does not fit anymore!!!!! + // should do a general reset (jump to 0), when a new element is added + // should reset to zero + // plus update notif + Main.logToServer(" done"); + + }, + error : function (jqXHR, status, error) { + Main.logToServer("Server.updateEntry Error Response - status= " + status + " error= "+ error); + Display.showPopup("Error with XML File: " + status); + Server.retries ++; + }, + parsererror : function () { + Main.logToServer("Server.updateEntry parserError " ); + Display.showPopup("Error in XML File"); + Server.retries ++; + if (Server.errorCallback != null) { + Server.errorCallback("XmlError"); + } + + } + }); +}; //--------------------------------------------- @@ -181,6 +254,33 @@ Server.deleteRecording = function(guid) { }); }; +Server.deleteUrls = function (guid) { + Main.log("Server.deleteUrls"); + Main.logToServer("Server.deleteUrls guid=" + guid); + Notify.handlerShowNotify("Deleting...", false); + + $.ajax({ + url: Config.serverUrl + "/deleteYtUrl?guid=" +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.deleteUrls: Success" ); + }, + error : function (XHR, status, error) { + Main.logToServer("Server.deleteUrls: Error" ); + Notify.showNotify(status, true); + + // show popup +// Notify.showNotify("Error", true); + } + }); + +}; Server.notifyServer = function (state) { Main.log("Server.notifyServer state="+state +"&mac=" + Network.ownMac + "&ip=" + Network.ownIp); diff --git a/smarttv-client/config.xml b/smarttv-client/config.xml index c3f8550..f075f63 100755 --- a/smarttv-client/config.xml +++ b/smarttv-client/config.xml @@ -9,7 +9,7 @@ Images/icon/SmartTvWeb_115.png Images/icon/SmartTvWeb_85.png Images/icon/SmartTvWeb_95.png - 0.93 + 0.94 y y -- cgit v1.2.3 From 9f8fa7d280397e1d79c0cb57969096b052d194fb Mon Sep 17 00:00:00 2001 From: thlo Date: Thu, 2 May 2013 20:29:17 +0200 Subject: New files added. --- Makefile.new | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.new b/Makefile.new index 3c3d769..1db4878 100644 --- a/Makefile.new +++ b/Makefile.new @@ -53,7 +53,7 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o log.o url.o stvw_cfg.o +OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o log.o url.o stvw_cfg.o responsebase.o responsefile.o responsevdrdir.o responsememblk.o ### The main target: -- cgit v1.2.3 From b08431d6b45acada11104c775d3fb56e907dfa5e Mon Sep 17 00:00:00 2001 From: thlo Date: Fri, 10 May 2013 13:17:30 +0200 Subject: HTTP Error responses are numbered (prep for multi-language). --- responsefile.c | 6 +++--- responsevdrdir.c | 34 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/responsefile.c b/responsefile.c index 9724957..938d8ef 100644 --- a/responsefile.c +++ b/responsefile.c @@ -83,7 +83,7 @@ int cResponseFile::sendFile() { struct stat64 statbuf; if (stat64((mRequest->mPath).c_str(), &statbuf) < 0) { - sendError(404, "Not Found", NULL, "File not found."); + sendError(404, "Not Found", NULL, "003 File not found."); return OKAY; } *(mLog->log())<< DEBUGPREFIX @@ -93,14 +93,14 @@ int cResponseFile::sendFile() { char f[400]; if (openFile((mRequest->mPath).c_str()) == ERROR) { - sendError(403, "Forbidden", NULL, "Access denied."); + sendError(403, "Forbidden", NULL, "001 Access denied."); return OKAY; } mFile = fopen((mRequest->mPath).c_str(), "r"); if (!mFile) { - sendError(403, "Forbidden", NULL, "Access denied."); + sendError(403, "Forbidden", NULL, "001 Access denied."); return OKAY; } diff --git a/responsevdrdir.c b/responsevdrdir.c index 50949d5..0d1498c 100644 --- a/responsevdrdir.c +++ b/responsevdrdir.c @@ -121,7 +121,7 @@ bool cResponseVdrDir::isTimeRequest(struct stat *statbuf) { if (rec == NULL) { *(mLog->log())<< DEBUGPREFIX << " Error: Did not find recording= " << mRequest->mPath << endl; - sendError(404, "Not Found", NULL, "File not found."); + sendError(404, "Not Found", NULL, "003 File not found."); return true; } @@ -129,7 +129,7 @@ bool cResponseVdrDir::isTimeRequest(struct stat *statbuf) { double dur = rec->NumFrames() * fps; bool is_pes = rec->IsPesRecording(); if (dur < time) { - sendError(400, "Bad Request", NULL, "Time to large."); + sendError(400, "Bad Request", NULL, "013 Time to large."); return true; } @@ -150,7 +150,7 @@ bool cResponseVdrDir::isTimeRequest(struct stat *statbuf) { *(mLog->log()) << DEBUGPREFIX << " failed to open idx file = "<< (mRequest->mDir +"/index").c_str() << endl; - sendError(404, "Not Found", NULL, "Failed to open Index file"); + sendError(404, "Not Found", NULL, "004 Failed to open Index file"); return OKAY; } @@ -170,7 +170,7 @@ bool cResponseVdrDir::isTimeRequest(struct stat *statbuf) { *(mLog->log())<log())<< DEBUGPREFIX << " No video file in the directory" << endl; - sendError(404, "Not Found", NULL, "File not found."); + sendError(404, "Not Found", NULL, "003 File not found."); return OKAY; } @@ -425,7 +425,7 @@ int cResponseVdrDir::sendVdrDir(struct stat *statbuf) { snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->mPath).c_str(), file_sizes[cur_idx].sIdx); if (openFile(pathbuf) != OKAY) { - sendError(403, "Forbidden", NULL, "Access denied."); + sendError(403, "Forbidden", NULL, "001 Access denied."); return OKAY; } @@ -443,7 +443,7 @@ int cResponseVdrDir::sendVdrDir(struct stat *statbuf) { if (mIsRecording && ((mRequest->rangeHdr).begin > total_file_size)) { *(mLog->log()) << DEBUGPREFIX << " ERROR: Not yet available" << endl; - sendError(404, "Not Found", NULL, "File not found."); + sendError(404, "Not Found", NULL, "003 File not found."); return OKAY; } cur_idx = file_sizes.size() -1; @@ -478,7 +478,7 @@ int cResponseVdrDir::sendVdrDir(struct stat *statbuf) { << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << (mRequest->rangeHdr).begin << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; *(mLog->log())<< "---------------" << endl; - sendError(403, "Forbidden", NULL, "Access denied."); + sendError(403, "Forbidden", NULL, "001 Access denied."); return OKAY; } mRequest->mDir = mRequest->mPath; @@ -562,7 +562,7 @@ int cResponseVdrDir::sendMediaSegment (struct stat *statbuf) { *(mLog->log()) << DEBUGPREFIX << " failed to open idx file = "<< (mRequest->mDir +"/index").c_str() << endl; - sendError(404, "Not Found", NULL, "Failed to open Index file"); + sendError(404, "Not Found", NULL, "004 Failed to open Index file"); return OKAY; } @@ -581,7 +581,7 @@ int cResponseVdrDir::sendMediaSegment (struct stat *statbuf) { *(mLog->log())<log())<< DEBUGPREFIX << " Failed to open file= " << seg_fn - << " mRemLength= " << mRemLength<< endl; - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; + *(mLog->log())<< DEBUGPREFIX << " Failed to open file= " << seg_fn + << " mRemLength= " << mRemLength<< endl; + sendError(404, "Not Found", NULL, "003 File not found."); + return OKAY; } fseek(mFile, start_offset, SEEK_SET); -- cgit v1.2.3 From 9674b225dd0fddc5d2fe0c11c108c5167607523d Mon Sep 17 00:00:00 2001 From: thlo Date: Fri, 10 May 2013 13:19:35 +0200 Subject: New Methods timer.xml and deleterTimer.xml. timestamp added to vdrstatus.xml. HTTP Error responses are numbered (prep for multi-language). --- httpresource.c | 43 ++++++++-- responsememblk.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++----- responsememblk.h | 6 +- 3 files changed, 266 insertions(+), 27 deletions(-) diff --git a/httpresource.c b/httpresource.c index f718816..08a3e08 100755 --- a/httpresource.c +++ b/httpresource.c @@ -309,6 +309,12 @@ int cHttpResource::processRequest() { return OKAY; } + if (mPath.compare("/timers.xml") == 0) { + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendTimersXml(); + return OKAY; + } + if (mPath.compare("/setResume.xml") == 0) { mResponse = new cResponseMemBlk(this); ((cResponseMemBlk*)mResponse)->receiveResume(); @@ -327,6 +333,19 @@ int cHttpResource::processRequest() { return OKAY; } + //thlo for testing purpose + if (mPath.compare("/deleteTimer.xml") == 0) { + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveDelTimerReq(); + return OKAY; + } + + //thlo for testing purpose + if (mPath.compare("/addTimer.xml") == 0) { + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveAddTimerReq(); + return OKAY; + } #endif if (mPath.compare("/yt-bookmarklet.js") == 0) { @@ -357,7 +376,7 @@ int cHttpResource::processRequest() { mPath = mFactory->getConfigDir() + "/widget.conf"; if (stat(mPath.c_str(), &statbuf) < 0) { - mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "003 File not found."); // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } @@ -373,7 +392,7 @@ int cHttpResource::processRequest() { mPath = mFactory->getConfigDir() + "/web/favicon.ico"; if (stat(mPath.c_str(), &statbuf) < 0) { - mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "003 File not found."); // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } @@ -427,7 +446,7 @@ int cHttpResource::processRequest() { // checking, whether the file or directory exists *(mLog->log())<< DEBUGPREFIX << " File Not found " << mPath << endl; - mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "003 File not found."); // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; @@ -453,7 +472,7 @@ int cHttpResource::processRequest() { if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { // No directory access outside of MediaFolder (and also VDRCONG/web) - mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "003 File not found."); // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } @@ -482,7 +501,7 @@ int cHttpResource::processRequest() { // Check, if requested file is in Media Directory if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { - mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found."); + mResponse = new cResponseError(this, 404, "Not Found", NULL, "003 File not found."); // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found."); return OKAY; } @@ -588,12 +607,24 @@ int cHttpResource::handlePost() { } if (mPath.compare("/deleteRecording.xml") == 0) { - mResponse = new cResponseMemBlk(this); ((cResponseMemBlk*)mResponse)->receiveDelRecReq(); return OKAY; } + if (mPath.compare("/deleteTimer.xml") == 0) { + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveDelTimerReq(); + return OKAY; + } + + if (mPath.compare("/addTimer.xml") == 0) { + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->receiveAddTimerReq(); + return OKAY; + } + + // Should not reach the end of the function return ERROR; } diff --git a/responsememblk.c b/responsememblk.c index 843894d..4f2b23a 100644 --- a/responsememblk.c +++ b/responsememblk.c @@ -138,7 +138,7 @@ int cResponseMemBlk::receiveYtUrl() { } - sendError(400, "Bad Request", NULL, "Mandatory Line attribute not present."); + sendError(400, "Bad Request", NULL, "001 Mandatory Line attribute not present."); return OKAY; } @@ -155,7 +155,7 @@ int cResponseMemBlk::receiveDelYtUrl() { *(mLog->log())<< DEBUGPREFIX << " ERROR in cResponseMemBlk::receiveDelYtUrl: guid not found in query." << endl; - sendError(400, "Bad Request", NULL, "No guid in query line"); + sendError(400, "Bad Request", NULL, "002 No guid in query line"); return OKAY; } @@ -163,7 +163,7 @@ int cResponseMemBlk::receiveDelYtUrl() { sendHeaders(200, "OK", NULL, NULL, -1, -1); } else { - sendError(400, "Bad Request.", NULL, "Entry not found. Deletion failed!"); + sendError(400, "Bad Request.", NULL, "003 Entry not found. Deletion failed!"); } return OKAY; @@ -184,7 +184,7 @@ int cResponseMemBlk::receiveCfgServerAddrs() { << " receiveCfgServerAddrs: no server address" << endl; - sendHeaders(400, "Bad Request", NULL, "TV address field empty", 0, -1); + sendHeaders(400, "Bad Request", NULL, "004 TV address field empty", 0, -1); return OKAY; } @@ -195,7 +195,7 @@ int cResponseMemBlk::receiveCfgServerAddrs() { } - sendError(400, "Bad Request", NULL, "Mandatory TV address attribute not present."); + sendError(400, "Bad Request", NULL, "005 Mandatory TV address attribute not present."); return OKAY; } @@ -339,7 +339,7 @@ int cResponseMemBlk::receiveResume() { << " resume= " << entry.mResume << endl; - sendError(400, "Bad Request", NULL, "Failed to find the recording."); + sendError(400, "Bad Request", NULL, "006 Failed to find the recording."); return OKAY; } @@ -397,13 +397,13 @@ int cResponseMemBlk::sendResumeXml () { //Error 404 *(mLog->log())<< DEBUGPREFIX << " ERROR in sendResume: recording not found - filename= " << entry.mFilename << endl; - sendError(400, "Bad Request", NULL, "Failed to find the recording."); + sendError(400, "Bad Request", NULL, "007 Failed to find the recording."); return OKAY; } if (rec->IsNew()) { *(mLog->log())<< DEBUGPREFIX << " sendResume: file is new " << endl; - sendError(400, "Bad Request", NULL, "File is new."); + sendError(400, "Bad Request", NULL, "008 File is new."); return OKAY; } cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); @@ -443,7 +443,7 @@ int cResponseMemBlk::receiveDelRecReq() { *(mLog->log())<< DEBUGPREFIX << " ERROR: id not found in query." << endl; - sendError(400, "Bad Request", NULL, "no id in query line"); + sendError(400, "Bad Request", NULL, "009 No id in query line"); return OKAY; } mRequest->mPath = cUrlEncode::doUrlSaveDecode(id); @@ -453,7 +453,7 @@ int cResponseMemBlk::receiveDelRecReq() { *(mLog->log())<< DEBUGPREFIX << " ERROR: Recording not found. Deletion failed: mPath= " << mRequest->mPath << endl; - sendError(404, "Not Found.", NULL, "Recording not found. Deletion failed!"); + sendError(404, "Not Found.", NULL, "001 Recording not found. Deletion failed!"); return OKAY; } if ( rec->Delete() ) { @@ -464,7 +464,7 @@ int cResponseMemBlk::receiveDelRecReq() { *(mLog->log())<< DEBUGPREFIX << " ERROR: rec->Delete() returns false. mPath= " << mRequest->mPath << endl; - sendError(500, "Internal Server Error", NULL, "deletion failed!"); + sendError(500, "Internal Server Error", NULL, "001 deletion failed!"); return OKAY; } @@ -498,7 +498,7 @@ int cResponseMemBlk::sendDir(struct stat *statbuf) { if (len == 0 || mRequest->mPath[len - 1] != '/') { snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mRequest->mPath.c_str()); - sendError(302, "Found", pathbuf, "Directories must end with a slash."); + sendError(302, "Found", pathbuf, "001 Directories must end with a slash."); return OKAY; } @@ -744,6 +744,203 @@ void cResponseMemBlk::writeMPD(double duration, int bitrate, float seg_dur, int sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); } +void cResponseMemBlk::receiveAddTimerReq() { + if (isHeadRequest()) + return ; + + *(mLog->log()) << DEBUGPREFIX << " cResponseMemBlk::receiveAddTimerReq" << endl; + + vector avps; + mRequest->parseQueryLine(&avps); + + //guid=&wd=&dy=&st=&sp= + string guid = ""; + + string dy_str; + time_t day = 0; + + string st_str; + int start = 0; + + string sp_str; + int stop =0; + + if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY) { + *(mLog->log()) << DEBUGPREFIX + << " guid= " << guid << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "dy", dy_str) == OKAY) { + day = atol(dy_str.c_str()); + *(mLog->log()) << DEBUGPREFIX << " dy= " << day << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "st", st_str) == OKAY) { + start = atoi(st_str.c_str()); + *(mLog->log()) << DEBUGPREFIX << " st= " << start << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "sp", sp_str) == OKAY) { + stop = atoi(sp_str.c_str()); + *(mLog->log()) << DEBUGPREFIX << " sp= " << stop << endl; + } + + if (Timers.BeingEdited()) { + *(mLog->log()) << DEBUGPREFIX << " cResponseMemBlk::receiveAddTimerReq: Timers are being edited. returning " << endl; + sendError(503, "Service Unavailable", NULL, "001 Timers are being edited."); + return; + } + + // create the timer... + // Issue: find the event object. + return; +} + +void cResponseMemBlk::receiveDelTimerReq() { + if (isHeadRequest()) + return ; + + *(mLog->log()) << DEBUGPREFIX << " cResponseMemBlk::receiveDelTimerReq" << endl; + + vector avps; + mRequest->parseQueryLine(&avps); + + //guid=&wd=&dy=&st=&sp= + string guid = ""; + string wd_str; + int weekdays = 0; + + string dy_str; + time_t day = 0; + + string st_str; + int start = 0; + + string sp_str; + int stop =0; + + if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY) { + *(mLog->log()) << DEBUGPREFIX + << " guid= " << guid << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "wd", wd_str) == OKAY) { + weekdays = atoi(wd_str.c_str()); + *(mLog->log()) << DEBUGPREFIX << " wd= " << weekdays << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "dy", dy_str) == OKAY) { + day = atol(dy_str.c_str()); + *(mLog->log()) << DEBUGPREFIX << " dy= " << day << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "st", st_str) == OKAY) { + start = atoi(st_str.c_str()); + *(mLog->log()) << DEBUGPREFIX << " st= " << start << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "sp", sp_str) == OKAY) { + stop = atoi(sp_str.c_str()); + *(mLog->log()) << DEBUGPREFIX << " sp= " << stop << endl; + } + + if (Timers.BeingEdited()) { + *(mLog->log()) << DEBUGPREFIX << " cResponseMemBlk::receiveDelTimerReq: Timers are being edited. returning " << endl; + sendError(503, "Service Unavailable", NULL, "001 Timers are being edited."); + return; + } + + cTimer *to_del = NULL; + for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ + ti->Matches(); + if ((guid.compare(*(ti->Channel()->GetChannelID()).ToString()) == 0) && + (ti->WeekDays() && ti->WeekDays() == weekdays || !ti->WeekDays() && ti->Day() == day) && + ti->Start() == start && + ti->Stop() == stop) { + to_del = ti; + break; + } + } + + if (to_del != NULL) { + *(mLog->log()) << DEBUGPREFIX << " found a timer to delete" << endl; + Timers.Del(to_del, true); + + sendHeaders(200, "OK", NULL, NULL, 0, -1); + } + else { + sendError(400, "Bad Request", NULL, "010 No Timer found."); + } +} + +void cResponseMemBlk::sendTimersXml() { + char f[200]; + + if (isHeadRequest()) + return; +#ifndef STANDALONE + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ + ti->Matches(); + + *mResponseMessage += "\n"; + + // snprintf(f, sizeof(f), "%s\n", cUrlEncode::doXmlSaveEncode(*(ti->ToText(true))).c_str()); + // *mResponseMessage += f; + snprintf(f, sizeof(f), "%s\n", cUrlEncode::doXmlSaveEncode(ti->File()).c_str()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%s\n", ti->Channel()->Name()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%s\n", ((ti->IsSingleEvent()) ? "true" : "false" )); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%s\n", *cTimer::PrintDay(ti->Day(), ti->WeekDays(), true)); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%d\n", ti->WeekDays()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%ld\n", ti->Day()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%d\n", ti->Start()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%d\n", ti->Stop()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%ld\n", ti->StartTime()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%ld\n", ti->StopTime()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%s\n", *(ti->Channel()->GetChannelID()).ToString()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%d\n", ti->Flags()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%s\n", ((ti->HasFlags(tfRecording) )? "true":"false")); + *mResponseMessage += f; + + *mResponseMessage += "\n"; + + } + *mResponseMessage += "\n"; + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); + // sendHeaders(200, "OK", NULL, "text/plain", mResponseMessage->size(), -1); +#endif +} uint64_t cResponseMemBlk::getVdrFileSize() { // iter over all vdr files and get file size @@ -1022,7 +1219,7 @@ int cResponseMemBlk::sendMediaXml (struct stat *statbuf) { vector entries; if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) { - sendError(404, "Not Found", NULL, "Media Folder likely not configured."); + sendError(404, "Not Found", NULL, "002 Media Folder likely not configured."); return OKAY; } @@ -1074,12 +1271,19 @@ int cResponseMemBlk::sendVdrStatusXml (struct stat *statbuf) { int free; int used; int percent; + char timebuf[128]; + time_t now; percent = VideoDiskSpace(&free, &used); *mResponseMessage += "\n"; *mResponseMessage += "\n"; + now = time(NULL); + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", localtime(&now)); // ISO 8601 + snprintf(f, sizeof(f), "%s\n", timebuf); + *mResponseMessage += f; + *mResponseMessage += "\n"; snprintf(f, sizeof(f), "%d", free); *mResponseMessage += f; @@ -1386,7 +1590,7 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { << endl; delete mResponseMessage; mResponseMessage = NULL; - sendError(400, "Bad Request", NULL, "no id in query line"); + sendError(400, "Bad Request", NULL, "011 No id in query line"); return OKAY; } @@ -1406,7 +1610,7 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { << endl; delete mResponseMessage; mResponseMessage = NULL; - sendError(400, "Bad Request", NULL, "Invalid Channel ID."); + sendError(400, "Bad Request", NULL, "012 Invalid Channel ID."); return OKAY; } @@ -1420,7 +1624,7 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { << endl; delete mResponseMessage; mResponseMessage = NULL; - sendError(500, "Internal Server Error", NULL, "Schedule is zero."); + sendError(500, "Internal Server Error", NULL, "001 Schedule is zero."); return OKAY; } @@ -1443,7 +1647,7 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { << endl; delete mResponseMessage; mResponseMessage = NULL; - sendError(500, "Internal Server Error", NULL, "Event is zero."); + sendError(500, "Internal Server Error", NULL, "002 Event is zero."); return OKAY; } @@ -1467,7 +1671,7 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { delete mResponseMessage; mResponseMessage = NULL; - sendError(500, "Internal Server Error", NULL, "Title is zero."); + sendError(500, "Internal Server Error", NULL, "003 Title is zero."); return OKAY; } @@ -1491,7 +1695,7 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { delete mResponseMessage; mResponseMessage = NULL; - sendError(500, "Internal Server Error", NULL, "Description is zero."); + sendError(500, "Internal Server Error", NULL, "004 Description is zero."); return OKAY; } hdr += "\n"; @@ -1702,7 +1906,7 @@ int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) { recording->Start(), rec_dur, recording->FramesPerSecond(), (recording->IsPesRecording() ? 0: 1), (recording->IsNew() ? 0: 1)) == ERROR) { *mResponseMessage = ""; - sendError(500, "Internal Server Error", NULL, "writeXMLItem returned an error"); + sendError(500, "Internal Server Error", NULL, "005 writeXMLItem returned an error"); return OKAY; } item_count ++; diff --git a/responsememblk.h b/responsememblk.h index 091d9ae..34d98b1 100644 --- a/responsememblk.h +++ b/responsememblk.h @@ -65,7 +65,11 @@ class cResponseMemBlk : public cResponseBase { int sendUrlsXml (); int sendMediaXml (struct stat *statbuf); int sendManifest (struct stat *statbuf, bool is_hls = true); - + void sendTimersXml(); + + void receiveAddTimerReq(); + void receiveDelTimerReq(); + void receiveClientInfo(); int receiveResume(); -- cgit v1.2.3 From 2c84a7c872c7237b0b2b87d9fe20fc4335573321 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 11 May 2013 14:49:20 +0200 Subject: First Version of addTimer. --- responsememblk.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 6 deletions(-) diff --git a/responsememblk.c b/responsememblk.c index 4f2b23a..9a81727 100644 --- a/responsememblk.c +++ b/responsememblk.c @@ -464,7 +464,7 @@ int cResponseMemBlk::receiveDelRecReq() { *(mLog->log())<< DEBUGPREFIX << " ERROR: rec->Delete() returns false. mPath= " << mRequest->mPath << endl; - sendError(500, "Internal Server Error", NULL, "001 deletion failed!"); + sendError(500, "Internal Server Error", NULL, "006 deletion failed!"); return OKAY; } @@ -752,9 +752,17 @@ void cResponseMemBlk::receiveAddTimerReq() { vector avps; mRequest->parseQueryLine(&avps); - - //guid=&wd=&dy=&st=&sp= + + //guid=&evid= + // later + //guid=&wd=&dy=&st=&sp=&f= + string guid = ""; + string ev_id_str = ""; + tEventID ev_id = 0; + + string wd_str; + int weekday = 0; string dy_str; time_t day = 0; @@ -770,6 +778,17 @@ void cResponseMemBlk::receiveAddTimerReq() { << " guid= " << guid << endl; } + if (mRequest->getQueryAttributeValue(&avps, "evid", ev_id_str) == OKAY) { + ev_id = atol(ev_id_str.c_str()); + *(mLog->log()) << DEBUGPREFIX + << " ev_id= " << ev_id << endl; + } + + if (mRequest->getQueryAttributeValue(&avps, "wd", wd_str) == OKAY) { + weekday = atoi(wd_str.c_str()); + *(mLog->log()) << DEBUGPREFIX << " wd= " << weekday << endl; + } + if (mRequest->getQueryAttributeValue(&avps, "dy", dy_str) == OKAY) { day = atol(dy_str.c_str()); *(mLog->log()) << DEBUGPREFIX << " dy= " << day << endl; @@ -792,8 +811,108 @@ void cResponseMemBlk::receiveAddTimerReq() { } // create the timer... + // Issue: find the event object. + // if ev_id is not "", then lookup the event_id + tChannelID chan_id = tChannelID::FromString (guid.c_str()); + if ( chan_id == tChannelID::InvalidID) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Not possible to get the ChannelId from the string" + << endl; + delete mResponseMessage; + mResponseMessage = NULL; + sendError(400, "Bad Request", NULL, "012 Invalid Channel ID."); + return; + } + + if (ev_id == 0) { + // no event id -> not implemented + sendError(501, "Not Implemented", NULL, "001 Not Implemented. evid shall be present."); + return ; + } + + // have an event id + cSchedulesLock * lock = new cSchedulesLock(false, 500); + const cSchedules *schedules = cSchedules::Schedules(*lock); + + const cSchedule *schedule = schedules->GetSchedule(chan_id); + if (schedule == NULL) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Schedule is zero for guid= " << guid + << endl; + delete mResponseMessage; + delete lock; + mResponseMessage = NULL; + sendError(500, "Internal Server Error", NULL, "001 Schedule is zero."); + return; + } + + // time_t now = time(NULL); + const cEvent *ev = schedule->GetEvent(ev_id); + + if (ev == NULL) { + *(mLog->log())<< DEBUGPREFIX + << "ERROR: Event not found for guid= " << guid + << " and ev_id= " << ev_id + << endl; + delete mResponseMessage; + delete lock; + mResponseMessage = NULL; + sendError(500, "Internal Server Error", NULL, "002 Event is zero."); + return; + } + + // now, create a new timer + if (ev->Title() == NULL) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Title not set for guid= " << guid + << " and ev_id= " << ev_id + << endl; + + delete mResponseMessage; + delete lock; + mResponseMessage = NULL; + sendError(500, "Internal Server Error", NULL, "007 Title is zero."); + return; + } + + delete lock; + //now + cTimer *t = new cTimer(ev); + Timers.Add(t); + Timers.SetModified(); + + *(mLog->log())<< DEBUGPREFIX + << " timer created for guid= " << guid + << " and ev_id= " << ev_id + << " filename= " << t->File() + << endl; + + + char f[200]; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + snprintf(f, sizeof(f), "%s\n", cUrlEncode::doUrlSaveEncode(t->File()).c_str()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%ld\n", t->StartTime()); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%ld\n", t->StopTime()); + *mResponseMessage += f; + + *mResponseMessage += "\n"; + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); return; + + + } void cResponseMemBlk::receiveDelTimerReq() { @@ -854,9 +973,9 @@ void cResponseMemBlk::receiveDelTimerReq() { for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ ti->Matches(); if ((guid.compare(*(ti->Channel()->GetChannelID()).ToString()) == 0) && - (ti->WeekDays() && ti->WeekDays() == weekdays || !ti->WeekDays() && ti->Day() == day) && - ti->Start() == start && - ti->Stop() == stop) { + ((ti->WeekDays() && (ti->WeekDays() == weekdays)) || (!ti->WeekDays() && (ti->Day() == day))) && + (ti->Start() == start) && + (ti->Stop() == stop)) { to_del = ti; break; } @@ -1623,6 +1742,7 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { << "ERROR: Schedule is zero for guid= " << id << endl; delete mResponseMessage; + delete lock; mResponseMessage = NULL; sendError(500, "Internal Server Error", NULL, "001 Schedule is zero."); return OKAY; @@ -1646,6 +1766,8 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { << "ERROR: Event is zero for guid= " << id << endl; delete mResponseMessage; + delete lock; + mResponseMessage = NULL; sendError(500, "Internal Server Error", NULL, "002 Event is zero."); return OKAY; @@ -1670,6 +1792,8 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { hdr += "Empty\n"; delete mResponseMessage; + delete lock; + mResponseMessage = NULL; sendError(500, "Internal Server Error", NULL, "003 Title is zero."); return OKAY; @@ -1677,12 +1801,16 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { hdr += "" + id + "\n"; + snprintf(f, sizeof(f), "%u\n", ev->EventID()); + hdr += f ; + *(mLog->log())<< DEBUGPREFIX << " guid= " << id << " title= " << ev->Title() << " start= " << ev->StartTime() << " end= " << ev->EndTime() << " now= " << now + << " EvId= " << ev->EventID() << endl; if (add_desc) { hdr += ""; @@ -1694,6 +1822,7 @@ int cResponseMemBlk::sendEpgXml (struct stat *statbuf) { << " ERROR: description is zero for guid= " << id << endl; delete mResponseMessage; + delete lock; mResponseMessage = NULL; sendError(500, "Internal Server Error", NULL, "004 Description is zero."); return OKAY; -- cgit v1.2.3 From aa83ca05d35edbba7784909ee09da1980b9fe450 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 11 May 2013 15:45:00 +0200 Subject: OsdStatusMessage added. --- httpclient.c | 16 ++++++++++++++++ httpclient.h | 12 +++++++++++- smarttvfactory.c | 21 ++++++++++++++++++++- smarttvfactory.h | 1 + 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/httpclient.c b/httpclient.c index f1dd793..a3fb192 100644 --- a/httpclient.c +++ b/httpclient.c @@ -389,3 +389,19 @@ cHttpInfoClient::~cHttpInfoClient() { string cHttpInfoClient::getMsgBody(int) { return "{\"type\":\"INFO\",payload:" + mBody +"}";; } + + +//-------------------------------- +//----- cHttpMesgPushClient ------ +//-------------------------------- +cHttpMesgPushClient::cHttpMesgPushClient(int f, int id, int port, SmartTvServer* fac, string peer, string mesg) : cHttpClientBase(f, id, port, fac, peer), mMesg(mesg) { + + createRequestMessage(""); +} + +cHttpMesgPushClient::~cHttpMesgPushClient() { +} + +string cHttpMesgPushClient::getMsgBody(int) { + return "{\"type\":\"MESG\",payload:" + mMesg +"}";; +} diff --git a/httpclient.h b/httpclient.h index 000eaa8..d30528e 100644 --- a/httpclient.h +++ b/httpclient.h @@ -105,8 +105,18 @@ class cHttpInfoClient : public cHttpClientBase { protected: string getMsgBody(int ); - string mBody; }; +class cHttpMesgPushClient : public cHttpClientBase { + public: + cHttpMesgPushClient(int, int, int, SmartTvServer*, string peer, string mesg); + virtual ~cHttpMesgPushClient(); + + protected: + string getMsgBody(int ); + string mMesg; +}; + + #endif diff --git a/smarttvfactory.c b/smarttvfactory.c index f721658..763baeb 100755 --- a/smarttvfactory.c +++ b/smarttvfactory.c @@ -132,6 +132,7 @@ void SmartTvServer::Recording(const cDevice *Device, const char *Name, const cha } }; + void SmartTvServer::TimerChange(const cTimer *Timer, eTimerChange Change) { #ifndef DEBUG *(mLog.log()) << "SmartTvServer::TimerChange" @@ -181,6 +182,24 @@ void SmartTvServer::TimerChange(const cTimer *Timer, eTimerChange Change) { } } +void SmartTvServer::OsdStatusMessage(const char *Message) { + *(mLog.log()) << "SmartTvServer::OsdStatusMessage: Msg= " << ((Message != NULL) ? Message : "") << endl; + + if (Message == NULL) + return; + + string msg = Message; + + for (uint i = 0; i < mConTvClients.size(); i ++) { + if ((mConTvClients[i]->ip).compare("") != 0) { + int cfd= connectToClient(mConTvClients[i]->ip); + if (cfd < 0) + continue; + addHttpResource(cfd, new cHttpMesgPushClient(cfd, mHttpClientId, serverPort, this, mConTvClients[i]->ip, msg)); + } + } +} + void SmartTvServer::cleanUp() { // close listening ports for (uint idx= 0; idx < clientList.size(); idx++) { @@ -373,7 +392,7 @@ void SmartTvServer::threadLoop() { void SmartTvServer::loop() { socklen_t addr_size = 0; - int rfd; + unsigned int rfd; sockaddr_in sadr; int req_id = 0; int ret = 0; diff --git a/smarttvfactory.h b/smarttvfactory.h index c35d2f6..a225a65 100644 --- a/smarttvfactory.h +++ b/smarttvfactory.h @@ -97,6 +97,7 @@ class SmartTvServer : public cStatus { // status callbacks void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On); void TimerChange(const cTimer *Timer, eTimerChange Change); + void OsdStatusMessage(const char *Message); pthread_t mThreadId; int mRequestCount; -- cgit v1.2.3 From 12acbea847e73b13ff4e29af2455ebc60d9508f4 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 25 May 2013 12:12:39 +0200 Subject: Increase Plugin Version --- smarttvfactory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smarttvfactory.h b/smarttvfactory.h index a225a65..5fb29d0 100644 --- a/smarttvfactory.h +++ b/smarttvfactory.h @@ -47,8 +47,8 @@ class cStatus { using namespace std; -#define PLG_VERSION "0.9.8" -#define SERVER "SmartTvWeb/0.9.8" +#define PLG_VERSION "0.9.9" +#define SERVER "SmartTvWeb/0.9.9" struct sClientEntry { string mac; -- cgit v1.2.3 From aec1dc17d2b04236b4ea35fa1a44379602ee19b9 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 25 May 2013 12:13:34 +0200 Subject: serverName.xml added. Skeleton for getRecCmds.xml added. --- httpresource.c | 13 +++++++++++++ responsememblk.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- responsememblk.h | 5 +++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/httpresource.c b/httpresource.c index 08a3e08..e29358b 100755 --- a/httpresource.c +++ b/httpresource.c @@ -315,6 +315,12 @@ int cHttpResource::processRequest() { return OKAY; } + if (mPath.compare("/reccmds") == 0) { + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendRecCmds(); + return OKAY; + } + if (mPath.compare("/setResume.xml") == 0) { mResponse = new cResponseMemBlk(this); ((cResponseMemBlk*)mResponse)->receiveResume(); @@ -348,6 +354,13 @@ int cHttpResource::processRequest() { } #endif + if (mPath.compare("/serverName.xml") == 0) { + mResponse = new cResponseMemBlk(this); + ((cResponseMemBlk*)mResponse)->sendServerNameXml( ); + return OKAY; + } + + if (mPath.compare("/yt-bookmarklet.js") == 0) { mResponse = new cResponseMemBlk(this); ((cResponseMemBlk*)mResponse)->sendYtBookmarkletJs(); diff --git a/responsememblk.c b/responsememblk.c index 9a81727..b8d62e8 100644 --- a/responsememblk.c +++ b/responsememblk.c @@ -1061,6 +1061,38 @@ void cResponseMemBlk::sendTimersXml() { #endif } + +void cResponseMemBlk::sendRecCmds() { + *(mLog->log()) << DEBUGPREFIX << " --------------- sendRecCmds ---------------" << endl; + string msg = writeCommands("Recording Commands", &RecordingCommands, " "); + *(mLog->log()) << DEBUGPREFIX << " --------------- sendRecCmds ---------------" << endl; + + if (isHeadRequest()) + return; + + /* mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + */ + sendHeaders(200, "OK", NULL, "text/plain", 0, -1); + +} + + +string cResponseMemBlk::writeCommands(const char *title, cList *commands, string pref) { + // *(mLog->log()) << DEBUGPREFIX << " title" << endl; + string res = "-\n"; + for (cNestedItem *Cmd = commands->First(); Cmd; Cmd = commands->Next(Cmd)) { + const char *s = Cmd->Text(); + if (Cmd->SubItems()) + res += writeCommands(s, Cmd->SubItems(), (pref + "+")); + else + *(mLog->log()) << DEBUGPREFIX << pref << "title= " << s << endl; + } + return res; +} + + uint64_t cResponseMemBlk::getVdrFileSize() { // iter over all vdr files and get file size struct stat statbuf; @@ -1369,8 +1401,31 @@ int cResponseMemBlk::sendMediaXml (struct stat *statbuf) { return OKAY; } +void cResponseMemBlk::sendServerNameXml () { + if (isHeadRequest()) + return ; + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + + mResponseMessagePos = 0; + + mRequest->mConnState = SERVING; + + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + *mResponseMessage += ""; + gethostname(f, sizeof(f)); + *mResponseMessage += f; + *mResponseMessage += "\n"; + + *mResponseMessage += "" + mRequest->getOwnIp() +"\n"; + *mResponseMessage += "\n"; + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); +} int cResponseMemBlk::sendVdrStatusXml (struct stat *statbuf) { @@ -1402,7 +1457,7 @@ int cResponseMemBlk::sendVdrStatusXml (struct stat *statbuf) { strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", localtime(&now)); // ISO 8601 snprintf(f, sizeof(f), "%s\n", timebuf); *mResponseMessage += f; - + *mResponseMessage += "\n"; snprintf(f, sizeof(f), "%d", free); *mResponseMessage += f; diff --git a/responsememblk.h b/responsememblk.h index 34d98b1..206c33c 100644 --- a/responsememblk.h +++ b/responsememblk.h @@ -30,6 +30,8 @@ #include #include "responsebase.h" +#include + using namespace std; struct sFileEntry { @@ -58,6 +60,7 @@ class cResponseMemBlk : public cResponseBase { int sendChannelsXml (struct stat *statbuf); int sendResumeXml (); int sendVdrStatusXml (struct stat *statbuf); + void sendServerNameXml (); int sendYtBookmarkletJs(); int sendBmlInstHtml(); @@ -66,7 +69,9 @@ class cResponseMemBlk : public cResponseBase { int sendMediaXml (struct stat *statbuf); int sendManifest (struct stat *statbuf, bool is_hls = true); void sendTimersXml(); + void sendRecCmds(); + string writeCommands(const char *title, cList *commands, string pref); void receiveAddTimerReq(); void receiveDelTimerReq(); -- cgit v1.2.3 From 5ca471d4f911572cc89f12ccd28ef94eb53d180a Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 25 May 2013 16:30:10 +0200 Subject: add port to hostname in serverName.xml --- responsememblk.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/responsememblk.c b/responsememblk.c index b8d62e8..cb90599 100644 --- a/responsememblk.c +++ b/responsememblk.c @@ -1408,11 +1408,16 @@ void cResponseMemBlk::sendServerNameXml () { char f[400]; mResponseMessage = new string(); *mResponseMessage = ""; - + mResponseMessagePos = 0; mRequest->mConnState = SERVING; + stringstream own_host ; + own_host << mRequest->getOwnIp() + << ":" << mRequest->mServerPort; + + *mResponseMessage += "\n"; *mResponseMessage += "\n"; @@ -1421,7 +1426,7 @@ void cResponseMemBlk::sendServerNameXml () { *mResponseMessage += f; *mResponseMessage += "\n"; - *mResponseMessage += "" + mRequest->getOwnIp() +"\n"; + *mResponseMessage += "" + own_host.str() +"\n"; *mResponseMessage += "\n"; sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); -- cgit v1.2.3 From c47e5227a1d1f47f74c67bc6b2d70b5f57e34339 Mon Sep 17 00:00:00 2001 From: thlo Date: Sat, 15 Jun 2013 15:46:52 +0200 Subject: init version --- debian/changelog | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 debian/changelog diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..6a8f677 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +PACKAGE (VERSION) UNRELEASED; urgency=medium + + * Initial release. (Closes: #XXXXXX) + + -- thlo Sat, 15 Jun 2013 15:44:43 +0200 -- cgit v1.2.3 From 66d5e7f95f2f1c3c51bd73f86466a8bd2f16fbc7 Mon Sep 17 00:00:00 2001 From: thlo Date: Wed, 24 Jul 2013 20:32:03 +0200 Subject: Widget updates: - Multi-Server Support - Number key personalization for recordings - Timezone fix - Fix in YouTube Handling - Fix in Helpbar - Other misc fixes --- smarttv-client/CSS/Main.css | 11 +- smarttv-client/Javascript/Buttons.js | 34 ++++- smarttv-client/Javascript/Comm.js | 15 +- smarttv-client/Javascript/Config.js | 233 +++++++++++++++++++++++++++--- smarttv-client/Javascript/Data.js | 35 ++--- smarttv-client/Javascript/DirectAccess.js | 14 +- smarttv-client/Javascript/Display.js | 8 +- smarttv-client/Javascript/Epg.js | 36 +++++ smarttv-client/Javascript/Helpbar.js | 48 ++++-- smarttv-client/Javascript/Main.js | 124 +++++++++++----- smarttv-client/Javascript/Options.js | 175 ++++++++++++++++++---- smarttv-client/Javascript/OverlayMenu.js | 218 ++++++++++++++++++++++++++++ smarttv-client/Javascript/Player.js | 72 ++++++--- smarttv-client/Javascript/Server.js | 61 +++++++- smarttv-client/config.xml | 3 +- smarttv-client/index.html | 17 ++- 16 files changed, 938 insertions(+), 166 deletions(-) create mode 100644 smarttv-client/Javascript/OverlayMenu.js diff --git a/smarttv-client/CSS/Main.css b/smarttv-client/CSS/Main.css index 70c01d9..7eaae7a 100755 --- a/smarttv-client/CSS/Main.css +++ b/smarttv-client/CSS/Main.css @@ -72,6 +72,11 @@ body { #selectNow{ text-align: left; } + +#selectTitle{ + text-align: right; +} + #selectDisk{ text-align: right; } @@ -245,19 +250,19 @@ body { #logoNow { text-align: left; font-size:18px; - width: 35%; + width: 25%; } #logoTitle { text-align: center; font-size:25px; - width: 30%; + width: 50%; } #logoDisk { text-align: right; font-size:18px; - width: 35%; + width: 25%; } /* Right Half */ #rightHalf { diff --git a/smarttv-client/Javascript/Buttons.js b/smarttv-client/Javascript/Buttons.js index b881975..2e293fd 100644 --- a/smarttv-client/Javascript/Buttons.js +++ b/smarttv-client/Javascript/Buttons.js @@ -31,6 +31,7 @@ Buttons.init = function (){ this.ynButton = new ButtonHandler(); this.ynButton.hndlName = "ynButtons"; this.ynButton.enterCallback = Buttons.ynEnterCallback; + this.ynButton.returnCallback = Buttons.ynReturnCallback; this.ynButton.btnMax = 1; this.ynButton.elmName = "#yn-btn-"; this.ynButton.masterElm = "#yn-buttons"; @@ -41,6 +42,16 @@ Buttons.init = function (){ // $("#prc-buttons-anchor").attr("onkeydown", "Button.prButton.onInput();"); } }; +Buttons.ynReturnCallback = function () { + switch (Main.state) { + case Main.eOPT: + Options.drawServerList(); + break; + default: + break; + } + +}; Buttons.ynEnterCallback = function () { Main.log("Buttons.ynEnterCallback btnSelected= " + Buttons.ynButton.btnSelected); @@ -57,9 +68,14 @@ Buttons.ynEnterCallback = function () { break; case Main.eURLS: Server.deleteUrls(Data.getCurrentItem().childs[Main.selectedVideo].payload.guid); + break; + case Main.eOPT: + Config.deletedFromContext((Options.selectedLine -1)); // 0 is reserved for the input field + Options.drawServerList(); + break; } - + break; } Buttons.ynHide(); @@ -103,6 +119,7 @@ Buttons.show = function () { Buttons.hide = function () { this.prButton.hide(); // $("#prc-buttons-anchor").blur(); + Main.enableKeys(); }; @@ -126,7 +143,10 @@ Buttons.ynShow = function () { Buttons.ynHide = function () { this.ynButton.hide(); - Main.enableKeys(); + if (Main.state == Main.eOPT) + $("#optionsViewAnchor").focus(); + else + Main.enableKeys(); }; Buttons.createStyleSheet = function () { @@ -219,8 +239,16 @@ Buttons.createYnButtons= function () { table.append(tbody); var row = $("
    ", {style :"height:80%; width:100%", colspan:"3" }); + cell.css("align","center"); + var txt_div = $("
    ", {text: "Delete ?"}); + cell.append(txt_div); + row.append(cell); + tbody.append(row); - var cell = $("
    ", {style :"height:80%; width:50%"}); + row = $("
    ", {style :"height:80%; width:50%"}); + cell.css("align","right"); $("