summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/curl.c176
-rw-r--r--lib/dbdict.c158
-rw-r--r--lib/dbdict.h52
-rw-r--r--lib/demo.c279
-rw-r--r--lib/imgtools.c190
-rw-r--r--lib/imgtools.h28
-rw-r--r--lib/test.c458
7 files changed, 1341 insertions, 0 deletions
diff --git a/lib/curl.c b/lib/curl.c
new file mode 100644
index 0000000..6eff24c
--- /dev/null
+++ b/lib/curl.c
@@ -0,0 +1,176 @@
+/*
+ * curl.c:
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <curl/curl.h>
+
+#include "common.h"
+#include "config.h"
+
+//***************************************************************************
+// Callbacks
+//***************************************************************************
+
+static size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data)
+{
+ size_t realsize = size * nmemb;
+ struct MemoryStruct* mem = (struct MemoryStruct*)data;
+
+ if (mem->memory)
+ mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);
+ else
+ mem->memory = (char*)malloc(mem->size + realsize + 1);
+
+ if (mem->memory)
+ {
+ memcpy (&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+
+ return realsize;
+}
+
+static size_t WriteHeaderCallback(void* ptr, size_t size, size_t nmemb, void* data)
+{
+ size_t realsize = size * nmemb;
+ struct MemoryStruct* mem = (struct MemoryStruct*)data;
+ char* p;
+
+ if (ptr)
+ {
+ // get filename
+ {
+ // Content-Disposition: attachment; filename="20140103_20140103_de_qy.zip"
+
+ const char* attribute = "Content-disposition: ";
+
+ if ((p = strcasestr((char*)ptr, attribute)))
+ {
+ if (p = strcasestr(p, "filename="))
+ {
+ p += strlen("filename=");
+
+ tell(4, "found filename at [%s]", p);
+
+ if (*p == '"')
+ p++;
+
+ sprintf(mem->name, "%s", p);
+
+ if ((p = strchr(mem->name, '\n')))
+ *p = 0;
+
+ if ((p = strchr(mem->name, '\r')))
+ *p = 0;
+
+ if ((p = strchr(mem->name, '"')))
+ *p = 0;
+
+ tell(4, "set name to '%s'", mem->name);
+ }
+ }
+ }
+
+ // since some sources update "ETag" an "Last-Modified:" without changing the contents
+ // we have to use "Content-Length:" to check for updates :(
+ {
+ const char* attribute = "Content-Length: ";
+
+ if ((p = strcasestr((char*)ptr, attribute)))
+ {
+ sprintf(mem->tag, "%s", p+strlen(attribute));
+
+ if ((p = strchr(mem->tag, '\n')))
+ *p = 0;
+
+ if ((p = strchr(mem->tag, '\r')))
+ *p = 0;
+
+ if ((p = strchr(mem->tag, '"')))
+ *p = 0;
+ }
+ }
+ }
+
+ return realsize;
+}
+
+//***************************************************************************
+// Download File
+//***************************************************************************
+
+int downloadFile(const char* url, int& size, MemoryStruct* data, int timeout, const char* userAgent)
+{
+ CURL* curl_handle;
+ long code;
+ CURLcode res = CURLE_OK;
+
+ size = 0;
+
+ // init curl
+
+ if (curl_global_init(CURL_GLOBAL_ALL) != 0)
+ {
+ tell(0, "Error, something went wrong with curl_global_init()");
+
+ return fail;
+ }
+
+ curl_handle = curl_easy_init();
+
+ if (!curl_handle)
+ {
+ tell(0, "Error, unable to get handle from curl_easy_init()");
+
+ return fail;
+ }
+
+ if (EPG2VDRConfig.useproxy)
+ {
+ curl_easy_setopt(curl_handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+ curl_easy_setopt(curl_handle, CURLOPT_PROXY, EPG2VDRConfig.httpproxy); // Specify HTTP proxy
+ }
+
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url); // Specify URL to get
+ curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 0); // don't follow redirects
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); // Send all data to this function
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)data); // Pass our 'data' struct to the callback function
+ curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, WriteHeaderCallback); // Send header to this function
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, (void*)data); // Pass some header details to this struct
+ curl_easy_setopt(curl_handle, CURLOPT_MAXFILESIZE, 100*1024*1024); // Set maximum file size to get (bytes)
+ curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1); // No progress meter
+ curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); // No signaling
+ curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, timeout); // Set timeout
+ curl_easy_setopt(curl_handle, CURLOPT_NOBODY, data->headerOnly ? 1 : 0); //
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, userAgent); // Some servers don't like requests
+ // that are made without a user-agent field
+ // perform http-get
+
+ if ((res = curl_easy_perform(curl_handle)) != 0)
+ {
+ data->clear();
+
+ tell(1, "Error, download failed; %s (%d)",
+ curl_easy_strerror(res), res);
+ curl_easy_cleanup(curl_handle); // Cleanup curl stuff
+
+ return fail;
+ }
+
+ curl_easy_getinfo(curl_handle, CURLINFO_HTTP_CODE, &code);
+ curl_easy_cleanup(curl_handle); // cleanup curl stuff
+
+ if (code == 404)
+ {
+ data->clear();
+ return fail;
+ }
+
+ size = data->size;
+
+ return success;
+}
diff --git a/lib/dbdict.c b/lib/dbdict.c
new file mode 100644
index 0000000..0b2211e
--- /dev/null
+++ b/lib/dbdict.c
@@ -0,0 +1,158 @@
+/*
+ * dbdict.c
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "common.h"
+#include "dbdict.h"
+
+//***************************************************************************
+// cDbDict
+//***************************************************************************
+
+cDbDict::cDbDict()
+{
+ inside = no;
+}
+
+cDbDict::~cDbDict()
+{
+}
+
+//***************************************************************************
+// In
+//***************************************************************************
+
+int cDbDict::in(const char* file)
+{
+ FILE* f;
+ char* line = 0;
+ size_t size = 0;
+ char* path;
+
+ asprintf(&path, "%s", file);
+
+ f = fopen(path, "r");
+
+ while (getline(&line, &size, f) > 0)
+ {
+ char* p = strstr(line, "//");
+
+ if (p) *p = 0;
+
+ allTrim(line);
+
+ if (isEmpty(line))
+ continue;
+
+ if (atLine(line) != success)
+ {
+ tell(0, "Found unexpected definition '%s', aborting", line);
+ free(path);
+ return fail;
+ }
+ }
+
+ fclose(f);
+ free(line);
+ free(path);
+
+ return success;
+}
+
+//***************************************************************************
+// At Line
+//***************************************************************************
+
+int cDbDict::atLine(const char* line)
+{
+ const char* p;
+
+ if (p = strcasestr(line, "Table"))
+ {
+ char tableName[100];
+
+ p += strlen("Table");
+ strcpy(tableName, p);
+ tell(0, "Table: '%s'", tableName);
+ }
+
+ else if (strchr(line, '{'))
+ inside = yes;
+
+ else if (strchr(line, '}'))
+ inside = no;
+
+ else if (inside)
+ parseField(line);
+
+ return success;
+}
+
+//***************************************************************************
+// Get Token
+//***************************************************************************
+
+int getToken(const char*& p, char* token, int size)
+{
+ char* dest = token;
+ int num = 0;
+
+ while (*p && *p == ' ')
+ p++;
+
+ while (*p && *p != ' ' && num < size)
+ {
+ if (*p == '"')
+ p++;
+ else
+ {
+ *dest++ = *p++;
+ num++;
+ }
+ }
+
+ *dest = 0;
+
+ return success;
+}
+
+//***************************************************************************
+// Parse Field
+//***************************************************************************
+
+int cDbDict::parseField(const char* line)
+{
+ const int sizeTokenMax = 100;
+ FieldDef f;
+ char token[sizeTokenMax+TB];
+ const char* p = line;
+
+ // tell(0, "Got: '%s'", p);
+
+ for (int i = 0; i < dtCount; i++)
+ {
+ if (getToken(p, token, sizeTokenMax) != success)
+ {
+ tell(0, "Errot: can't parse line [%s]", line);
+ return fail;
+ }
+
+ switch (i)
+ {
+ case dtName: f.name = strdup(token); break;
+ case dtDescription: break;
+ case dtFormat: f.format = toDictFormat(token); break;
+ case dtSize: f.size = atoi(token); break;
+ case dtType: f.type = toType(token); break;
+ }
+
+ free((char*)f.name); // böser cast ...
+
+ tell(0, "token %d -> '%s'", i, token);
+ }
+
+ return success;
+}
diff --git a/lib/dbdict.h b/lib/dbdict.h
new file mode 100644
index 0000000..100c626
--- /dev/null
+++ b/lib/dbdict.h
@@ -0,0 +1,52 @@
+/*
+ * dbdict.h
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef __DBDICT_H
+#define __DBDICT_H
+
+#include "db.h"
+
+//***************************************************************************
+// cDbDict
+//***************************************************************************
+
+class cDbDict : public cDbService
+{
+
+ public:
+
+ // declarations
+
+ enum DictToken
+ {
+ dtName,
+ dtDescription,
+ dtFormat,
+ dtSize,
+ dtType,
+
+ dtCount
+ };
+
+ cDbDict();
+ virtual ~cDbDict();
+
+ int in(const char* file);
+
+ protected:
+
+ int atLine(const char* line);
+ int parseField(const char* line);
+
+ // data
+
+ int inside;
+ static FieldDef fields[];
+
+};
+
+#endif // __DBDICT_H
diff --git a/lib/demo.c b/lib/demo.c
new file mode 100644
index 0000000..ae97cca
--- /dev/null
+++ b/lib/demo.c
@@ -0,0 +1,279 @@
+
+
+#include "config.h"
+#include "common.h"
+
+#include "db.h"
+#include "tabledef.h"
+
+cDbConnection* connection = 0;
+
+//***************************************************************************
+// Init Connection
+//***************************************************************************
+
+void initConnection()
+{
+ cDbConnection::init();
+
+ cDbConnection::setEncoding("utf8");
+ cDbConnection::setHost("localhost");
+
+ cDbConnection::setPort(3306);
+ cDbConnection::setName("epg2vdr");
+ cDbConnection::setUser("epg2vdr");
+ cDbConnection::setPass("epg");
+ cDbTable::setConfPath("/etc/epgd/");
+
+ connection = new cDbConnection();
+}
+
+void exitConnection()
+{
+ cDbConnection::exit();
+
+ if (connection)
+ delete connection;
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+int demoStatement()
+{
+ int status = success;
+
+ cTableEvents* eventsDb = new cTableEvents(connection);
+
+ tell(0, "------------------- attach table ---------------");
+
+ // open table (attach)
+
+ if (eventsDb->open() != success)
+ return fail;
+
+ tell(0, "---------------- prepare select statement -------------");
+
+ // vorbereiten (prepare) eines statement, am besten einmal bei programmstart!
+ // ----------
+ // select eventid, compshorttext, episodepart, episodelang
+ // from events
+ // where eventid > ?
+
+ cDbStatement* selectByCompTitle = new cDbStatement(eventsDb);
+
+ status += selectByCompTitle->build("select ");
+ status += selectByCompTitle->bind(cTableEvents::fiEventId, cDBS::bndOut);
+ status += selectByCompTitle->bind(cTableEvents::fiChannelId, cDBS::bndOut, ", ");
+ status += selectByCompTitle->bind(cTableEvents::fiTitle, cDBS::bndOut, ", ");
+ status += selectByCompTitle->build(" from %s where ", eventsDb->TableName());
+ status += selectByCompTitle->bindCmp(0, cTableEvents::fiEventId, 0, ">");
+
+ status += selectByCompTitle->prepare(); // prepare statement
+
+ if (status != success)
+ {
+ // prepare sollte oracle fehler ausgegeben haben!
+
+ delete eventsDb;
+ delete selectByCompTitle;
+
+ return fail;
+ }
+
+ tell(0, "------------------ prepare done ----------------------");
+
+ tell(0, "------------------ create some rows ----------------------");
+
+ eventsDb->clear(); // alle values löschen
+
+ for (int i = 0; i < 10; i++)
+ {
+ char* title;
+ asprintf(&title, "title %d", i);
+
+ eventsDb->setValue(cTableEvents::fiEventId, 800 + i * 100);
+ eventsDb->setValue(cTableEvents::fiChannelId, "xxx-yyyy-zzz");
+ eventsDb->setValue(cTableEvents::fiTitle, title);
+
+ eventsDb->store(); // store -> select mit anschl. update oder insert je nachdem ob dier PKey bereits vorhanden ist
+ // eventsDb->insert(); // sofern man schon weiß das es ein insert ist
+ // eventsDb->update(); // sofern man schon weiß das der Datensatz vorhanden ist
+
+ free(title);
+ }
+
+ tell(0, "------------------ done ----------------------");
+
+ tell(0, "-------- select all where eventid > 1000 -------------");
+
+ eventsDb->clear(); // alle values löschen
+ eventsDb->setValue(cTableEvents::fiEventId, 1000);
+
+ for (int f = selectByCompTitle->find(); f; f = selectByCompTitle->fetch())
+ {
+ tell(0, "id: %ld", eventsDb->getIntValue(cTableEvents::fiEventId));
+ tell(0, "channel: %s", eventsDb->getStrValue(cTableEvents::fiChannelId));
+ tell(0, "titel: %s", eventsDb->getStrValue(cTableEvents::fiTitle));
+ }
+
+ // freigeben der Ergebnissmenge !!
+
+ selectByCompTitle->freeResult();
+
+ // folgendes am programmende
+
+ delete eventsDb; // implizietes close (detach)
+ delete selectByCompTitle; // statement freigeben (auch gegen die DB)
+
+ return success;
+}
+
+//***************************************************************************
+// Join
+//***************************************************************************
+
+int joinDemo()
+{
+ int status = success;
+
+ // grundsätzlich genügt hier auch eine Tabelle, für die anderen sind cDbValue Instanzen außreichend
+ // so ist es etwas einfacher die cDbValues zu initialisieren.
+ // Ich habe statische "virtual FieldDef* getFieldDef(int f)" Methode in der Tabellenklassen geplant
+ // um ohne Instanz der cTable ein Feld einfach initialisieren zu können
+
+ cTableEvents* eventsDb = new cTableEvents(connection);
+ cTableImageRefs* imageRefDb = new cTableImageRefs(connection);
+ cTableImages* imageDb = new cTableImages(connection);
+
+ tell(0, "------------------- attach table ---------------");
+
+ // open table (attach)
+
+ if (eventsDb->open() != success)
+ return fail;
+
+ if (imageDb->open() != success)
+ return fail;
+
+ if (imageRefDb->open() != success)
+ return fail;
+
+ tell(0, "---------------- prepare select statement -------------");
+
+ // all images
+
+ cDbStatement* selectAllImages = new cDbStatement(imageRefDb);
+
+ // prepare fields
+
+ cDbValue imageUpdSp;
+ cDbValue imageSize;
+ cDbValue masterId;
+
+ cDBS::FieldDef imageSizeDef = { "image", cDBS::ffUInt, 0, 999, cDBS::ftData }; // eine Art ein Feld zu erzeugen
+ imageSize.setField(&imageSizeDef); // eine andere Art ein Feld zu erzeugen ...
+ imageUpdSp.setField(imageDb->getField(cTableImages::fiUpdSp));
+ masterId.setField(eventsDb->getField(cTableEvents::fiMasterId));
+
+ // select e.masterid, r.imagename, r.eventid, r.lfn, length(i.image)
+ // from imagerefs r, images i, events e
+ // where i.imagename = r.imagename
+ // and e.eventid = r.eventid
+ // and (i.updsp > ? or r.updsp > ?)
+
+ selectAllImages->build("select ");
+ selectAllImages->setBindPrefix("e.");
+ selectAllImages->bind(&masterId, cDBS::bndOut);
+ selectAllImages->setBindPrefix("r.");
+ selectAllImages->bind(cTableImageRefs::fiImgName, cDBS::bndOut, ", ");
+ selectAllImages->bind(cTableImageRefs::fiEventId, cDBS::bndOut, ", ");
+ selectAllImages->bind(cTableImageRefs::fiLfn, cDBS::bndOut, ", ");
+ selectAllImages->setBindPrefix("i.");
+ selectAllImages->build(", length(");
+ selectAllImages->bind(&imageSize, cDBS::bndOut);
+ selectAllImages->build(")");
+ selectAllImages->clrBindPrefix();
+ selectAllImages->build(" from %s r, %s i, %s e where ",
+ imageRefDb->TableName(), imageDb->TableName(), eventsDb->TableName());
+ selectAllImages->build("e.%s = r.%s and i.%s = r.%s and (",
+ eventsDb->getField(cTableEvents::fiEventId)->name,
+ imageRefDb->getField(cTableImageRefs::fiEventId)->name,
+ imageDb->getField(cTableImages::fiImgName)->name,
+ imageRefDb->getField(cTableImageRefs::fiImgName)->name);
+ selectAllImages->bindCmp("i", &imageUpdSp, ">");
+ selectAllImages->build(" or ");
+ selectAllImages->bindCmp("r", cTableImageRefs::fiUpdSp, 0, ">");
+ selectAllImages->build(")");
+
+ status += selectAllImages->prepare();
+
+ if (status != success)
+ {
+ // prepare sollte oracle fehler ausgegeben haben!
+
+ delete eventsDb;
+ delete imageDb;
+ delete imageRefDb;
+ delete selectAllImages;
+
+ return fail;
+ }
+
+ tell(0, "------------------ prepare done ----------------------");
+
+ tell(0, "------------------ select ----------------------");
+
+ time_t since = time(0) - 60 * 60;
+ imageRefDb->clear();
+ imageRefDb->setValue(cTableImageRefs::fiUpdSp, since);
+ imageUpdSp.setValue(since);
+
+ for (int res = selectAllImages->find(); res; res = selectAllImages->fetch())
+ {
+ // so kommst du an die Werte der unterschgiedlichen Tabellen
+
+ // int eventid = masterId.getIntValue();
+ // const char* imageName = imageRefDb->getStrValue(cTableImageRefs::fiImgName);
+ // int lfn = imageRefDb->getIntValue(cTableImageRefs::fiLfn);
+ // int size = imageSize.getIntValue();
+
+
+ }
+
+ // freigeben der Ergebnissmenge !!
+
+ selectAllImages->freeResult();
+
+ // folgendes am programmende
+
+ delete eventsDb; // implizietes close (detach)
+ delete imageDb;
+ delete imageRefDb;
+ delete selectAllImages; // statement freigeben (auch gegen die DB)
+
+ return success;
+}
+
+
+//***************************************************************************
+// Main
+//***************************************************************************
+
+int main()
+{
+ EPG2VDRConfig.logstdout = yes;
+ EPG2VDRConfig.loglevel = 2;
+
+ initConnection();
+
+ // demoStatement();
+ // joinDemo();
+
+ tell(0, "uuid: '%s'", getUniqueId());
+
+ exitConnection();
+
+ return 0;
+}
diff --git a/lib/imgtools.c b/lib/imgtools.c
new file mode 100644
index 0000000..168bac9
--- /dev/null
+++ b/lib/imgtools.c
@@ -0,0 +1,190 @@
+/*
+ * imgtools.c
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "imgtools.h"
+
+//***************************************************************************
+// Image converting stuff
+//***************************************************************************
+
+int fromJpeg(Imlib_Image& image, unsigned char* buffer, int size)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ int w, h;
+ DATA8 *ptr, *line[16], *data;
+ DATA32 *ptr2, *dest;
+ int x, y;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jpeg_create_decompress(&cinfo);
+ jpeg_mem_src(&cinfo, buffer, size);
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.do_fancy_upsampling = FALSE;
+ cinfo.do_block_smoothing = FALSE;
+
+ jpeg_start_decompress(&cinfo);
+
+ w = cinfo.output_width;
+ h = cinfo.output_height;
+
+ image = imlib_create_image(w, h);
+ imlib_context_set_image(image);
+
+ dest = ptr2 = imlib_image_get_data();
+ data = (DATA8*)malloc(w * 16 * cinfo.output_components);
+
+ for (int i = 0; i < cinfo.rec_outbuf_height; i++)
+ line[i] = data + (i * w * cinfo.output_components);
+
+ for (int l = 0; l < h; l += cinfo.rec_outbuf_height)
+ {
+ jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
+ int scans = cinfo.rec_outbuf_height;
+
+ if (h - l < scans)
+ scans = h - l;
+
+ ptr = data;
+
+ for (y = 0; y < scans; y++)
+ {
+ for (x = 0; x < w; x++)
+ {
+ *ptr2 = (0xff000000) | ((ptr[0]) << 16) | ((ptr[1]) << 8) | (ptr[2]);
+ ptr += cinfo.output_components;
+ ptr2++;
+ }
+ }
+ }
+
+ free(data);
+
+ imlib_image_put_back_data(dest);
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ return success;
+}
+
+long toJpeg(Imlib_Image image, MemoryStruct* data, int quality)
+{
+ struct jpeg_compress_struct cinfo = { 0 };
+ struct jpeg_error_mgr jerr;
+ DATA32* ptr;
+ DATA8* buf;
+ long unsigned int size = data->size;
+
+ imlib_context_set_image(image);
+
+ data->clear();
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jpeg_create_compress(&cinfo);
+ jpeg_mem_dest(&cinfo, (unsigned char**)(&data->memory), &size);
+
+ cinfo.image_width = imlib_image_get_width();
+ cinfo.image_height = imlib_image_get_height();
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, TRUE);
+ jpeg_start_compress(&cinfo, TRUE);
+
+ // get data pointer
+
+ if (!(ptr = imlib_image_get_data_for_reading_only()))
+ return 0;
+
+ // allocate a small buffer to convert image data */
+
+ buf = (DATA8*)malloc(imlib_image_get_width() * 3 * sizeof(DATA8));
+
+ while (cinfo.next_scanline < cinfo.image_height)
+ {
+ // convert scanline from ARGB to RGB packed
+
+ for (int j = 0, i = 0; i < imlib_image_get_width(); i++)
+ {
+ buf[j++] = ((*ptr) >> 16) & 0xff;
+ buf[j++] = ((*ptr) >> 8) & 0xff;
+ buf[j++] = ((*ptr)) & 0xff;
+
+ ptr++;
+ }
+
+ // write scanline
+
+ jpeg_write_scanlines(&cinfo, (JSAMPROW*)(&buf), 1);
+ }
+
+ free(buf);
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ return data->size;
+}
+
+int scaleImageToJpegBuffer(Imlib_Image image, MemoryStruct* data, int width, int height)
+{
+ if (width && height)
+ {
+ Imlib_Image scaledImage;
+
+ imlib_context_set_image(image);
+
+ int imgWidth = imlib_image_get_width();
+ int imgHeight = imlib_image_get_height();
+ double ratio = (double)imgWidth / (double)imgHeight;
+
+ if ((double)width/(double)imgWidth < (double)height/(double)imgHeight)
+ height = (int)((double)width / ratio);
+ else
+ width = (int)((double)height * ratio);
+
+ scaledImage = imlib_create_image(width, height);
+ imlib_context_set_image(scaledImage);
+
+ imlib_context_set_color(240, 240, 240, 255);
+ imlib_image_fill_rectangle(0, 0, width, height);
+
+ imlib_blend_image_onto_image(image, 0, 0, 0,
+ imgWidth, imgHeight, 0, 0,
+ width, height);
+
+ toJpeg(scaledImage, data, 70);
+
+ imlib_context_set_image(scaledImage);
+ imlib_free_image();
+
+ tell(1, "Scaled image to %d/%d, now %d bytes", width, height, (int)data->size);
+ }
+ else
+ {
+ toJpeg(image, data, 70);
+ }
+
+ return success;
+}
+
+int scaleJpegBuffer(MemoryStruct* data, int width, int height)
+{
+ Imlib_Image image;
+
+ fromJpeg(image, (unsigned char*)data->memory, data->size);
+
+ scaleImageToJpegBuffer(image, data, width, height);
+
+ imlib_context_set_image(image);
+ imlib_free_image();
+
+ return success;
+}
diff --git a/lib/imgtools.h b/lib/imgtools.h
new file mode 100644
index 0000000..c4f8ab7
--- /dev/null
+++ b/lib/imgtools.h
@@ -0,0 +1,28 @@
+/*
+ * imgtools.c
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef __IMGTOOLS_H
+#define __IMGTOOLS_H
+
+#include <stdio.h>
+
+#include <jpeglib.h>
+#include <Imlib2.h>
+
+#include "common.h"
+
+//***************************************************************************
+// Image Manipulating
+//***************************************************************************
+
+int fromJpeg(Imlib_Image& image, unsigned char* buffer, int size);
+long toJpeg(Imlib_Image image, MemoryStruct* data, int quality);
+int scaleImageToJpegBuffer(Imlib_Image image, MemoryStruct* data, int width = 0, int height = 0);
+int scaleJpegBuffer(MemoryStruct* data, int width = 0, int height = 0);
+
+//***************************************************************************
+#endif // __IMGTOOLS_H
diff --git a/lib/test.c b/lib/test.c
new file mode 100644
index 0000000..01b5c2d
--- /dev/null
+++ b/lib/test.c
@@ -0,0 +1,458 @@
+
+#include <stdint.h> // uint_64_t
+#include <sys/time.h>
+#include <time.h>
+
+#include <stdio.h>
+#include <string>
+
+#include "config.h"
+#include "common.h"
+#include "db.h"
+#include "tabledef.h"
+#include "dbdict.h"
+
+cDbConnection* connection = 0;
+
+//***************************************************************************
+//
+//***************************************************************************
+
+class cTimeMs
+{
+ private:
+
+ uint64_t begin;
+
+ public:
+
+ cTimeMs(int Ms = 0);
+ static uint64_t Now(void);
+ void Set(int Ms = 0);
+ bool TimedOut(void);
+ uint64_t Elapsed(void);
+};
+
+//***************************************************************************
+// Init Connection
+//***************************************************************************
+
+void initConnection()
+{
+ cDbConnection::init();
+
+ cDbConnection::setEncoding("utf8");
+ cDbConnection::setHost("localhost");
+ cDbConnection::setPort(EPG2VDRConfig.dbPort);
+ cDbConnection::setName(EPG2VDRConfig.dbName);
+ cDbConnection::setUser(EPG2VDRConfig.dbUser);
+ cDbConnection::setPass(EPG2VDRConfig.dbPass);
+ cDbTable::setConfPath("/etc/epgd/");
+
+ connection = new cDbConnection();
+}
+
+void exitConnection()
+{
+ cDbConnection::exit();
+
+ if (connection)
+ delete connection;
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+void chkCompress()
+{
+ std::string s = "_+*!#?=&%$< Hallo TEIL Hallo Folge ";
+
+ printf("'%s'\n", s.c_str());
+ prepareCompressed(s);
+ printf("'%s'\n", s.c_str());
+
+ s = "Place Vendôme - Heiße Diamanten";
+ printf("'%s'\n", s.c_str());
+ prepareCompressed(s);
+ printf("'%s'\n", s.c_str());
+
+ s = "Halöö älter";
+ printf("'%s'\n", s.c_str());
+ prepareCompressed(s);
+ printf("'%s'\n", s.c_str());
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+void chkStatement1()
+{
+ cDbTable* epgDb = new cTableEvents(connection);
+
+ if (epgDb->open() != success)
+ {
+ tell(0, "Could not access database '%s:%d' (%s)",
+ cDbConnection::getHost(), cDbConnection::getPort(), epgDb->TableName());
+
+ return ;
+ }
+
+ tell(0, "---------------------------------------------------");
+
+ // prepare statement to mark wasted DVB events
+
+ cDbValue* endTime = new cDbValue("starttime+duration", cDBS::ffInt, 10);
+ cDbStatement* updateDelFlg = new cDbStatement(epgDb);
+
+ // update events set delflg = ?, updsp = ?
+ // where channelid = ? and source = ?
+ // and starttime+duration > ?
+ // and starttime < ?
+ // and (tableid > ? or (tableid = ? and version <> ?))
+
+ updateDelFlg->build("update %s set ", epgDb->TableName());
+ updateDelFlg->bind(cTableEvents::fiDelFlg, cDBS::bndIn | cDBS::bndSet);
+ updateDelFlg->bind(cTableEvents::fiUpdSp, cDBS::bndIn | cDBS::bndSet, ", ");
+ updateDelFlg->build(" where ");
+ updateDelFlg->bind(cTableEvents::fiChannelId, cDBS::bndIn | cDBS::bndSet);
+ updateDelFlg->bind(cTableEvents::fiSource, cDBS::bndIn | cDBS::bndSet, " and ");
+
+ updateDelFlg->bindCmp(0, endTime, ">", " and ");
+
+ updateDelFlg->bindCmp(0, cTableEvents::fiStartTime, 0, "<" , " and ");
+ updateDelFlg->bindCmp(0, cTableEvents::fiTableId, 0, ">" , " and (");
+ updateDelFlg->bindCmp(0, cTableEvents::fiTableId, 0, "=" , " or (");
+ updateDelFlg->bindCmp(0, cTableEvents::fiVersion, 0, "<>" , " and ");
+ updateDelFlg->build("));");
+
+ updateDelFlg->prepare();
+
+ tell(0, "---------------------------------------------------");
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+void chkStatement2()
+{
+ cDbTable* imageRefDb = new cTableImageRefs(connection);
+ cDbTable* imageDb = new cTableImages(connection);
+
+ if (imageRefDb->open() != success)
+ return ;
+
+ if (imageDb->open() != success)
+ return ;
+
+ tell(0, "---------------------------------------------------");
+
+ cDbStatement* selectAllImages = new cDbStatement(imageRefDb);
+
+ cDbValue imageData;
+ imageData.setField(imageDb->getField(cTableImages::fiImage));
+
+ // select r.imagename, r.eventid, r.lfn, i.image from imagerefs r, images i
+ // where r.imagename = i.imagename and i.image is not null;
+
+ selectAllImages->build("select ");
+ selectAllImages->setBindPrefix("r.");
+ selectAllImages->bind(cTableImageRefs::fiImgName, cDBS::bndOut);
+ selectAllImages->bind(cTableImageRefs::fiEventId, cDBS::bndOut, ", ");
+ selectAllImages->bind(cTableImageRefs::fiLfn, cDBS::bndOut, ", ");
+ selectAllImages->setBindPrefix("i.");
+ selectAllImages->bind(&imageData, cDBS::bndOut, ",");
+ selectAllImages->clrBindPrefix();
+ selectAllImages->build(" from %s r, %s i where ", imageRefDb->TableName(), imageDb->TableName());
+ selectAllImages->build("r.%s = i.%s and i.%s is not null;",
+ imageRefDb->getField(cTableImageRefs::fiImgName)->name,
+ imageDb->getField(cTableImages::fiImgName)->name,
+ imageDb->getField(cTableImages::fiImage)->name);
+
+ selectAllImages->prepare();
+
+
+ tell(0, "---------------------------------------------------");
+
+ //delete s;
+ delete imageRefDb;
+ delete imageDb;
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+void chkStatement3()
+{
+ int count = 0;
+ int lcount = 0;
+
+ cDbTable* epgDb = new cTableEvents(connection);
+ cDbTable* mapDb = new cTableChannelMap(connection);
+
+ if (epgDb->open() != success)
+ return ;
+
+ if (mapDb->open() != success)
+ return ;
+
+ tell(0, "---------------------------------------------------");
+
+ cDbStatement* s = new cDbStatement(epgDb);
+
+ s->build("select ");
+ s->setBindPrefix("e.");
+ s->bind(cTableEvents::fiEventId, cDBS::bndOut);
+ s->bind(cTableEvents::fiChannelId, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiSource, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiDelFlg, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiFileRef, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiTableId, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiVersion, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiTitle, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiShortText, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiStartTime, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiDuration, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiParentalRating, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiVps, cDBS::bndOut, ", ");
+ s->bind(cTableEvents::fiDescription, cDBS::bndOut, ", ");
+ s->clrBindPrefix();
+ s->build(" from eventsview e, %s m where ", mapDb->TableName());
+ s->build("e.%s = m.%s and e.%s = m.%s and ",
+ epgDb->getField(cTableEvents::fiChannelId)->name,
+ mapDb->getField(cTableChannelMap::fiChannelName)->name,
+ epgDb->getField(cTableEvents::fiSource)->name,
+ mapDb->getField(cTableChannelMap::fiSource)->name);
+ s->bindCmp("e", cTableEvents::fiUpdSp, 0, ">");
+ s->build(" order by m.%s;", mapDb->getField(cTableChannelMap::fiChannelName)->name);
+
+ s->prepare();
+
+ epgDb->clear();
+ epgDb->setValue(cTableEvents::fiUpdSp, (double)0);
+ epgDb->setValue(cTableEvents::fiSource, "vdr"); // used by selectUpdEventsByChannel
+ epgDb->setValue(cTableEvents::fiChannelId, "xxxxxxxxxxxxx"); // used by selectUpdEventsByChannel
+
+ int channels = 0;
+ char chan[100]; *chan = 0;
+
+ tell(0, "---------------------------------------------------");
+
+ for (int found = s->find(); found; found = s->fetch())
+ {
+ if (!*chan || strcmp(chan, epgDb->getStrValue(cTableEvents::fiChannelId)) != 0)
+ {
+ if (*chan)
+ tell(0, "processed %-20s with %d events", chan, count - lcount);
+
+ lcount = count;
+ channels++;
+ strcpy(chan, epgDb->getStrValue(cTableEvents::fiChannelId));
+
+ tell(0, "processing %-20s now", chan);
+ }
+
+ tell(0, "-> '%s' - (%ld)", epgDb->getStrValue(cTableEvents::fiChannelId),
+ epgDb->getIntValue(cTableEvents::fiEventId));
+
+
+ count++;
+ }
+
+ s->freeResult();
+
+ tell(0, "---------------------------------------------------");
+ tell(0, "updated %d channels and %d events", channels, count);
+ tell(0, "---------------------------------------------------");
+
+ delete s;
+ delete epgDb;
+ delete mapDb;
+}
+
+// --- cTimeMs ---------------------------------------------------------------
+
+cTimeMs::cTimeMs(int Ms)
+{
+ if (Ms >= 0)
+ Set(Ms);
+ else
+ begin = 0;
+}
+
+uint64_t cTimeMs::Now(void)
+{
+#define MIN_RESOLUTION 5 // ms
+ static bool initialized = false;
+ static bool monotonic = false;
+ struct timespec tp;
+ if (!initialized) {
+ // check if monotonic timer is available and provides enough accurate resolution:
+ if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) {
+ // long Resolution = tp.tv_nsec;
+ // require a minimum resolution:
+ if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) {
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+ monotonic = true;
+ }
+ else
+ tell(0, "cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
+ }
+ else
+ tell(0, "cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)", tp.tv_sec, tp.tv_nsec);
+ }
+ else
+ tell(0, "cTimeMs: clock_getres(CLOCK_MONOTONIC) failed");
+ initialized = true;
+ }
+ if (monotonic) {
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
+ return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
+ tell(0, "cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
+ monotonic = false;
+ // fall back to gettimeofday()
+ }
+ struct timeval t;
+ if (gettimeofday(&t, NULL) == 0)
+ return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
+ return 0;
+}
+
+void cTimeMs::Set(int Ms)
+{
+ begin = Now() + Ms;
+}
+
+bool cTimeMs::TimedOut(void)
+{
+ return Now() >= begin;
+}
+
+uint64_t cTimeMs::Elapsed(void)
+{
+ return Now() - begin;
+}
+
+//***************************************************************************
+//
+//***************************************************************************
+
+void chkStatement4()
+{
+ cDbTable* eventDb = new cTableEvents(connection);
+ if (eventDb->open() != success) return;
+
+ cDbTable* imageRefDb = new cTableImageRefs(connection);
+ if (imageRefDb->open() != success) return;
+
+ cDbTable* imageDb = new cTableImages(connection);
+ if (imageDb->open() != success) return;
+
+ // select e.masterid, r.imagename, r.eventid, r.lfn, i.image
+ // from imagerefs r, images i, events e
+ // where r.imagename = i.imagename
+ // and e.eventid = r.eventid,
+ // and i.image is not null
+ // and (i.updsp > ? or r.updsp > ?);
+
+ cDBS::FieldDef masterFld = { "masterid", cDBS::ffUInt, 0, 999, cDBS::ftData };
+ cDbValue masterId;
+ cDbValue imageData;
+ cDbValue imageUpdSp;
+
+ masterId.setField(&masterFld);
+ imageData.setField(imageDb->getField(cTableImages::fiImage));
+ imageUpdSp.setField(imageDb->getField(cTableImages::fiUpdSp));
+
+ cDbStatement* selectAllImages = new cDbStatement(imageRefDb);
+
+ selectAllImages->build("select ");
+ selectAllImages->setBindPrefix("e.");
+ selectAllImages->bind(&masterId, cDBS::bndOut);
+ selectAllImages->setBindPrefix("r.");
+ selectAllImages->bind(cTableImageRefs::fiImgName, cDBS::bndOut, ", ");
+ selectAllImages->bind(cTableImageRefs::fiEventId, cDBS::bndOut, ", ");
+ selectAllImages->bind(cTableImageRefs::fiLfn, cDBS::bndOut, ", ");
+ selectAllImages->setBindPrefix("i.");
+ selectAllImages->bind(&imageData, cDBS::bndOut, ", ");
+ selectAllImages->clrBindPrefix();
+ selectAllImages->build(" from %s r, %s i, %s e where ",
+ imageRefDb->TableName(), imageDb->TableName(), eventDb->TableName());
+ selectAllImages->build("e.%s = r.%s and i.%s = r.%s and i.%s is not null and (",
+ eventDb->getField(cTableEvents::fiEventId)->name,
+ imageRefDb->getField(cTableImageRefs::fiEventId)->name,
+ imageDb->getField(cTableImageRefs::fiImgName)->name,
+ imageRefDb->getField(cTableImageRefs::fiImgName)->name,
+ imageDb->getField(cTableImages::fiImage)->name);
+ selectAllImages->bindCmp("i", &imageUpdSp, ">");
+ selectAllImages->build(" or ");
+ selectAllImages->bindCmp("r", cTableImageRefs::fiUpdSp, 0, ">");
+ selectAllImages->build(");");
+
+ selectAllImages->prepare();
+
+ imageRefDb->clear();
+ imageRefDb->setValue(cTableImageRefs::fiUpdSp, 1377733333L);
+ imageUpdSp.setValue(1377733333L);
+
+ int count = 0;
+ for (int res = selectAllImages->find(); res; res = selectAllImages->fetch())
+ {
+ count ++;
+ }
+ tell(0,"%d", count);
+}
+
+//***************************************************************************
+// Main
+//***************************************************************************
+
+int main()
+{
+ EPG2VDRConfig.logstdout = yes;
+ EPG2VDRConfig.loglevel = 2;
+
+ setlocale(LC_CTYPE, "");
+ char* lang = setlocale(LC_CTYPE, 0);
+
+ if (lang)
+ {
+ tell(0, "Set locale to '%s'", lang);
+
+ if ((strcasestr(lang, "UTF-8") != 0) || (strcasestr(lang, "UTF8") != 0))
+ tell(0, "detected UTF-8");
+ else
+ tell(0, "no UTF-8");
+ }
+ else
+ {
+ tell(0, "Reseting locale for LC_CTYPE failed.");
+ }
+
+
+ cDbDict* dict = new cDbDict();
+
+ dict->in("../epg.dat");
+
+ delete dict;
+
+ return 0;
+
+ initConnection();
+
+ chkCompress();
+
+ tell(0, "duration was: '%s'", ms2Dur(2340).c_str());
+
+ // chkStatement1();
+ // chkStatement2();
+ // chkStatement3();
+ // chkStatement4();
+
+ exitConnection();
+
+ return 0;
+}