[vdr-plugin-epg2vdr] 02/08: New upstream version 1.1.81

Tobias Grimm tiber-guest at moszumanska.debian.org
Sun Feb 4 14:53:21 UTC 2018


This is an automated email from the git hooks/post-receive script.

tiber-guest pushed a commit to branch master
in repository vdr-plugin-epg2vdr.

commit b536b3be8ccd000495953020045fa99ceb483d17
Author: Tobias Grimm <git at e-tobi.net>
Date:   Sun Feb 4 15:38:54 2018 +0100

    New upstream version 1.1.81
---
 HISTORY.h                                          | 202 ++++++++-
 Make.config                                        |  35 +-
 Makefile                                           |  37 +-
 README                                             |   3 +-
 TODO                                               |   3 +-
 configs/epg.dat                                    |  25 +-
 epg2vdr.c                                          | 225 ++++++++--
 epg2vdr.h                                          |  21 +-
 handler.h                                          | 329 ++++++++------
 lib/Makefile                                       |  32 +-
 lib/common.c                                       |   4 +-
 lib/common.h                                       |   3 +-
 lib/db.c                                           |  22 +-
 lib/db.h                                           |  11 +-
 lib/demo.c                                         |  94 ++--
 lib/epgservice.c                                   |   1 +
 lib/epgservice.h                                   |  18 +-
 lib/python.c                                       |  40 +-
 lib/python.h                                       |   6 +-
 lib/searchtimer.c                                  |  76 ++--
 lib/searchtimer.h                                  |  14 +-
 lib/test.c                                         | 148 +++---
 lib/vdrlocks.h                                     |  78 ++++
 lib/xml.c                                          |  99 +++++
 lib/xml.h                                          |  56 +++
 menu.c                                             | 128 +++++-
 menu.h                                             |  77 +++-
 menudone.c                                         |  14 +-
 menusched.c                                        | 331 +++++++++-----
 menusearchtimer.c                                  |  69 ++-
 menutimers.c                                       |  37 +-
 parameters.c                                       |  13 +-
 ...re-vdr-2.1.x--epghandler-segment-transfer.patch |  65 ---
 patches/vdr-1.7.27-to-epghandler-of-1.7.31.patch   | 219 ---------
 patches/vdr-1.7.28-epghandledexternally.diff       | 118 -----
 patches/vdr-1.7.29-epgIsUpdate.diff                |  52 ---
 patches/vdr-2.2.0-aux.patch                        |  79 ++++
 patches/vdr-2.3.2-aux.patch                        |  79 ++++
 patches/vdr-2.3.2.patch                            |  56 +++
 plgconfig.c                                        |   5 +-
 plgconfig.h                                        |   5 +-
 po/de_DE.po                                        |  14 +-
 po/it_IT.po                                        |  14 +-
 recinfofile.c                                      | 109 +++--
 recording.c                                        |  87 ++--
 service.c                                          |  30 +-
 service.h                                          | 101 ++---
 status.c                                           | 232 +++++-----
 svdrpclient.c                                      |   8 +-
 svdrpclient.h                                      |   7 +-
 timer.c                                            |  94 ++--
 ttools.c                                           |  53 ++-
 update.c                                           | 494 +++++++++++++++------
 update.h                                           |  35 +-
 54 files changed, 2674 insertions(+), 1533 deletions(-)

diff --git a/HISTORY.h b/HISTORY.h
index 27f51e4..385a819 100644
--- a/HISTORY.h
+++ b/HISTORY.h
@@ -5,10 +5,10 @@
  *
  */
 
-#define _VERSION     "1.1.19"
-#define VERSION_DATE "30.11.2016"
+#define _VERSION     "1.1.81"
+#define VERSION_DATE "29.01.2018"
 
-#define DB_API       4
+#define DB_API       5
 
 #ifdef GIT_REV
 #  define VERSION _VERSION "-GIT" GIT_REV
@@ -19,6 +19,200 @@
 /*
  * ------------------------------------
 
+2018-01-29 version 1.1.81 (horchi)
+   - bugfix: Fixed possible crash on recording update without longdescription
+   - change: More readable error message on DBAPI mismatch (thx to Lars!)
+
+2018-01-27 version 1.1.80 (horchi)
+   - added: Service to check if event has a timer
+
+2018-01-24 version 1.1.79 (horchi)
+   - change: minor changes, fixes and code cleanup
+
+2017-12-22 version 1.1.78 (horchi)
+   - change: update of recording description handling
+
+2017-12-22 version 1.1.77 (horchi)
+   - change: backward compatibility to vdr 2.2.0 - another step
+
+2017-12-21 version 1.1.76 (horchi)
+   - change: backward compatibility to vdr 2.2.0 - second try
+   - change: g++ 7 porting
+
+2017-12-21 version 1.1.75 (horchi)
+   - change: backward compatibility to vdr 2.2.0
+
+2017-12-19 version 1.1.74 (horchi)
+   - added: recording detail query to service interface
+
+2017-06-23 version 1.1.73 (horchi)
+   - bugfix: Fixed compile with VDR 2.2.0
+   - bugfix: Fixed problem with unknown channels
+
+2017-06-22 version 1.1.72 (horchi)
+   - bugfix: Fixed crash on end of recording
+
+2017-06-22 version 1.1.71 (horchi)
+   - bugfix: Fixed crash on recording
+
+2017-06-22 version 1.1.70 (horchi)
+   - change: Fixed possible crash with unknown channels on wrong configuration
+
+2017-06-22 version 1.1.69 (horchi)
+   - change: More rework of lock handling
+
+2017-06-11: version 1.1.68 (horchi)
+   - change: Added lock macros for easier handling the vdr versions
+
+2017-06-11: version 1.1.67 (horchi)
+   - change: Porting to VDR 2.3.7
+
+2017-06-10: version 1.1.66 (horchi)
+   - Bugfix: Fixed vdr 2.2.0 compile problem (thx to Alexander Grothe)
+
+2017-06-09: version 1.1.65 (horchi)
+   - Bugfix: Fixed another lock sequence
+
+2017-06-09: version 1.1.64 (horchi)
+   - Bugfix: Fixed lock sequence
+
+2017-06-08 version 1.1.63 (horchi)
+   - change: Improved aux field
+   - change: Suppress inactive timers on service interface
+
+2017-06-02 version 1.1.62 (horchi)
+   - change: Minor sorting change
+
+2017-05-22 version 1.1.61 (horchi)
+   - bugfix: Fixed aux handling
+
+2017-05-07 version 1.1.60 (horchi)
+   - bugfix: Fixed possible crash on channel lock
+
+2017-05-05 version 1.1.59 (horchi)
+   - bugfix: Fixed crash on missing channels
+
+2017-05-03 version 1.1.58 (horchi)
+   - change: Fill timersdone table even for timers created on OSD
+
+2017-05-02 version 1.1.57 (horchi)
+   - bugfix: Fixed possible problem with epg for new channels since vdr 2.3.x
+
+2017-03-24 version 1.1.56 (horchi)
+   - bugfix: Fixed problem with service interface (frequent db reconnects)
+
+2017-03-24 version 1.1.55 (horchi)
+   - change: Fixed default make option
+
+2017-03-23 version 1.1.54 (horchi)
+   - added: New event plugin interface
+
+2017-03-22 version 1.1.53 (horchi)
+   - change: Removed old patches for vdr < 2.2.0
+   - added: Patch to extend cEvent with aux field like cTimer
+   - change: Moved user defines from Makefile to Make.config
+
+2017-03-21 version 1.1.52 (horchi)
+   - bugfix: crash in EPG menu
+
+2017-03-20: version 1.1.51 (horchi)
+   - change: Removed compiler warnings when using clang
+   - added:  Added clang++ to Make.config (as optional compiler)
+   - change: Fixed APIVERSION check for VDR < 2.2.0 (thx to nobanzai)
+
+2017-03-19: version 1.1.50 (horchi)
+   - bugfix: Fixed AMC address lookup
+
+2017-03-19: version 1.1.49 (horchi)
+   - bugfix: Fixed possible crash in extended skins interface
+
+2017-03-16: version 1.1.48 (horchi)
+   - added: Further improvement of extended skins interface
+
+2017-03-14: version 1.1.47 (horchi)
+   - added: Started extended event interface for skins
+
+2017-03-06: version 1.1.46 (horchi)
+   - added: Added column for future features to vdrs table
+
+2017-03-06: version 1.1.45 (horchi)
+   - change: Fixed order of push/tag in Makefile
+
+2017-03-06: version 1.1.44 (horchi)
+   - change: Improved Makefile (Thx to magicamun)
+
+2017-03-01: version 1.1.43 (horchi)
+   - bugfix: Fixed crash in meues without database connection
+
+2017-02-27: version 1.1.41 (horchi)
+   - bugfix: Fixed lookup of repeating events
+
+2017-02-27: version 1.1.40 (horchi)
+   - change: minor changes (logging, ...)
+
+2017-02-24: version 1.1.39 (horchi)
+   - change: modified null field handling for timersdonedb
+
+2017-02-14: version 1.1.38 (horchi)
+   - bugfix: fixed compile bug
+
+2017-02-14: version 1.1.37 (horchi)
+   - bugfix: fixed detection if recording is complete or not
+
+2017-02-14: version 1.1.36 (horchi)
+   - added: command menu for schedules (key 0)
+
+2017-02-14: version 1.1.35 (horchi)
+   - change: modified timer delete/reject handling
+
+2017-02-10: version 1.1.34 (horchi)
+   - bugfix: Fixed possible crash at timer update
+
+2017-02-09: version 1.1.33 (horchi)
+   - added: Added epg handler patch for VDR 2.3.2
+            -> enhancement of epg handler interface to fix a threading
+               problem which can occur on systems with more than one tuner
+
+2017-02-08: version 1.1.32 (horchi)
+   - bugfix: first program menu item now also selectable
+
+2017-02-08: version 1.1.31 (horchi)
+   - added: Added patch for VDR >= 2.3.1
+            Important, without this patch it will not work!
+
+2017-02-06: version 1.1.30 (horchi)
+   - bugfix: fix of '1.1.28' next try ;)
+
+2017-02-06: version 1.1.29 (horchi)
+   - bugfix: fixed compile issue with gcc 6.2.0
+
+2017-02-06: version 1.1.28 (horchi)
+   - bugfix: fixed sql error in epg search menu
+
+2017-02-01: version 1.1.27 (horchi)
+   - bugfix: fixed timer naming problem
+
+2017-01-19: version 1.1.26 (horchi)
+   - bugfix: Fixed buf with cyclic db reconnect
+
+2017-01-19: version 1.1.25 (horchi)
+   - added: support of namingmode 'template' by epgd/epghttpd
+
+2017-01-16: version 1.1.24 (horchi)
+   - change: Finished porting to VDR 2.3.2
+
+2017-01-08: version 1.1.23 (horchi)
+   - bugfix: fixed schedules lock problem for vdr < 2.3.x
+
+2017-01-08: version 1.1.22 (horchi)
+   - change: fixed problem with 2.3.1 / 2.3.2
+
+2017-01-07: version 1.1.21 (horchi)
+   - change: now compiles with vdr 2.3.1 / 2.3.2
+
+2017-01-03: version 1.1.20 (horchi)
+   - bugfix: fixed reinstate of events in epg handler
+
 2016-11-30: version 1.1.19 (horchi)
    - change: merging of lib
 
@@ -150,7 +344,7 @@
    - bugfix: added missing table init
 
 2016-04-23: version 1.0.27 (horchi)
-   - change: added totoal count dor service interface (ForEachTimer)
+   - change: added totoal count for service interface (ForEachTimer)
 
 2016-04-22: version 1.0.26 (horchi)
    - bugfix: fixed set of scrnew on info.epg2vdr changes
diff --git a/Make.config b/Make.config
index 127756c..85024f4 100644
--- a/Make.config
+++ b/Make.config
@@ -7,14 +7,27 @@
 
 # user defined stuff
 
-PREFIX   = /usr/local
+PREFIX = /usr/local
+
+# enable AUX patch, in VDR >= 2.3.4 not needed!
+#WITH_AUX_PATCH = 1
+
+# enable graphtftng and/or pin plugin support if autodetection below don't work
+#WITH_GTFT = 1
+#WITH_PIN  = 1
 
 DEBUG = 1
+#USE_CLANG = 1
 
 # -----------------------
 # don't touch below ;)
 
-CC        = g++
+ifdef USE_CLANG
+  CC = clang++
+else
+  CC = g++
+endif
+
 doCompile = $(CC) -c $(CFLAGS) $(DEFINES)
 doLink    = $(CC) $(LFLAGS)
 doLib     = ar -rs
@@ -28,12 +41,20 @@ ifdef DEBUG
   CXXFLAGS += -ggdb -O0
 endif
 
-CXXFLAGS += -fPIC -Wreturn-type -Wall -Wno-parentheses -Wformat -pedantic \
-            -Wno-long-long -Wunused-variable -Wunused-label -Wno-unused-result \
-            -Wunused-value -Wunused-but-set-variable -Wunused-function \
-            -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+CXXFLAGS   += -fPIC -Wreturn-type -Wall -Wno-parentheses -Wformat -pedantic \
+              -Wno-long-long -Wunused-variable -Wunused-label -Wno-unused-result \
+              -Wunused-value -Wunused-function -Wno-variadic-macros \
+              -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+CXXFLAGS   += -std=c++11 -D__STDC_FORMAT_MACROS
+
+ifdef USE_CLANG
+  CXXFLAGS += -x c++ -Wunused-const-variable
+else
+  CXXFLAGS += -Wunused-but-set-variable
+endif
 
-CFLAGS += $(CXXFLAGS)
+DEFINES    += -D__STL_CONFIG_H
+CFLAGS     += $(CXXFLAGS)
 
 %.o: %.c
 	$(doCompile) -o $@ $<
diff --git a/Makefile b/Makefile
index 66cd1cb..7036cc6 100644
--- a/Makefile
+++ b/Makefile
@@ -3,15 +3,11 @@
 #
 
 PLUGIN    = epg2vdr
-HLIB      = -L./lib -lhorchi 
+HLIB      = -L./lib -lhorchi
 HISTFILE  = "HISTORY.h"
 
 include Make.config
 
-# enable graphtftng and/or pin plugin support if autodetection below don't work
-#WITH_GTFT = 1
-#WITH_PIN  = 1
-
 ### The version number of this plugin (taken from the main source file):
 
 VERSION = $(shell grep 'define _VERSION ' $(HISTFILE) | awk '{ print $$3 }' | sed -e 's/[";]//g')
@@ -71,18 +67,24 @@ OBJS = $(PLUGIN).o \
        menu.o menusched.o menutimers.o menudone.o menusearchtimer.o
 
 LIBS  = $(HLIB)
-LIBS += -lrt -larchive -lcrypto -luuid
-LIBS += $(shell mysql_config --libs_r) $(shell python-config --libs) $(shell pkg-config --cflags --libs jansson)
+LIBS += -lrt -larchive -lcrypto
+LIBS += $(shell pkg-config --libs uuid)
+LIBS += $(shell pkg-config --libs tinyxml2)
+LIBS += $(shell mysql_config --libs_r) $(shell python-config --libs) $(shell pkg-config --libs jansson)
 
 EPG2VDR_DATA_DIR = "/var/cache/vdr"
 
 ifdef WITH_GTFT
 	DEFINES += -DWITH_GTFT
-endif	
+endif
 
 ifdef WITH_PIN
 	DEFINES += -DWITH_PIN
-endif	
+endif
+
+ifdef WITH_AUX_PATCH
+	DEFINES += -DWITH_AUX_PATCH
+endif
 
 ifdef EPG2VDR_DATA_DIR
    DEFINES += -DEPG2VDR_DATA_DIR='$(EPG2VDR_DATA_DIR)'
@@ -111,17 +113,17 @@ endif
 
 all: $(SOFILE) i18n
 
-hlib: 
+hlib:
 	(cd lib && $(MAKE) -s lib)
 
 ### Implicit rules:
 
 %.o: %.c
-	$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+	$(doCompile) $(INCLUDES) -o $@ $<
 
 ### Dependencies:
 
-MAKEDEP = $(CXX) -MM -MG
+MAKEDEP = $(CC) -MM -MG
 DEPFILE = .dependencies
 $(DEPFILE): Makefile
 	$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
@@ -157,7 +159,7 @@ install-i18n: $(I18Nmsgs)
 ### Targets:
 
 $(SOFILE): hlib $(OBJS)
-	$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
+	$(CC) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
 
 install-lib: $(SOFILE)
 	install -D -m644 $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
@@ -192,7 +194,7 @@ clean:
 	(cd lib && $(MAKE) clean)
 
 cppchk:
-	cppcheck --template="{file}:{line}:{severity}:{message}" --quiet --force *.c *.h 
+	cppcheck --language=c++ --template="{file}:{line}:{severity}:{message}" --quiet --force *.c *.h
 
 # ------------------------------------------------------
 # Git / Versioning / Tagging
@@ -207,9 +209,11 @@ vcheck:
 
 push: vcheck
 	echo "tagging git with $(VERSION)"
+	git push origin master
+	git push github master
 	git tag $(VERSION)
-	git push --tags
-	git push
+	git push origin master --tags
+	git push github master --tags
 
 commit: vcheck
 	git commit -m "$(LASTCOMMENT)" -a
@@ -227,4 +231,3 @@ update:
 	git pull
 	@make clean install
 	restart vdr
-
diff --git a/README b/README
index 0d2ee30..4a071d6 100644
--- a/README
+++ b/README
@@ -25,6 +25,7 @@ Requirements:
   - python libpython libpython-dev python-dev
   - libjansson4 libjansson-dev
   - uuid-dev
+  - libtinyxml2-dev
 
 Ubuntu (16.04):
   - libarchive13, libarchive-dev
@@ -32,7 +33,7 @@ Ubuntu (16.04):
   - libjansson4 libjansson-dev
   - python libpython libpython-dev python-dev
   - uuid-dev
-
+  - libtinyxml2-dev
 
 Description:
 ------------
diff --git a/TODO b/TODO
index 1e42d47..3fd95d1 100644
--- a/TODO
+++ b/TODO
@@ -3,6 +3,5 @@
 - beim Timer löschen und laufender Aufnahme kommt dieser Hinweis nicht
 
 - anlegen eines Suchtimer über das event
-- Menü für suchtimer
-
 - timer mit action 'C' auch ohne vdruuid übernehmen
+- db Verbindungssaten ohne Neustart übernehmen
\ No newline at end of file
diff --git a/configs/epg.dat b/configs/epg.dat
index b1855b0..1b3884d 100644
--- a/configs/epg.dat
+++ b/configs/epg.dat
@@ -264,12 +264,14 @@ Table vdrs
    DBAPI                ""  dbapi                UInt         0 Data,
    LASTUPDATE           ""  lastupd              Int          0 Data,
    NEXTUPDATE           ""  nextupd              Int          0 Data,
+   LASTMERGE            ""  lastmerge            Int          0 Data,
    STATE                ""  state                Ascii       20 Data,
    MASTER               ""  master               Ascii        1 Data,
    IP                   ""  ip                   Ascii       20 Data,
    MAC                  ""  mac                  Ascii       18 Data,
    PID                  ""  pid                  UInt         0 Data filter epgd|httpd|epg2vdr,
    SVDRP                ""  svdrp                UInt         0 Data filter epgd|httpd|epg2vdr,
+   OSD2WEBP             ""  osd2webp             UInt         0 Data,
    TUNERCOUNT           ""  tunercount           UInt         0 Data filter epgd|httpd|epg2vdr,
 
    SHAREINWEB           ""  shareinweb           UInt         1 Data,
@@ -500,6 +502,7 @@ Table recordinglist
    TITLE                ""            title            Ascii      200 Data,
    SHORTTEXT            ""            shorttext        Ascii      300 Data,
    LONGDESCRIPTION      ""            longdescription  MText    25000 Data,
+   DESCRIPTION          ""            description      MText    25000 Data,
    DURATION             ""            duration         UInt         0 Data,
    FSK                  ""            fsk              UInt         1 Data,
 
@@ -595,9 +598,11 @@ Table timers
    INFO                 "error reason if state is failed"        info                 Ascii      255 Data,
    ACTION               ""                                       action               Ascii        1 Data default a,
    TCCMAILCNT           ""                                       tccmailcnt           UInt         0 Data,
+   WRNCOUNT             ""                                       wrncount             UInt         0 Data,
    RETRYS               ""                                       retrys               UInt         0 Data,
 
    NAMINGMODE           ""                                       namingmode           Int          0 Data,
+   TEMPLATE             ""                                       template             Ascii      100 Data,
    ACTIVE               ""                                       active               UInt         0 Data,
    DAY                  ""                                       day                  Int         10 Data,
    WEEKDAYS             ""                                       weekdays             Int         10 Data,
@@ -674,6 +679,7 @@ Table searchtimers
    TYPE                 "'R'ecord, 'V'iew, 'S'earch"                           type                 Ascii        1 Data,
    STATE                "D - Deleted"                                          state                Ascii        1 Data,
    NAMINGMODE           ""                                                     namingmode           Int          0 Data,
+   TEMPLATE             ""                                                     template             Ascii      100 Data,
 
    ACTIVE               "0,1"                                                  active               UInt         0 Data,
    SOURCE               "webif ,osd, ..."                                      source               Ascii       40 Data,
@@ -749,7 +755,24 @@ Index TimersDone
 }
 
 // ----------------------------------------------------------------
-// SCRAPER stuff --->
+// Table Messages / Notifications
+// ----------------------------------------------------------------
+
+Table messages
+{
+   ID                   ""                                    id            UInt         0 Primary|Autoinc,
+
+   INSSP                ""                                    inssp         Int          0 Meta,
+   UPDSP                ""                                    updsp         Int          0 Meta,
+
+   TYPE                 "Warning, Info, Error, Fatal"         type          Ascii        1 Data,
+   TITLE                ""                                    title         Ascii      200 Data,
+   STATE                "Read, New, Deleted"                  state         Ascii        1 Data,
+   TEXT                 ""                                    text          MText    20000 Data,
+}
+
+// ----------------------------------------------------------------
+// SCRAPER stuff
 // ----------------------------------------------------------------
 // ----------------------------------------------------------------
 // Table Series
diff --git a/epg2vdr.c b/epg2vdr.c
index 9abf890..f56ccb3 100644
--- a/epg2vdr.c
+++ b/epg2vdr.c
@@ -9,11 +9,12 @@
 #include <vdr/tools.h>
 
 #include "plgconfig.h"
+
 #include "update.h"
 #include "menu.h"
 #include "handler.h"
 
-#if defined (APIVERSNUM) && (APIVERSNUM < 20200)
+#if !defined (APIVERSNUM) || (APIVERSNUM < 20200)
 #  error VDR API versions < 2.2.0 are not supported !
 #endif
 
@@ -99,11 +100,7 @@ cEpgPluginMenu::cEpgPluginMenu(const char* title, cPluginEPG2VDR* aPlugin)
       cOsdMenu::Add(new cOsdItem(hk(tr("Timer journal")), (eOSState)emsDones));
    }
 
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-#  warning "Program menu have to be ported to VDR >= 2.3.1, compiling without it"
-#else
    cOsdMenu::Add(new cOsdItem(hk(tr("Program")), (eOSState)emsProgram));
-#endif
    cOsdMenu::Add(new cOsdItem(hk(tr("Update")), (eOSState)emsUpdate));
    cOsdMenu::Add(new cOsdItem(hk(tr("Reload")), (eOSState)emsReload));
 
@@ -123,6 +120,7 @@ eOSState cEpgPluginMenu::ProcessKey(eKeys key)
          state = AddSubMenu(new cMenuEpgTimers());
          break;
       }
+
       case emsSearchtimer:
       {
          state = AddSubMenu(new cMenuEpgSearchTimers());
@@ -134,15 +132,13 @@ eOSState cEpgPluginMenu::ProcessKey(eKeys key)
          state = AddSubMenu(new cEpgMenuDones());
          break;
       }
+
       case emsProgram:
       {
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-#  warning "Program menu have to be ported to VDR >= 2.3.1, compiling without it"
-#else
          state = AddSubMenu(new cMenuEpgWhatsOn());
-#endif
          break;
       }
+
       case emsUpdate:
       {
          Skins.Message(mtInfo, tr("Update EPG"));
@@ -285,6 +281,9 @@ void cMenuSetupEPG2VDR::Setup()
    Add(new cMenuEditBoolItem(tr("Prohibit Shutdown On Busy 'epgd'"), &data.activeOnEpgd));
    Add(new cMenuEditBoolItem(tr("Schedule Boot For Update"), &data.scheduleBoot));
    Add(new cMenuEditBoolItem(tr("Blacklist not configured Channels"), &data.blacklist));
+#if (defined (APIVERSNUM) && (APIVERSNUM >= 20304)) || (WITH_AUX_PATCH)
+   Add(new cMenuEditBoolItem(tr("Store extended EPD Data to AUX (e.g. for Skins)"), &data.extendedEpgData2Aux));
+#endif
 
    Add(new cOsdItem(cString::sprintf("--------------------- %s ---------------------------------", tr("Menu"))));
    cList<cOsdItem>::Last()->SetSelectable(false);
@@ -375,6 +374,7 @@ void cMenuSetupEPG2VDR::Store()
    SetupStore("UseCommonRecFolder", Epg2VdrConfig.useCommonRecFolder);
    SetupStore("ReplaceScheduleMenu", Epg2VdrConfig.replaceScheduleMenu);
    SetupStore("ReplaceTimerMenu", Epg2VdrConfig.replaceTimerMenu);
+   SetupStore("ExtendedEpgData2Aux", Epg2VdrConfig.extendedEpgData2Aux);
 
    if (userCount && Epg2VdrConfig.userIndex >= 0)
    {
@@ -415,10 +415,15 @@ void cMenuSetupEPG2VDR::Store()
 
 cPluginEPG2VDR::cPluginEPG2VDR()
 {
+   pluginInitialized = no;
    oUpdate = 0;
    connection = 0;
    timerDb = 0;
    vdrDb = 0;
+   useeventsDb = 0;
+   selectTimers = 0;
+   selectEventById = 0;
+   recordingListDb = 0;
 }
 
 cPluginEPG2VDR::~cPluginEPG2VDR()
@@ -441,6 +446,12 @@ int cPluginEPG2VDR::initDb()
    vdrDb = new cDbTable(connection, "vdrs");
    if (vdrDb->open() != success) return fail;
 
+   useeventsDb = new cDbTable(connection, "useevents");
+   if (useeventsDb->open() != success) return fail;
+
+   recordingListDb = new cDbTable(connection, "recordinglist");
+   if (recordingListDb->open() != success) return fail;
+
    // ----------
    // select
    //    t.*,
@@ -460,16 +471,35 @@ int cPluginEPG2VDR::initDb()
    selectTimers->bind(vdrDb, "UUID", cDBS::bndOut, ", ");
    selectTimers->bind(vdrDb, "STATE", cDBS::bndOut, ", ");
    selectTimers->clrBindPrefix();
-   selectTimers->build(" from %s t, %s v where (t.%s in ('P','R') or t.%s is null)",
+   selectTimers->build(" from %s t, %s v where t.%s and (t.%s in ('P','R') or t.%s is null)",
                        timerDb->TableName(), vdrDb->TableName(),
+                       timerDb->getField("ACTIVE")->getDbName(),
                        timerDb->getField("STATE")->getDbName(),
                        timerDb->getField("STATE")->getDbName());
-   selectTimers->build(" and t.%s = v.%s",
+   selectTimers->build(" and t.%s = v.%s order by t.%s",
                        timerDb->getField("VDRUUID")->getDbName(),
-                       vdrDb->getField("UUID")->getDbName());
+                       vdrDb->getField("UUID")->getDbName(),
+                       timerDb->getField("_STARTTIME")->getDbName());
 
    status += selectTimers->prepare();
 
+   // select event by useid
+   // select * from eventsview
+   //      where useid = ?
+   //        and updflg in (.....)
+
+   selectEventById = new cDbStatement(useeventsDb);
+
+   selectEventById->build("select ");
+   selectEventById->bindAllOut();
+   selectEventById->build(" from %s where ", useeventsDb->TableName());
+   selectEventById->bind("USEID", cDBS::bndIn | cDBS::bndSet);
+   selectEventById->build(" and %s in (%s)",
+                          useeventsDb->getField("UPDFLG")->getDbName(),
+                          Us::getNeeded());
+
+   status += selectEventById->prepare();
+
    return status;
 }
 
@@ -477,8 +507,14 @@ int cPluginEPG2VDR::exitDb()
 {
    if (connection)
    {
+      delete selectEventById;      selectEventById = 0;
+      delete selectTimers;         selectTimers = 0;
+
+      delete useeventsDb;          useeventsDb = 0;
       delete timerDb;              timerDb = 0;
       delete vdrDb;                vdrDb = 0;
+      delete recordingListDb;      recordingListDb = 0;
+
       delete connection;           connection = 0;
    }
 
@@ -778,9 +814,9 @@ cString cPluginEPG2VDR::SVDRPCommand(const char* cmd, const char* Option, int &R
 bool cPluginEPG2VDR::Service(const char* id, void* data)
 {
    if (!data)
-      return fail;
+      return false;
 
-   tell(4, "Service called with '%s', %d/%d", id,
+   tell(3, "Service called with '%s', %d/%d", id,
         Epg2VdrConfig.replaceScheduleMenu, Epg2VdrConfig.replaceTimerMenu);
 
    if (strcmp(id, EPG2VDR_UUID_SERVICE) == 0)
@@ -807,7 +843,13 @@ bool cPluginEPG2VDR::Service(const char* id, void* data)
       return true;
    }
 
-   else if (strcmp(id, "MainMenuHooksPatch-v1.0::osSchedule") == 0 && Epg2VdrConfig.replaceScheduleMenu)
+   if (!pluginInitialized)
+   {
+      tell(2, "Service called but plugin not ready, retry later");
+      return false;
+   }
+
+   if (strcmp(id, "MainMenuHooksPatch-v1.0::osSchedule") == 0 && Epg2VdrConfig.replaceScheduleMenu)
    {
       cOsdMenu** menu = (cOsdMenu**)data;
 
@@ -817,7 +859,7 @@ bool cPluginEPG2VDR::Service(const char* id, void* data)
       return true;
    }
 
-   else if (strcmp(id, "MainMenuHooksPatch-v1.0::osTimers") == 0 && Epg2VdrConfig.replaceTimerMenu)
+   else if (strcmp(id, "MainMenuHooksPatch-v1.0::osTimers") == 0 && Epg2VdrConfig.replaceTimerMenu && Epg2VdrConfig.shareInWeb)
    {
       cOsdMenu** menu = (cOsdMenu**)data;
 
@@ -827,18 +869,69 @@ bool cPluginEPG2VDR::Service(const char* id, void* data)
       return true;
    }
 
-   else if (strcmp(id, EPG2VDR_TIMER_SERVICE) == 0)
+   if (strcmp(id, EPG2VDR_TIMER_SERVICE) == 0 || strcmp(id, EPG2VDR_REC_DETAIL_SERVICE) == 0 || strcmp(id, EPG2VDR_HAS_TIMER) == 0)
    {
-      cEpgTimer_Service_V1* ts = (cEpgTimer_Service_V1*)data;
+      // Services with direct db access
+
+      cMutexLock lock(&mutexServiceWithDb);
+
+      if (initDb() == success)
+      {
+         if (strcmp(id, EPG2VDR_TIMER_SERVICE) == 0)
+         {
+            cEpgTimer_Service_V1* ts = (cEpgTimer_Service_V1*)data;
+
+            if (ts)
+               return timerService(ts);
+         }
+         if (strcmp(id, EPG2VDR_HAS_TIMER) == 0)
+         {
+            cHas_Timer_V1* d = (cHas_Timer_V1*)data;
+
+            if (d)
+               return hasTimerService(d);
+         }
+         else if (strcmp(id, EPG2VDR_REC_DETAIL_SERVICE) == 0)
+         {
+            cEpgRecording_Details_Service_V1* rd = (cEpgRecording_Details_Service_V1*)data;
 
-      if (ts)
-         return timerService(ts);
+            if (rd)
+               return recordingDetails(rd);
+         }
+
+         exitDb();
+      }
    }
 
    return false;
 }
 
 //***************************************************************************
+// Has Timer Service
+//***************************************************************************
+
+int cPluginEPG2VDR::hasTimerService(cHas_Timer_V1* d)
+{
+   cMutexLock lock(&mutexTimerService);
+
+   timerDb->clear();
+   vdrDb->clear();
+
+   d->hastimer = no;
+
+   for (int f = selectTimers->find(); f && connection->check() == success; f = selectTimers->fetch())
+   {
+      if (timerDb->hasValue("EVENTID", d->eventid))
+      {
+         d->hastimer = yes;
+         break;
+      }
+   }
+
+   return true;
+}
+
+//***************************************************************************
 // Timer Service
 //***************************************************************************
 
@@ -847,32 +940,83 @@ int cPluginEPG2VDR::timerService(cEpgTimer_Service_V1* ts)
    cMutexLock lock(&mutexTimerService);
    uint64_t start = cTimeMs::Now();
 
-   if (initDb() == success)
+   timerDb->clear();
+   vdrDb->clear();
+
+   ts->epgTimers.clear();
+
+   for (int f = selectTimers->find(); f && connection->check() == success; f = selectTimers->fetch())
    {
-      timerDb->clear();
-      vdrDb->clear();
+      cEpgTimer* epgTimer = newTimerObjectFromRow(timerDb->getRow(), vdrDb->getRow());
 
-      ts->epgTimers.clear();
+      if (Epg2VdrConfig.shareInWeb || epgTimer->isLocal())
+         ts->epgTimers.push_back(epgTimer);
+      else
+         delete epgTimer;
+   }
 
-      for (int f = selectTimers->find(); f && connection->check() == success; f = selectTimers->fetch())
-      {
-         cEpgTimer* epgTimer = newTimerObjectFromRow(timerDb->getRow(), vdrDb->getRow());
+   tell(1, "Answer '%s' call with %zd timers, duration was (%s)",
+        EPG2VDR_TIMER_SERVICE,
+        ts->epgTimers.size(),
+        ms2Dur(cTimeMs::Now()-start).c_str());
 
-         if (Epg2VdrConfig.shareInWeb || epgTimer->isLocal())
-            ts->epgTimers.push_back(epgTimer);
-         else
-            delete epgTimer;
-      }
+   return true;
+}
 
-      tell(1, "Answer '%s' call with %lu timers, duration was (%s)",
-           EPG2VDR_TIMER_SERVICE,
-           ts->epgTimers.size(),
-           ms2Dur(cTimeMs::Now()-start).c_str());
+//***************************************************************************
+// Recording Details
+//***************************************************************************
+
+#include <vdr/videodir.h>
+
+int cPluginEPG2VDR::recordingDetails(cEpgRecording_Details_Service_V1* rd)
+{
+   int found = false;
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+   const char* videoBasePath = cVideoDirectory::Name();
+   md5Buf md5path;
+   const cRecording* recording;
+   int pathOffset = 0;
+
+   LOCK_RECORDINGS_READ;
+   const cRecordings* recordings = Recordings;
+
+   if (!(recording = recordings->GetById(rd->id)))
+      return false;
+
+   if (strncmp(recording->FileName(), videoBasePath, strlen(videoBasePath)) == 0)
+   {
+      pathOffset = strlen(videoBasePath);
+
+      if (*(recording->FileName()+pathOffset) == '/')
+         pathOffset++;
    }
 
-   exitDb();
+   createMd5(recording->FileName()+pathOffset, md5path);
 
-   return true;
+   recordingListDb->clear();
+
+   recordingListDb->setValue("MD5PATH", md5path);
+   recordingListDb->setValue("STARTTIME", recording->Start());
+   recordingListDb->setValue("OWNER", Epg2VdrConfig.useCommonRecFolder ? "" : Epg2VdrConfig.uuid);
+
+   cXml xml;
+
+   found = recordingListDb->find();
+
+   xml.create("epg2vdr");
+
+   if (found)
+      cEventDetails::row2Xml(recordingListDb->getRow(), &xml);
+
+   rd->details = xml.toText();
+
+   recordingListDb->reset();
+
+#endif
+
+   return found;
 }
 
 //***************************************************************************
@@ -898,7 +1042,10 @@ bool cPluginEPG2VDR::Start()
    oUpdate = new cUpdate(this);
 
    if (oUpdate->init() == success)
+   {
       oUpdate->Start();                 // start plugin thread
+      pluginInitialized = yes;
+   }
    else
       tell(0, "Initialization failed, start of plugin aborted!");
 
@@ -974,6 +1121,7 @@ bool cPluginEPG2VDR::SetupParse(const char *Name, const char *Value)
    else if (!strcasecmp(Name, "ReplaceScheduleMenu"))  Epg2VdrConfig.replaceScheduleMenu = atoi(Value);
    else if (!strcasecmp(Name, "ReplaceTimerMenu"))     Epg2VdrConfig.replaceTimerMenu = atoi(Value);
    else if (!strcasecmp(Name, "User"))                 sstrcpy(Epg2VdrConfig.user, Value, sizeof(Epg2VdrConfig.user));
+   else if (!strcasecmp(Name, "ExtendedEpgData2Aux"))  Epg2VdrConfig.extendedEpgData2Aux = atoi(Value);
 
    else
       return false;
@@ -989,6 +1137,7 @@ void cPluginEPG2VDR::Stop()
 
    req.action = mieaExit;
    Service(MYSQL_INIT_EXIT, &req);
+   pluginInitialized = no;
 }
 
 //***************************************************************************
diff --git a/epg2vdr.h b/epg2vdr.h
index d2f05a2..26ab2bd 100644
--- a/epg2vdr.h
+++ b/epg2vdr.h
@@ -32,9 +32,7 @@ static const char* MAINMENUENTRY = tr("EPG and Timer Service");
 // cPluginEPG2VDR
 //***************************************************************************
 
-cOsdMenu* newWathsOn();
-
-class cPluginEPG2VDR : public cPlugin 
+class cPluginEPG2VDR : public cPlugin
 {
    public:
 
@@ -51,7 +49,7 @@ class cPluginEPG2VDR : public cPlugin
       virtual bool Initialize(void);
       virtual bool Start(void);
       virtual cString Active(void);
-      virtual const char* MainMenuEntry(void) 
+      virtual const char* MainMenuEntry(void)
       { return Epg2VdrConfig.mainmenuVisible ? MAINMENUENTRY : 0; }
       virtual cOsdObject* MainMenuAction(void);
       virtual cMenuSetupPage* SetupMenu(void);
@@ -59,21 +57,28 @@ class cPluginEPG2VDR : public cPlugin
       virtual void Stop();
       virtual void DisplayMessage(const char* s);
       virtual time_t WakeupTime(void);
-   
+
    protected:
-   
+
       int initDb();
       int exitDb();
 
       int timerService(cEpgTimer_Service_V1* ts);
-         
+      int hasTimerService(cHas_Timer_V1* d);
+      int recordingDetails(cEpgRecording_Details_Service_V1* rd);
+
    private:
-   
+
+      int pluginInitialized;
       cDbConnection* connection;
       cDbTable* timerDb;
       cDbTable* vdrDb;
+      cDbTable* useeventsDb;
+      cDbTable* recordingListDb;
       cDbStatement* selectTimers;
+      cDbStatement* selectEventById;
       cMutex mutexTimerService;
+      cMutex mutexServiceWithDb;
 };
 
 //***************************************************************************
diff --git a/handler.h b/handler.h
index abf82e3..d00e1f2 100644
--- a/handler.h
+++ b/handler.h
@@ -8,8 +8,18 @@
 #ifndef __HANDLER_H
 #define __HANDLER_H
 
+#include "lib/vdrlocks.h"
+
 #include "update.h"
 
+#define CHANNELMARKOBSOLETE "OBSOLETE"
+
+//***************************************************************************
+// Define tEventID again, to create a compiler error case it was defines different
+//***************************************************************************
+
+typedef u_int32_t tEventID;     // on error vdr >= 2.3.1 and patch is not applied!!
+
 //***************************************************************************
 // Mutex Try
 //***************************************************************************
@@ -91,8 +101,9 @@ class cEpgHandlerInstance
          selectDelFlg = 0;
          delCompOf = 0;
          selectMergeSp = 0;
+         selectEventByStarttime = 0;
 
-         tell(0, "Init handler instance for thread %d", cThread::ThreadId());
+         tell(0, "Handler: Init handler instance for thread %d", cThread::ThreadId());
       }
 
       virtual ~cEpgHandlerInstance() { exitDb(); }
@@ -114,18 +125,19 @@ class cEpgHandlerInstance
          {
             // try to connect
 
-            tell(0, "Trying to re-connect to database!");
+            tell(0, "Handler: Trying to re-connect to database!");
             retry++;
 
             if (initDb() != success)
             {
                exitDb();
 
+               tell(0, "Handler: Database re-connect failed!");
                return fail;
             }
 
             retry = 0;
-            tell(0, "Connection established successfull!");
+            tell(0, "Handler: Connection established successfull!");
          }
 
          return success;
@@ -172,8 +184,24 @@ class cEpgHandlerInstance
          compDb = new cDbTable(connection, "components");
          if (compDb->open() != success) return fail;
 
-         // prepare statement to get mapsp
+         // select
+         //   * from events
+         // where
+         //   source = 'vdr'
+         //   and starttime = ?
+         //   and channelid = ?
+
+         selectEventByStarttime = new cDbStatement(eventsDb);
+
+         selectEventByStarttime->build("select ");
+         selectEventByStarttime->bindAllOut(0, cDBS::ftAll);
+         selectEventByStarttime->build(" from %s where source = 'vdr'", eventsDb->TableName());
+         selectEventByStarttime->bind("STARTTIME", cDBS::bndIn | cDBS::bndSet, " and ");
+         selectEventByStarttime->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet, " and ");
+
+         status += selectEventByStarttime->prepare();
 
+         // prepare statement to get mapsp
          // select
          //   mergesp from channelmap
          // where
@@ -185,7 +213,7 @@ class cEpgHandlerInstance
          selectMergeSp->build("select ");
          selectMergeSp->bind("MergeSp", cDBS::bndOut);
          selectMergeSp->build(" from %s where source != 'vdr'", mapDb->TableName());
-         selectMergeSp->bind("ChannelId", cDBS::bndIn |cDBS::bndSet, " and ");
+         selectMergeSp->bind("ChannelId", cDBS::bndIn | cDBS::bndSet, " and ");
          selectMergeSp->build(" limit 1");
 
          status += selectMergeSp->prepare();
@@ -202,8 +230,8 @@ class cEpgHandlerInstance
          updateDelFlg = new cDbStatement(eventsDb);
 
          updateDelFlg->build("update %s set ", eventsDb->TableName());
-         updateDelFlg->bind("DelFlg", cDBS::bndIn |cDBS::bndSet);
-         updateDelFlg->bind("UpdFlg", cDBS::bndIn |cDBS::bndSet, ", ");
+         updateDelFlg->bind("DelFlg", cDBS::bndIn | cDBS::bndSet);
+         updateDelFlg->bind("UpdFlg", cDBS::bndIn | cDBS::bndSet, ", ");
          updateDelFlg->bind("UpdSp", cDBS::bndIn | cDBS::bndSet, ", ");
          updateDelFlg->build(" where ");
          updateDelFlg->bind("ChannelId", cDBS::bndIn | cDBS::bndSet);
@@ -241,7 +269,7 @@ class cEpgHandlerInstance
          delCompOf = new cDbStatement(compDb);
 
          delCompOf->build("delete from %s where ", compDb->TableName());
-         delCompOf->bind("EventId", cDBS::bndIn |cDBS::bndSet);
+         delCompOf->bind("EventId", cDBS::bndIn | cDBS::bndSet);
          delCompOf->build(";");
 
          status += delCompOf->prepare();
@@ -272,6 +300,7 @@ class cEpgHandlerInstance
             delete compDb;        compDb = 0;
             delete mapDb;         mapDb = 0;
             delete selectMergeSp; selectMergeSp = 0;
+            delete selectEventByStarttime; selectEventByStarttime = 0;
 
             delete connection;    connection = 0;
          }
@@ -288,53 +317,55 @@ class cEpgHandlerInstance
          // select eventid, channelid, version, tableid, delflg
          //   from events where source = 'vdr'
 
-         cDbStatement* selectAllVdr = new cDbStatement(eventsDb);
+         cDbStatement* selectAllVdrEvents = new cDbStatement(eventsDb);
 
-         selectAllVdr->build("select ");
-         selectAllVdr->bind("EventId", cDBS::bndOut);
-         selectAllVdr->bind("ChannelId", cDBS::bndOut, ", ");
-         selectAllVdr->bind("Version", cDBS::bndOut, ", ");
-         selectAllVdr->bind("TableId", cDBS::bndOut, ", ");
-         selectAllVdr->bind("DelFlg", cDBS::bndOut, ", ");
-         selectAllVdr->build(" from %s where source = 'vdr'", eventsDb->TableName());
+         selectAllVdrEvents->build("select ");
+         selectAllVdrEvents->bind("EventId", cDBS::bndOut);
+         selectAllVdrEvents->bind("ChannelId", cDBS::bndOut, ", ");
+         selectAllVdrEvents->bind("Version", cDBS::bndOut, ", ");
+         selectAllVdrEvents->bind("TableId", cDBS::bndOut, ", ");
+         selectAllVdrEvents->bind("DelFlg", cDBS::bndOut, ", ");
+         selectAllVdrEvents->build(" from %s where source = 'vdr'", eventsDb->TableName());
 
-         if (selectAllVdr->prepare() != success)
+         if (selectAllVdrEvents->prepare() != success)
          {
-            tell(0, "Aborted reading hashes from db due to prepare error");
-            delete selectAllVdr;
+            tell(0, "Handler: Aborted reading hashes from db due to prepare error");
+            delete selectAllVdrEvents;
             return fail;
          }
 
-         tell(1, "Start reading hashes from db");
+         tell(1, "Handler: Start reading hashes from db");
 
          eventsDb->clear();
 
-         for (int f = selectAllVdr->find(); f; f = selectAllVdr->fetch())
+         for (int f = selectAllVdrEvents->find(); f; f = selectAllVdrEvents->fetch())
          {
             char evtKey[100];
 
             if (eventsDb->hasValue("DelFlg", "Y"))
                continue;
 
-            sprintf(evtKey, "%"PRId64":%s",
-                    eventsDb->getBigintValue("EventId"),
-                    eventsDb->getStrValue("ChannelId"));
+            sprintf(evtKey, "%ld:%s",
+                    eventsDb->getIntValue("STARTTIME"),
+                    eventsDb->getStrValue("CHANNELID"));
 
             evtMemList[evtKey].version = eventsDb->getIntValue("Version");
             evtMemList[evtKey].tableid = eventsDb->getIntValue("TableId");
+
+            tell(4, "Handler: cInsert: '%s' with %d/%d", evtKey, evtMemList[evtKey].tableid, evtMemList[evtKey].version);
          }
 
-         selectAllVdr->freeResult();
-         delete selectAllVdr;
+         selectAllVdrEvents->freeResult();
+         delete selectAllVdrEvents;
 
-         tell(1, "Finished reading hashes from db, got %d hashes (in %ld seconds)",
+         tell(1, "Handler: Finished reading hashes from db, got %d hashes (in %ld seconds)",
               (int)evtMemList.size(), time(0)-start);
 
          return success;
       }
 
       //***************************************************************************
-      // NOEPG feature - so we don't need the noepg plugin
+      // Ignore Channel
       //***************************************************************************
 
       virtual bool IgnoreChannel(const cChannel* Channel)
@@ -346,7 +377,7 @@ class cEpgHandlerInstance
          // for the db an we have the active role!
          // (already checked by cEpg2VdrEpgHandler)
 
-         // check connection
+         // only check the DB connection here!!
 
          if (!dbConnected() && time(0) < nextRetryAt)
             return true;
@@ -364,18 +395,13 @@ class cEpgHandlerInstance
       // Transaction Stuff
       //***************************************************************************
 
-      virtual bool BeginSegmentTransfer(const cChannel* Channel, bool OnlyRunningStatus)
+      virtual bool BeginSegmentTransfer(const cChannel* Channel, bool dummy)
       {
          // inital die channelid setzen
 
          channelId = Channel->GetChannelID();
 
-         // start transaction
-
-         // if (!OnlyRunningStatus && dbConnected() && getExternalIdOfChannel(&channelId) != "")
-         //   connection->startTransaction();
-
-         return false;
+         return true;
       }
 
       //***************************************************************************
@@ -397,22 +423,24 @@ class cEpgHandlerInstance
             if (Channel->GetChannelID().Valid() && reportFirst)
             {
                reportFirst = no;
-               tell(0, "WARNING: Assume 'SegmentTransfer' patch is missing, "
+               tell(0, "Handler: WARNING: Assume 'SegmentTransfer' patch is missing, "
                     "VDR < 2.1.0 have to be patched! Or disable the handler in the plugin setup");
             }
 
             return false;
          }
 
+         // dbConnected() here okay -> if not connected we return false an the vdr will handle the event?!?
+
          if (dbConnected() && getExternalIdOfChannel(&channelId) != "")
             return true;
 
          return false;
       }
 
-      virtual bool EndSegmentTransfer(bool Modified, bool OnlyRunningStatus)
+      virtual bool EndSegmentTransfer(bool Modified, bool dummy)
       {
-         if (OnlyRunningStatus || !dbConnected() || !connection->inTransaction())
+         if (dummy || !dbConnected() || !connection->inTransaction())
             return false;
 
          if (Modified)
@@ -441,12 +469,12 @@ class cEpgHandlerInstance
          if (!isZero(getExternalIdOfChannel(&channelId).c_str()) && StartTime > time(0) + 4 * tmeSecondsPerDay)
             return false;
 
-         sprintf(evtKey, "%ld:%s", (long)EventID, (const char*)channelId.ToString());
+         sprintf(evtKey, "%ld:%s", StartTime, (const char*)channelId.ToString());
 
          if (evtMemList.find(evtKey) == evtMemList.end())
          {
-            tell(4, "Handle insert of event %d for channel '%s'",
-                 EventID, (const char*)channelId.ToString());
+            tell(4, "Handler: Handle insert (or starttime update) of event '%s' (%d) for channel '%s'",
+                 evtKey, EventID, (const char*)channelId.ToString());
 
             if (!connection->inTransaction())
                connection->startTransaction();
@@ -454,26 +482,28 @@ class cEpgHandlerInstance
             return true;
          }
 
-         uchar oldTableId = ::max(uchar(evtMemList[evtKey].tableid), uchar(0x4E));
+         uchar currentTableId = std::max(uchar(evtMemList[evtKey].tableid), uchar(0x4E));
 
-         // skip if old tid is already lower
+         // skip bigger ids as current
 
-         if (oldTableId < TableID)
+         if (TableID > currentTableId)
          {
-            tell(4, "Ignoring update with old tableid for event '%s'", evtKey);
+            tell(4, "Handler: Ignoring update with older tableid (%d) for event '%s' (%d)(has tableid %d)",
+                 TableID, evtKey, EventID, evtMemList[evtKey].tableid);
             return false;
          }
 
          // skip if version an tid identical
 
-         if (oldTableId == TableID && evtMemList[evtKey].version == Version)
+         if (currentTableId == TableID && evtMemList[evtKey].version == Version)
          {
-            tell(4, "Ignoring 'non' update for event '%s'", evtKey);
+            tell(4, "Handler: Ignoring 'non' update for event '%s' (%d), version still (%d)",
+                 evtKey, EventID, Version);
             return false;
          }
 
          if (Epg2VdrConfig.loglevel > 3)
-            tell(4, "Handle update of event %d [%s] %d/%d - %d/%d", EventID, evtKey,
+            tell(4, "Handler: Handle update of event '%s' (%d)  %d/%d - %d/%d", evtKey, EventID,
                  Version, TableID,
                  evtMemList[evtKey].version, evtMemList[evtKey].tableid);
 
@@ -489,6 +519,8 @@ class cEpgHandlerInstance
 
       virtual bool HandleEvent(cEvent* event)
       {
+         int oldStartTime = 0;
+
          if (!dbConnected() || !event || !channelId.Valid())
             return false;
 
@@ -507,7 +539,7 @@ class cEpgHandlerInstance
 
          if (!connection->inTransaction())
          {
-            tell(0, "Error missing tact in HandleEvent");
+            tell(0, "Handler: Error missing tact in HandleEvent");
             return false;
          }
 
@@ -515,28 +547,44 @@ class cEpgHandlerInstance
 
          // lookup the event ..
          //   first try by starttime
-         //   second by eventid
 
          eventsDb->clear();
-         eventsDb->setBigintValue("EventId", (long)event->EventID());
-         eventsDb->setValue("ChannelId", channelId.ToString());
+         eventsDb->setValue("CHANNELID", channelId.ToString());
+         eventsDb->setValue("STARTTIME", event->StartTime());
+
+         int insert = !selectEventByStarttime->find();
+
+         if (insert)
+         {
+            // try lookup by eventid
+
+            eventsDb->setValue("CHANNELID", channelId.ToString());
+            eventsDb->setBigintValue("EVENTID", (long)event->EventID());
+
+            if (eventsDb->find())
+            {
+               // fount => NOT a insert, just a update of the starttime
 
-         int insert = !eventsDb->find();
+               insert = no;
+               oldStartTime = eventsDb->getIntValue("STARTTIME");
+               eventsDb->setValue("STARTTIME", event->StartTime());
+            }
+         }
 
          // reinstate ??
 
-         if (eventsDb->hasValue("DelFlg", "Y"))
+         if (eventsDb->hasValue("DELFLG", "Y"))
          {
             char updFlg = Us::usPassthrough;
 
             mapDb->clear();
-            mapDb->setValue("ChannelId", channelId.ToString());
+            mapDb->setValue("CHANNELID", channelId.ToString());
 
             if (selectMergeSp->find())
             {
-               time_t mergesp = mapDb->getIntValue("MergeSp");
-               long masterid = eventsDb->getIntValue("MasterId");
-               long useid = eventsDb->getIntValue("UseId");
+               time_t mergesp = mapDb->getIntValue("MERGESP");
+               long masterid = eventsDb->getIntValue("MASTERID");
+               long useid = eventsDb->getIntValue("USEID");
 
                if (event->StartTime() > mergesp)
                   updFlg = Us::usRemove;
@@ -546,13 +594,13 @@ class cEpgHandlerInstance
                   updFlg = Us::usLink;
             }
 
-            eventsDb->setCharValue("UpdFlg", updFlg);
-            eventsDb->setValue("DelFlg", 0, 0);        // set to NULL
+            eventsDb->setCharValue("UPDFLG", updFlg);
+            eventsDb->getValue("DELFLG")->setNull();
          }
 
-         if (!insert && abs(event->StartTime() - eventsDb->getIntValue("StartTime")) > 6*tmeSecondsPerHour)
+         if (!insert && std::abs(event->StartTime() - eventsDb->getIntValue("StartTime")) > 6*tmeSecondsPerHour)
          {
-            tell(3, "Info: Start time of %d/%s - '%s' moved %ld hours from %s to %s - '%s'",
+            tell(3, "Handler: Info: Start time of %d/%s - '%s' moved %ld hours from %s to %s - '%s'",
                  event->EventID(), (const char*)channelId.ToString(),
                  eventsDb->getStrValue("Title"),
                  (event->StartTime() - eventsDb->getIntValue("StartTime")) / tmeSecondsPerHour,
@@ -568,7 +616,7 @@ class cEpgHandlerInstance
              && eventsDb->getIntValue("StartTime") >  time(0)
              && event->StartTime() < eventsDb->getIntValue("StartTime"))
          {
-            tell(1, "Info: Got update of %d/%s with startime more than 2h in past "
+            tell(1, "Handler: Info: Got update of %d/%s with startime more than 2h in past "
                  "(%s/%d) before (%s), ignoring update, set delflg instead",
                  event->EventID(), (const char*)channelId.ToString(),
                  l2pTime(event->StartTime()).c_str(), event->Duration(),
@@ -582,10 +630,10 @@ class cEpgHandlerInstance
             eventsDb->setValue("StartTime", event->StartTime());
          }
 
-         if (!insert && eventsDb->getIntValue("Vps") != event->Vps())
-            tell(1, "Toggle vps flag for '%s' at '%s' from %s to %s",
+         if (!insert && eventsDb->getIntValue("VPS") != event->Vps())
+            tell(1, "Handler: Toggle vps flag for '%s' at '%s' from %s to %s",
                  event->Title(), (const char*)channelId.ToString(),
-                 l2pTime(eventsDb->getIntValue("Vps")).c_str(), l2pTime(event->Vps()).c_str());
+                 l2pTime(eventsDb->getIntValue("VPS")).c_str(), l2pTime(event->Vps()).c_str());
 
          eventsDb->setValue("Source", "vdr");
          eventsDb->setValue("TableId", event->TableID());
@@ -601,7 +649,7 @@ class cEpgHandlerInstance
 
          // contents
 
-         char contents[MaxEventContents * 10]; *contents = 0;
+         char contents[MaxEventContents * 10] = "";
 
          for (int i = 0; i < MaxEventContents; i++)
             if (event->Contents(i) > 0)
@@ -613,7 +661,7 @@ class cEpgHandlerInstance
          // components ..
 
          compDb->clear();
-         compDb->setBigintValue("EventId", (long)event->EventID());
+         compDb->setBigintValue("EVENTID", eventsDb->getBigintValue("EVENTID"));
          delCompOf->execute();
 
          if (event->Components())
@@ -623,7 +671,7 @@ class cEpgHandlerInstance
                tComponent* p = event->Components()->Component(i);
 
                compDb->clear();
-               compDb->setBigintValue("EventId", (long)event->EventID());
+               compDb->setBigintValue("EventId", eventsDb->getBigintValue("EVENTID"));
                compDb->setValue("ChannelId", channelId.ToString());
                compDb->setValue("Stream", p->stream);
                compDb->setValue("Type", p->type);
@@ -669,8 +717,7 @@ class cEpgHandlerInstance
                // vdr event for merge with external event
 
                time_t mergesp = mapDb->getIntValue("MergeSp");
-               eventsDb->setCharValue("UpdFlg",
-                                      event->StartTime() > mergesp ? Us::usInactive : Us::usActive);
+               eventsDb->setCharValue("UpdFlg", event->StartTime() > mergesp ? Us::usInactive : Us::usActive);
             }
 
             eventsDb->insert();
@@ -680,15 +727,27 @@ class cEpgHandlerInstance
             eventsDb->update();
          }
 
+         if (!insert && oldStartTime)
+         {
+            char evtKey[100];
+            sprintf(evtKey, "%ld:%s", (long)oldStartTime, (const char*)channelId.ToString());
+            evtMemList.erase(evtKey);
+            tell(4, "Handler: cRemove: '%s'  (due to starttime update)", evtKey);
+         }
+
          // update hash map
 
          char evtKey[100];
 
-         sprintf(evtKey, "%ld:%s", (long)event->EventID(), (const char*)channelId.ToString());
+         sprintf(evtKey, "%ld:%s", (long)event->StartTime(), (const char*)channelId.ToString());
 
          evtMemList[evtKey].version = event->Version();
          evtMemList[evtKey].tableid = event->TableID();
 
+         tell(4, "Handler: cUpdate/cInsert: '%s' to %d/%d", evtKey, evtMemList[evtKey].tableid, evtMemList[evtKey].version);
+
+         selectEventByStarttime->freeResult();
+
          return true;
       }
 
@@ -706,7 +765,7 @@ class cEpgHandlerInstance
 
          if (!connection->inTransaction())
          {
-            tell(0, "Error missing tact DropOutdated");
+            tell(0, "Handler: Error missing tact DropOutdated");
             return false;
          }
 
@@ -730,17 +789,18 @@ class cEpgHandlerInstance
          {
             char evtKey[100];
 
-            sprintf(evtKey, "%"PRId64":%s",
-                    eventsDb->getBigintValue("EventId"),
-                    eventsDb->getStrValue("ChannelId"));
+            sprintf(evtKey, "%ld:%s",
+                    eventsDb->getIntValue("STARTTIME"),
+                    eventsDb->getStrValue("CHANNELID"));
 
             evtMemList.erase(evtKey);
+            tell(4, "Handler: cRemove: '%s'", evtKey);
          }
 
          selectDelFlg->freeResult();
 
-         eventsDb->setValue("DelFlg", "Y");
-         eventsDb->setCharValue("UpdFlg", Us::usDelete);
+         eventsDb->setValue("DELFLG", "Y");
+         eventsDb->setCharValue("UPDFLG", Us::usDelete);
 
          // mark segment as deleted
 
@@ -772,7 +832,7 @@ class cEpgHandlerInstance
       int updateExternalIdsMap()
       {
          externIdMap.clear();
-         tell(1, "Start reading external ids from db");
+         tell(1, "Handler: Start reading external ids from db");
          mapDb->clear();
 
          // select extid, channelid
@@ -787,7 +847,7 @@ class cEpgHandlerInstance
 
          if (selectAll->prepare() != success)
          {
-            tell(0, "Reading external id's from db aborted due to prepare error");
+            tell(0, "Handler: Reading external id's from db aborted due to prepare error");
             delete selectAll;
             return fail;
          }
@@ -800,7 +860,7 @@ class cEpgHandlerInstance
             externIdMap[chan] = extid;
          }
 
-         tell(1, "Finished reading external id's from db, got %d id's",
+         tell(1, "Handler: Finished reading external id's from db, got %d id's",
               (int)externIdMap.size());
 
          selectAll->freeResult();
@@ -832,7 +892,9 @@ class cEpgHandlerInstance
       cDbStatement* selectDelFlg;
       cDbStatement* delCompOf;
       cDbStatement* selectMergeSp;
-      cUpdate* update;
+      cDbStatement* selectEventByStarttime;
+
+      // cUpdate* update;
 };
 
 //***************************************************************************
@@ -865,6 +927,7 @@ class cEpg2VdrEpgHandler : public cEpgHandler
 
       //***************************************************************************
       // Ignore Channel
+      //   - includes the NOEPG feature - so we don't need the noepg plugin
       //***************************************************************************
 
       virtual bool IgnoreChannel(const cChannel* Channel)
@@ -888,12 +951,7 @@ class cEpg2VdrEpgHandler : public cEpgHandler
          if (!active)
             return true;
 
-         // solange der handler beschäftigt ist (wir den lock nicht bekommen) erst mal ignorieren
-
-         if (!handlerMutex.tryLock())
-            return true;
-
-         // vom handler ignoriert (wegen db Problemen etc.)?
+         // Handler check - only to check if the DB connection is fine
 
          if (getHandler()->IgnoreChannel(Channel))
             return true;
@@ -903,22 +961,25 @@ class cEpg2VdrEpgHandler : public cEpgHandler
 
       virtual bool HandledExternally(const cChannel* Channel)
       {
-         cEpgHandlerInstance* h = getHandler();
-         return h->HandledExternally(Channel);
+         return getHandler()->HandledExternally(Channel);
       }
 
-      virtual bool BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus)
+      virtual bool BeginSegmentTransfer(const cChannel *Channel, bool dummy)
       {
-         cEpgHandlerInstance* h = getHandler();
-         return h->BeginSegmentTransfer(Channel, OnlyRunningStatus);
+         // solange die Datenbank mit einem anderen handler thread
+         // beschäftigt ist (ergo wir den lock nicht bekommen) erst mal ignorieren
+
+         if (!handlerMutex.tryLock())
+            return false;
+
+         return getHandler()->BeginSegmentTransfer(Channel, dummy);
       }
 
-      virtual bool EndSegmentTransfer(bool Modified, bool OnlyRunningStatus)
+      virtual bool EndSegmentTransfer(bool Modified, bool dummy)
       {
          handlerMutex.unlock();
-
-         cEpgHandlerInstance* h = getHandler();
-         return h->EndSegmentTransfer(Modified, OnlyRunningStatus);
+         getHandler()->EndSegmentTransfer(Modified, dummy);
+         return false;
       }
 
       virtual bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
@@ -946,7 +1007,7 @@ class cEpg2VdrEpgHandler : public cEpgHandler
          cMutexLock lock(&mapMutex);
 
          externIdMap.clear();
-         tell(1, "Start reading external ids from db");
+         tell(1, "Handler: Start reading external ids from db");
          mapDb->clear();
 
          // select extid, channelid
@@ -965,61 +1026,57 @@ class cEpg2VdrEpgHandler : public cEpgHandler
 
          if (selectAll->prepare() != success)
          {
-            tell(0, "Reading external id's from db aborted due to prepare error");
+            tell(0, "Handler: Reading external id's from db aborted due to prepare error");
             delete selectAll;
             return fail;
          }
 
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-            cChannelsLock channelsLock(false);
-            const cChannels* channels = channelsLock.Channels();
-#else
-            cChannels* channels = &Channels;
-#endif
-
-#define CHANNELMARKOBSOLETE "OBSOLETE"
-
-         for (int f = selectAll->find(); f; f = selectAll->fetch())
+         // channel lock scope
          {
-            std::string extid = mapDb->getStrValue("ExternalId");
+            GET_CHANNELS_READ(channels);
 
-            const char* strChannelId = mapDb->getStrValue("ChannelId");
-            const cChannel* channel = channels->GetByChannelID(tChannelID::FromString(strChannelId));
+            for (int f = selectAll->find(); f; f = selectAll->fetch())
+            {
+               std::string extid = mapDb->getStrValue("ExternalId");
 
-            // update channelname in channelmap
+               const char* strChannelId = mapDb->getStrValue("ChannelId");
+               const cChannel* channel = channels->GetByChannelID(tChannelID::FromString(strChannelId));
 
-            if (channel && !isEmpty(channel->Name()) &&
-                (!mapDb->hasValue("CHANNELNAME", channel->Name()) || mapDb->getValue("FORMAT")->isNull()))
-            {
-               mapDb->find();              // get all fields from table (needed for update)!
+               // update channelname in channelmap
 
-               mapDb->setValue("CHANNELNAME", channel->Name());
+               if (channel && !isEmpty(channel->Name()) &&
+                   (!mapDb->hasValue("CHANNELNAME", channel->Name()) || mapDb->getValue("FORMAT")->isNull()))
+               {
+                  mapDb->find();              // get all fields from table (needed for update)!
 
-               if (strstr(channel->Name(), CHANNELMARKOBSOLETE))
-                  mapDb->setValue("UNKNOWNATVDR", yes);
+                  mapDb->setValue("CHANNELNAME", channel->Name());
 
-               if (strstr(channel->Name(), "HD"))
-                  mapDb->setValue("FORMAT", "HD");
-               else if (strstr(channel->Name(), "3D"))
-                  mapDb->setValue("FORMAT", "3D");
-               else
-                  mapDb->setValue("FORMAT", "SD");
+                  if (strstr(channel->Name(), CHANNELMARKOBSOLETE))
+                     mapDb->setValue("UNKNOWNATVDR", yes);
 
-               mapDb->update();
-               mapDb->reset();
-            }
+                  if (strstr(channel->Name(), "HD"))
+                     mapDb->setValue("FORMAT", "HD");
+                  else if (strstr(channel->Name(), "3D"))
+                     mapDb->setValue("FORMAT", "3D");
+                  else
+                     mapDb->setValue("FORMAT", "SD");
 
-            // we should get the merge > 1 channels already via a merge 1 entry of the channelmap!
+                  mapDb->update();
+                  mapDb->reset();
+               }
 
-            if (mapDb->getIntValue("Merge") > 1)
-               continue;
+               // we should get the merge > 1 channels already via a merge 1 entry of the channelmap!
 
-            // insert into map
+               if (mapDb->getIntValue("Merge") > 1)
+                  continue;
 
-            externIdMap[strChannelId] = extid;
+               // insert into map
+
+               externIdMap[strChannelId] = extid;
+            }
          }
 
-         tell(1, "Finished reading external id's from db, got %d id's",
+         tell(1, "Handler: Finished reading external id's from db, got %d id's",
               (int)externIdMap.size());
 
          selectAll->freeResult();
diff --git a/lib/Makefile b/lib/Makefile
index 0fe3774..07c3e36 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -12,7 +12,7 @@ HLIB      = -L. -lhorchi
 DEMO = demo
 TEST = tst
 
-LIBOBJS = common.o config.o db.o epgservice.o dbdict.o json.o
+LIBOBJS = common.o config.o db.o epgservice.o dbdict.o json.o xml.o
 
 ifdef USEJPEG
    LIBOBJS += imgtools.o
@@ -22,11 +22,17 @@ ifdef USECURL
    LIBOBJS += curl.o configuration.o thread.o
 endif
 
-BASELIBS = -lrt -lz -luuid 
+CFLAGS += $(shell pkg-config --cflags uuid)
+CFLAGS += $(shell pkg-config --cflags zlib)
+
+BASELIBS = -lrt
 BASELIBS += $(shell mysql_config --libs_r)
+BASELIBS += $(shell pkg-config --libs uuid)
+BASELIBS += $(shell pkg-config --libs zlib)
+BASELIBS += $(shell pkg-config --libs tinyxml2)
 
 ifdef USECURL
-   BASELIBS += -lcurl 
+   BASELIBS += -lcurl
 endif
 
 ifdef USEEPGS
@@ -47,10 +53,6 @@ ifdef SYSD_NOTIFY
 	CFLAGS += $(shell pkg-config --cflags libsystemd-daemon)
 endif
 
-ifdef DEBUG
-  CFLAGS += -ggdb -O0
-endif
-
 CFLAGS += $(shell mysql_config --include)
 DEFINES += $(USES)
 
@@ -68,21 +70,21 @@ $(LIBTARGET).a : $(LIBOBJS)
 tst: test.o lib
 	$(doLink) test.o $(HLIB) -larchive -lcrypto $(BASELIBS) -o $@
 
-demo: demo.o lib 
+demo: demo.o lib
 	$(doLink) demo.o $(HLIB) -larchive -lcrypto $(BASELIBS) -o $@
 
 pytst: pytst.c python.c python.h hlib
-	$(CC) $(CFLAGS) pytst.c python.c -L./lib -lhorchi $(DLIBS) -o pytst 
+	$(CC) $(CFLAGS) pytst.c python.c -L./lib -lhorchi $(DLIBS) -o pytst
 
 clean:
 	rm -f *.o *~ core $(TEST) $(DEMO) $(LIBTARGET).a
 
 cppchk:
-	cppcheck --template="{file}:{line}:{severity}:{message}" --quiet --force *.c *.h 
+	cppcheck --template="{file}:{line}:{severity}:{message}" --quiet --force *.c *.h
 
 %.o: %.c
 	@echo Compile "$(*F)" ...
-	$(doCompile) $(*F).c -o $@
+	$(doCompile) -O3 $(INCLUDES) -o $@ $<
 
 #--------------------------------------------------------
 # dependencies
@@ -100,9 +102,9 @@ db.o         		:  db.c          		 $(HEADER) db.h
 epgservice.o 		:  epgservice.c  		 $(HEADER) epgservice.h
 dbdict.o     		:  dbdict.c      		 $(HEADER) dbdict.h
 json.o       		:  json.c        		 $(HEADER) json.h
-python.o        :  python.c          $(HEADER) python.h
+xml.o       		:  xml.c        		 $(HEADER) xml.h
+python.o          :  python.c           $(HEADER) python.h
 searchtimer.o     :  searchtimer.c      $(HEADER) searchtimer.h
 
-demo.o       		:  demo.c        		 $(HEADER) 
-test.o       		:  test.c        		 $(HEADER) 
-
+demo.o       		:  demo.c        		 $(HEADER)
+test.o       		:  test.c        		 $(HEADER)
diff --git a/lib/common.c b/lib/common.c
index 8a0e9ac..413b9f5 100644
--- a/lib/common.c
+++ b/lib/common.c
@@ -13,7 +13,7 @@
 #include <arpa/inet.h>
 
 #ifdef USEUUID
-#  include <uuid/uuid.h>
+#  include <uuid.h>
 #endif
 
 #include <stdarg.h>
@@ -1443,7 +1443,7 @@ const char* getMacOf(const char* device)
    int s;
 
    s = socket(AF_INET, SOCK_DGRAM, 0);
-   strcpy(ifr.ifr_name, "eth0");
+   strcpy(ifr.ifr_name, device);
    ioctl(s, SIOCGIFHWADDR, &ifr);
 
    for (int i = 0; i < macTuppel; i++)
diff --git a/lib/common.h b/lib/common.h
index df1c236..28536ec 100644
--- a/lib/common.h
+++ b/lib/common.h
@@ -45,7 +45,6 @@ class MemoryStruct;
   inline long min(long a, long b) { return a < b ? a : b; }
   inline long max(long a, long b) { return a > b ? a : b; }
 #else
-#  define __STL_CONFIG_H
 #  include <vdr/tools.h>
 #endif
 
@@ -133,7 +132,7 @@ int gunzip(MemoryStruct* zippedData, MemoryStruct* unzippedData);
 // MemoryStruct
 //***************************************************************************
 
-struct MemoryStruct
+class MemoryStruct
 {
    public:
 
diff --git a/lib/db.c b/lib/db.c
index 85efad6..9743dc3 100644
--- a/lib/db.c
+++ b/lib/db.c
@@ -278,7 +278,7 @@ int cDbStatement::bind(cDbValue* value, int mode, const char* delim)
    return success;
 }
 
-int cDbStatement::bindAllOut(const char* delim)
+int cDbStatement::bindAllOut(const char* delim, int incTypes, int excTypes)
 {
    int n = 0;
    std::map<std::string, cDbFieldDef*>::iterator f;
@@ -289,7 +289,10 @@ int cDbStatement::bindAllOut(const char* delim)
 
    for (f = tableDef->dfields.begin(); f != tableDef->dfields.end(); f++)
    {
-      if (f->second->getType() & ftMeta)
+      if (!(incTypes & f->second->getType()))  // Include Types
+         continue;
+
+      if (excTypes & f->second->getType())     // Exclude Types
          continue;
 
       bind(f->second, bndOut, n++ ? ", " : "");
@@ -734,15 +737,18 @@ int cDbTable::init(int allowAlter)
 
    // check/create table ...
 
-   if (exist() && allowAlter)
-      validateStructure(allowAlter);
+   if (allowAlter)
+   {
+      if (exist())
+         validateStructure(allowAlter);
 
-   if (createTable() != success)
-      return fail;
+      if (!exist() && createTable() != success)
+         return fail;
 
-   // check/create indices
+      // check/create indices
 
-   createIndices();
+      createIndices();
+   }
 
    // ------------------------------
    // prepare BASIC statements
diff --git a/lib/db.h b/lib/db.h
index 2c92856..43f820e 100644
--- a/lib/db.h
+++ b/lib/db.h
@@ -461,7 +461,7 @@ class cDbStatement : public cDbService
       int bind(cDbTable* aTable, cDbFieldDef* field, int mode, const char* delim);
       int bind(cDbTable* aTable, const char* fname, int mode, const char* delim);
       int bind(cDbFieldDef* field, int mode, const char* delim = 0);
-      int bindAllOut(const char* delim = 0);
+      int bindAllOut(const char* delim = 0, int incTypes = ftData | ftPrimary, int excTypes = 0);
 
       int bindCmp(const char* ctable, cDbValue* value,
                   const char* comp, const char* delim = 0);
@@ -615,7 +615,8 @@ class cDbRow : public cDbService
          int count = 0;
 
          for (int f = 0; f < tableDef->fieldCount(); f++)
-            count += dbValues[f].getChanges();
+            if (dbValues[f].getChanges())
+               count++;
 
          return count;
       }
@@ -729,7 +730,7 @@ class cDbConnection
          {
             connectDropped = yes;
 
-            tell(0, "Calling mysql_init(%ld)", syscall(__NR_gettid));
+            tell(2, "Calling mysql_init(%ld)", syscall(__NR_gettid));
 
             if (!(mysql = mysql_init(0)))
                return errorSql(this, "attachConnection(init)");
@@ -779,7 +780,7 @@ class cDbConnection
       {
          if (mysql)
          {
-            tell(0, "Closing mysql connection and calling mysql_thread_end(%ld)", syscall(__NR_gettid));
+            tell(2, "Closing mysql connection and calling mysql_thread_end(%ld)", syscall(__NR_gettid));
 
             mysql_close(mysql);
             mysql_thread_end();
@@ -1055,7 +1056,7 @@ class cDbConnection
 
       MYSQL* mysql;
 
-      int initialized;
+      // int initialized;
       int attached;
       int inTact;
       int connectDropped;
diff --git a/lib/demo.c b/lib/demo.c
index 0c4c234..347cabf 100644
--- a/lib/demo.c
+++ b/lib/demo.c
@@ -38,13 +38,13 @@ void initConnection()
 void exitConnection()
 {
    cDbConnection::exit();
-   
+
    if (connection)
       delete connection;
 }
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 int demoStatement()
@@ -57,15 +57,15 @@ int demoStatement()
 
    // open table (attach)
 
-   if (eventsDb->open() != success) 
+   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 
+   // select eventid, compshorttext, episodepart, episodelang
+   //   from events
    //     where eventid > ?
 
    cDbStatement* selectByCompTitle = new cDbStatement(eventsDb);
@@ -77,14 +77,14 @@ int demoStatement()
    status += selectByCompTitle->build(" from %s where ", eventsDb->TableName());
    status += selectByCompTitle->bindCmp(0, eventsDb->getField("EventId"), 0, ">");
 
-   status += selectByCompTitle->prepare();   // prepare statement 
+   status += selectByCompTitle->prepare();   // prepare statement
 
    if (status != success)
    {
       // prepare sollte MySQL fehler ausgegeben haben!
 
       delete eventsDb;
-      delete selectByCompTitle; 
+      delete selectByCompTitle;
 
       return fail;
    }
@@ -93,7 +93,7 @@ int demoStatement()
 
    tell(0, "------------------ create some rows  ----------------------");
 
-   eventsDb->clear();     // alle values löschen   
+   eventsDb->clear();     // alle values löschen
 
    for (int i = 0; i < 10; i++)
    {
@@ -103,7 +103,7 @@ int demoStatement()
       eventsDb->setValue(eventsDb->getField("EventId"), 800 + i * 100);
       eventsDb->setValue(eventsDb->getField("ChannelId"), "xxx-yyyy-zzz");
       eventsDb->setValue(eventsDb->getField("Title"), 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
@@ -114,7 +114,7 @@ int demoStatement()
    tell(0, "------------------ done  ----------------------");
 
    tell(0, "-------- select all where eventid > 1000 -------------");
-   
+
    eventsDb->clear();     // alle values löschen
    eventsDb->setValue(eventsDb->getField("EventId"), 1000);
 
@@ -148,7 +148,7 @@ 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. 
+   // 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
 
@@ -161,7 +161,7 @@ int joinDemo()
    delete timerDb;
 
    // init dict fields as needed (normaly done once at programm start)
-   //   init and using the pointer improve the speed since the lookup via 
+   //   init and using the pointer improve the speed since the lookup via
    //   the name is dine only once
 
    // F_INIT(events, EventId); // ergibt: cDbFieldDef* eventsEventId; dbDict.init(eventsEventId, "events", "EventId");
@@ -170,15 +170,15 @@ int joinDemo()
 
    // open tables (attach)
 
-   if (eventsDb->open() != success) 
+   if (eventsDb->open() != success)
       return fail;
 
-   if (imageDb->open() != success) 
+   if (imageDb->open() != success)
       return fail;
 
-   if (imageRefDb->open() != success) 
+   if (imageRefDb->open() != success)
       return fail;
-   
+
    tell(0, "---------------- prepare select statement -------------");
 
    // all images
@@ -192,13 +192,13 @@ int joinDemo()
    cDbValue masterId;
 
    cDbFieldDef imageSizeDef("image", "image", cDBS::ffUInt,  999, cDBS::ftData, 0);  // eine Art ein Feld zu erzeugen
-   imageSize.setField(&imageSizeDef); 
+   imageSize.setField(&imageSizeDef);
    imageUpdSp.setField(imageDb->getField("UpdSp"));
    masterId.setField(eventsDb->getField("MasterId"));
 
    // select e.masterid, r.imagename, r.eventid, r.lfn, length(i.image)
-   //      from imagerefs r, images i, events e 
-   //      where i.imagename = r.imagename 
+   //      from imagerefs r, images i, events e
+   //      where i.imagename = r.imagename
    //         and e.eventid = r.eventid
    //         and (i.updsp > ? or r.updsp > ?)
 
@@ -214,10 +214,10 @@ int joinDemo()
    selectAllImages->bind(&imageSize, cDBS::bndOut);
    selectAllImages->build(")");
    selectAllImages->clrBindPrefix();
-   selectAllImages->build(" from %s r, %s i, %s e where ", 
+   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 (",
-                          "EventId",        // eventsEventId->getDbName(), 
+                          "EventId",        // eventsEventId->getDbName(),
                           imageRefDb->getField("EventId")->getDbName(),
                           "imagename",      // direkt den DB Feldnamen verwenden -> nicht so schön da nicht dynamisch
                           imageRefDb->getField("ImgName")->getDbName()); // ordentlich via dictionary übersetzt -> schön ;)
@@ -235,7 +235,7 @@ int joinDemo()
       delete eventsDb;
       delete imageDb;
       delete imageRefDb;
-      delete selectAllImages; 
+      delete selectAllImages;
 
       return fail;
    }
@@ -280,7 +280,7 @@ int joinDemo()
 }
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 int insertDemo()
@@ -302,7 +302,7 @@ int insertDemo()
 }
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 int findUseEvent()
@@ -324,7 +324,7 @@ int findUseEvent()
    selectEventById->bindAllOut();
    selectEventById->build(" from %s where ", useeventsDb->TableName());
    selectEventById->bind("USEID", cDBS::bndIn | cDBS::bndSet);
-   selectEventById->build(" and %s in (%s)", 
+   selectEventById->build(" and %s in (%s)",
                           useeventsDb->getField("UPDFLG")->getDbName(),
                           Us::getNeeded());
 
@@ -390,8 +390,8 @@ int findUseEvent()
             continue;
          }
 
-         if (field->getFormat() == cDbService::ffAscii || 
-             field->getFormat() == cDbService::ffText || 
+         if (field->getFormat() == cDbService::ffAscii ||
+             field->getFormat() == cDbService::ffText ||
              field->getFormat() == cDbService::ffMText)
          {
             fprintf(f, "%s: %s\n", flds[i], useeventsDb->getStrValue(flds[i]));
@@ -411,11 +411,11 @@ int findUseEvent()
 
    selectEventById->freeResult();
 
-   return done; 
+   return done;
 }
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 int updateRecordingDirectory()
@@ -428,31 +428,31 @@ int updateRecordingDirectory()
    // char* dir = strdup("more~Marvel's Agents of S.H.I.E.L.D.~xxx.ts");
    char* dir = strdup("aaaaa~bbbbbb~ccccc.ts");
    char* pos = strrchr(dir, '~');
-   
+
    if (pos)
    {
       *pos = 0;
-      
+
       for (int level = 0; level < 3; level++)
       {
          recordingDirDb->clear();
 
          recordingDirDb->setValue("VDRUUID", "foobar");
          recordingDirDb->setValue("DIRECTORY", dir);
-         
+
          if (!recordingDirDb->find())
          {
             ins++;
             recordingDirDb->store();
          }
-         
+
          recordingDirDb->reset();
-         
+
          char* pos = strrchr(dir, '~');
          if (pos) *pos=0;
       }
    }
-   
+
    tell(0, "inserted %d directories", ins);
 
    delete recordingDirDb;
@@ -475,16 +475,6 @@ int main(int argc, char** argv)
    if (argc > 1)
       path = argv[1];
 
-   // read deictionary
-   
-   dbDict.setFilterFromNameFct(toFieldFilter);
-   
-   if (dbDict.in(path, ffEpgd) != success)
-   {
-      tell(0, "Invalid dictionary configuration, aborting!");
-      return 1;
-   }
-
    cUserTimes userTimes;
 
    tell(0, "--------------");
@@ -508,6 +498,16 @@ int main(int argc, char** argv)
 
    return 0;
 
+   // read dictionary
+
+   dbDict.setFilterFromNameFct(toFieldFilter);
+
+   if (dbDict.in(path, ffEpgd) != success)
+   {
+      tell(0, "Invalid dictionary configuration, aborting!");
+      return 1;
+   }
+
    // dbDict.show();
 
    initConnection();
@@ -519,7 +519,7 @@ int main(int argc, char** argv)
    tell(0, "uuid: '%s'", getUniqueId());
 
    tell(0, "- - - - - - - - - - - - - - - - - ");
-   
+
    //updateRecordingDirectory();
    findUseEvent();
 
diff --git a/lib/epgservice.c b/lib/epgservice.c
index f913c80..69e5a8e 100644
--- a/lib/epgservice.c
+++ b/lib/epgservice.c
@@ -37,6 +37,7 @@ const char* toName(TimerAction a, int nice)
       case taDelete:  return "delete";
       case taAssumed: return nice ? "-" : "assumed";
       case taFailed:  return "failed";
+      case taReject:  return "reject";
    }
 
    return nice ? "-" : "unknown";
diff --git a/lib/epgservice.h b/lib/epgservice.h
index 65274ba..36d513e 100644
--- a/lib/epgservice.h
+++ b/lib/epgservice.h
@@ -102,7 +102,8 @@ enum TimerAction
    taAdjust   = 'J',
    taDelete   = 'D',
    taAssumed  = 'A',
-   taFailed   = 'F'
+   taFailed   = 'F',
+   taReject   = 'T'
 };
 
 enum TimerType
@@ -300,7 +301,7 @@ class cUserTimes
             if (strchr(strTime, ':'))
             {
                hhmm = atoi(strTime) * 100 + atoi(strchr(strTime, ':')+1);
-               sprintf(hhmmStr, "%02d:%02d", hhmm / 100, hhmm % 100);
+               sprintf(hhmmStr, "%02d%02d", hhmm / 100, hhmm % 100);
                mode = mTime;
             }
             else if (*strTime == '@')
@@ -365,7 +366,7 @@ class cUserTimes
          char* title;
          char* search;
          int hhmm;
-         char hhmmStr[5+TB];
+         char hhmmStr[15+TB];
       };
 
       cUserTimes()
@@ -464,4 +465,15 @@ struct Mysql_Init_Exit_v1_0
    MysqlInitExitAction action;
 };
 
+//***************************************************************************
+// OSD2EPG Services
+//***************************************************************************
+
+#define OSD2WEB_PORT_SERVICE	"osd2web-WebPortService-v1.0"
+
+struct Osd2Web_Port_v1_0
+{
+   int webPort;
+};
+
 #endif // __EPGSERVICE_H
diff --git a/lib/python.c b/lib/python.c
index da3c063..2ec2175 100644
--- a/lib/python.c
+++ b/lib/python.c
@@ -9,6 +9,7 @@
 
 cDbTable* Python::globalEventsDb = 0;
 int Python::globalNamingMode = 0;
+const char* Python::globalTmplExpression = "";
 
 //***************************************************************************
 // Static Interface Methods (Table events)
@@ -131,11 +132,16 @@ PyObject* Python::namingMode(PyObject* /*self*/, PyObject* /*args*/)
    return Py_BuildValue("i", globalNamingMode);
 }
 
+PyObject* Python::tmplExpression(PyObject* /*self*/, PyObject* /*args*/)
+{
+   return Py_BuildValue("s", globalTmplExpression);
+}
+
 //***************************************************************************
 // The Methods
 //***************************************************************************
 
-PyMethodDef Python::eventMethods[] = 
+PyMethodDef Python::eventMethods[] =
 {
    {  "title",           Python::eventTitle,       METH_VARARGS,  "Return the events ..."   },
    {  "shorttext",       Python::eventShortText,   METH_VARARGS,  "Return the events ..."   },
@@ -156,20 +162,21 @@ PyMethodDef Python::eventMethods[] =
    {  "extracol3",       Python::extracol3,        METH_VARARGS,  "Return the events ..."   },
 
    {  "namingmode",      Python::namingMode,       METH_VARARGS,  "Return the ..."          },
+   {  "tmplExpression",  Python::tmplExpression,   METH_VARARGS,  "Return the ..."          },
 
    {            0,                       0,                   0,                        0   }
 };
 
 #if PY_MAJOR_VERSION >= 3
 
-PyModuleDef Python::moduledef = 
+PyModuleDef Python::moduledef =
 {
    PyModuleDef_HEAD_INIT,    // m_base
    "event",                  // m_name     - name of module
    0,                        // m_doc      - module documentation, may be NULL
    -1,                       // m_size     - size of per-interpreter state of the module, or -1 if the module keeps state in global variables.
    eventMethods,             // m_methods
-   0,                        // m_slots    - array of slot definitions for multi-phase initialization, terminated by a {0, NULL} entry. 
+   0,                        // m_slots    - array of slot definitions for multi-phase initialization, terminated by a {0, NULL} entry.
                              //              when using single-phase initialization, m_slots must be NULL.
    0,                        // traverseproc m_traverse - traversal function to call during GC traversal of the module object, or NULL if not needed.
    0,                        // inquiry m_clear         - clear function to call during GC clearing of the module object, or NULL if not needed.
@@ -227,9 +234,9 @@ int Python::init(const char* modulePath)
    if (modulePath)
    {
       char* p;
-      asprintf(&p, "sys.path.append(\"%s\")", modulePath); 
+      asprintf(&p, "sys.path.append(\"%s\")", modulePath);
       PyRun_SimpleString("import sys");
-      PyRun_SimpleString(p); 
+      PyRun_SimpleString(p);
       free(p);
    }
 
@@ -247,19 +254,19 @@ int Python::init(const char* modulePath)
    }
 
    pFunc = PyObject_GetAttrString(pModule, function);
-   
+
    // pFunc is a new reference
-   
-   if (!pFunc || !PyCallable_Check(pFunc)) 
+
+   if (!pFunc || !PyCallable_Check(pFunc))
    {
       if (PyErr_Occurred())
          showError();
-      
+
       tell(0, "Cannot find function '%s'", function);
 
       return fail;
    }
-   
+
    return success;
 }
 
@@ -280,7 +287,7 @@ int Python::exit()
 // Execute
 //***************************************************************************
 
-int Python::execute(cDbTable* eventsDb, int namingmode)
+int Python::execute(cDbTable* eventsDb, int namingmode, const char* tmplExpression)
 {
    PyObject* pValue;
 
@@ -289,10 +296,11 @@ int Python::execute(cDbTable* eventsDb, int namingmode)
 
    globalEventsDb = eventsDb;
    globalNamingMode = namingmode;
+   globalTmplExpression = tmplExpression;
 
    pValue = PyObject_CallObject(pFunc, 0);
-   
-   if (!pValue) 
+
+   if (!pValue)
    {
       showError();
       tell(0, "Python: Call of function '%s()' failed", function);
@@ -306,13 +314,13 @@ int Python::execute(cDbTable* eventsDb, int namingmode)
    PyObject* pyStr = PyUnicode_AsEncodedString(pValue, "utf-8", "replace");
    result = strdup(PyBytes_AsString(pyStr));
    // Py_XDECREF(strExc);
-   
+
 #else
    result = strdup(PyString_AsString(pValue));
 #endif
 
    tell(3, "Result of call: %s", result);
-   
+
    Py_DECREF(pValue);
 
    return success;
@@ -326,7 +334,7 @@ void Python::showError()
 {
    const char* error = "";
    PyObject *ptype = 0, *pvalue = 0, *ptraceback = 0;
-   
+
    PyErr_Fetch(&ptype, &pvalue, &ptraceback);
 
 #if PY_MAJOR_VERSION >= 3
diff --git a/lib/python.h b/lib/python.h
index 6f747f8..eb50964 100644
--- a/lib/python.h
+++ b/lib/python.h
@@ -26,7 +26,7 @@ class Python
       int init(const char* modulePath = 0);
       int exit();
 
-      int execute(cDbTable* eventsDb, int namingmode);
+      int execute(cDbTable* eventsDb, int namingmode, const char* tmplExpression);
 
       const char* getResult() { return result ? result : ""; }
 
@@ -48,13 +48,14 @@ class Python
       static PyObject* episodePart(PyObject* self, PyObject* args);
       static PyObject* episodeNumber(PyObject* self, PyObject* args);
       static PyObject* namingMode(PyObject* self, PyObject* args);
+      static PyObject* tmplExpression(PyObject* self, PyObject* args);
 
       void showError();
 
       // data
 
       PyObject* pModule;
-      PyObject* pFunc;      
+      PyObject* pFunc;
 
       char* file;
       char* function;
@@ -64,6 +65,7 @@ class Python
 
       static cDbTable* globalEventsDb;
       static int globalNamingMode;
+      static const char* globalTmplExpression;
       static PyMethodDef eventMethods[];
 
 #if PY_MAJOR_VERSION >= 3
diff --git a/lib/searchtimer.c b/lib/searchtimer.c
index 98fc31b..971230f 100644
--- a/lib/searchtimer.c
+++ b/lib/searchtimer.c
@@ -68,7 +68,7 @@ cSearchTimer::cSearchTimer()
    selectSearchtimerMaxModSp = 0;
    selectAllTimer = 0;
    selectTimerByEvent = 0;
-   selectConflictingTimers = 0;
+   // selectConflictingTimers = 0;
 
    ptyRecName = 0;
    lastSearchTimerUpdate = 0;
@@ -217,19 +217,19 @@ int cSearchTimer::initDb()
    //        and vdruuid = ?
    //     group by SUBSTRING_INDEX(channelid, '-', 3);
 
-   selectConflictingTimers = new cDbStatement(timerDb);
+   // selectConflictingTimers = new cDbStatement(timerDb);
 
-   selectConflictingTimers->build("select ");
-   selectConflictingTimers->bindAllOut();
-   selectConflictingTimers->build(" from %s where %s in ('P','R')",
-                                  timerDb->TableName(), timerDb->getField("STATE")->getDbName());
-   selectConflictingTimers->build(" and active = 1");
-   selectConflictingTimers->bindText("day + starttime div 100 * 60 * 60 + starttime % 100 * 60", &startValue, ">=", " and ");
-   selectConflictingTimers->bindText("day + starttime div 100 * 60 * 60 + starttime % 100 * 60", &endValue, "<=", " and ");
-   selectConflictingTimers->bind("VDRUUID", cDBS::bndIn | cDBS::bndSet, " and ");
-   selectConflictingTimers->build(" group by SUBSTRING_INDEX(channelid, '-', 3)");
+   // selectConflictingTimers->build("select ");
+   // selectConflictingTimers->bindAllOut();
+   // selectConflictingTimers->build(" from %s where %s in ('P','R')",
+   //                                timerDb->TableName(), timerDb->getField("STATE")->getDbName());
+   // selectConflictingTimers->build(" and active = 1");
+   // selectConflictingTimers->bindText("day + starttime div 100 * 60 * 60 + starttime % 100 * 60", &startValue, ">=", " and ");
+   // selectConflictingTimers->bindText("day + starttime div 100 * 60 * 60 + starttime % 100 * 60", &endValue, "<=", " and ");
+   // selectConflictingTimers->bind("VDRUUID", cDBS::bndIn | cDBS::bndSet, " and ");
+   // selectConflictingTimers->build(" group by SUBSTRING_INDEX(channelid, '-', 3)");
 
-   status += selectConflictingTimers->prepare();
+   // status += selectConflictingTimers->prepare();
 
    // ----------
 
@@ -251,7 +251,7 @@ int cSearchTimer::exitDb()
       delete selectDoneTimer;           selectDoneTimer = 0;
       delete selectChannelFromMap;      selectChannelFromMap = 0;
       delete selectAllTimer;            selectAllTimer = 0;
-      delete selectConflictingTimers;   selectConflictingTimers = 0;
+      // delete selectConflictingTimers;   selectConflictingTimers = 0;
       delete selectTimerByEvent;        selectTimerByEvent = 0;
 
       delete mapDb;                     mapDb = 0;
@@ -319,7 +319,7 @@ cDbStatement* cSearchTimer::prepareSearchStatement(cDbRow* searchTimer, cDbTable
    }
 
    select->build("select ");
-   select->bindAllOut();
+   select->bindAllOut(0, cDBS::ftData | cDBS::ftPrimary, cDBS::ftMeta);
    select->setBindPrefix("c.");
    select->bind(mapDb, "FORMAT", cDBS::bndOut, ", ");
    select->clrBindPrefix();
@@ -726,7 +726,7 @@ int cSearchTimer::getDoneFor(cDbRow* searchTimer, cDbRow* useevent, json_t* obj)
 
    if (!useeventsDb->find())
    {
-      tell(0, "Warning: Event '%s/%"PRId64"/%s' not found",
+      tell(0, "Warning: Event '%s/%" PRId64 "/%s' not found",
            useevent->getStrValue("CHANNELID"),
            useevent->getBigintValue("CNTEVENTID"),
            useevent->getStrValue("CNTSOURCE"));
@@ -956,13 +956,20 @@ int cSearchTimer::prepareDoneSelect(cDbRow* useeventsRow, int repeatfields, cDbS
 
    timersDoneDb->clear();
    timersDoneDb->setValue("COMPTITLE", useeventsRow->getStrValue("COMPTITLE"));
-   timersDoneDb->setValue("EPISODECOMPNAME", useeventsRow->getStrValue("EPISODECOMPNAME"));
-   timersDoneDb->setValue("EPISODECOMPSHORTNAME", useeventsRow->getStrValue("EPISODECOMPSHORTNAME"));
 
-   timersDoneDb->setValue("COMPSHORTTEXT", useeventsRow->getStrValue("COMPSHORTTEXT"));
-   timersDoneDb->setValue("EPISODECOMPPARTNAME", useeventsRow->getStrValue("EPISODECOMPPARTNAME"));
+   if (!useeventsRow->getValue("COMPSHORTTEXT")->isEmpty())
+      timersDoneDb->setValue("COMPSHORTTEXT", useeventsRow->getStrValue("COMPSHORTTEXT"));
 
-   timersDoneDb->setValue("COMPLONGDESCRIPTION", useeventsRow->getStrValue("COMPLONGDESCRIPTION"));
+   if (!useeventsRow->getValue("EPISODECOMPNAME")->isEmpty())
+      timersDoneDb->setValue("EPISODECOMPNAME", useeventsRow->getStrValue("EPISODECOMPNAME"));
+   if (!useeventsRow->getValue("EPISODECOMPSHORTNAME")->isEmpty())
+      timersDoneDb->setValue("EPISODECOMPSHORTNAME", useeventsRow->getStrValue("EPISODECOMPSHORTNAME"));
+
+   if (!useeventsRow->getValue("EPISODECOMPPARTNAME")->isEmpty())
+      timersDoneDb->setValue("EPISODECOMPPARTNAME", useeventsRow->getStrValue("EPISODECOMPPARTNAME"));
+
+   if (!useeventsRow->getValue("COMPLONGDESCRIPTION")->isEmpty())
+      timersDoneDb->setValue("COMPLONGDESCRIPTION", useeventsRow->getStrValue("COMPLONGDESCRIPTION"));
 
    select = selectDoneTimer;
 
@@ -982,6 +989,7 @@ int cSearchTimer::createTimer(int id)
 
    const char* channelname = "";
    int namingmode = searchtimerDb->getIntValue("NAMINGMODE");
+   const char* tmplExpression = timerDb->getStrValue("TEMPLATE");
 
    // ------------------------------------------
    // lookup channel name to store in timersdone
@@ -1012,6 +1020,7 @@ int cSearchTimer::createTimer(int id)
    timerRow.setValue("EVENTID", useeventsDb->getIntValue("USEID"));
    timerRow.setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID"));
    timerRow.setValue("NAMINGMODE", namingmode);
+   timerRow.setValue("TEMPLATE", tmplExpression);
    timerRow.setValue("CHILDLOCK", searchtimerDb->getIntValue("CHILDLOCK"));
    timerRow.setValue("VDRUUID", searchtimerDb->getStrValue("VDRUUID"));
    timerRow.setValue("VPS", searchtimerDb->getIntValue("VPS"));
@@ -1028,7 +1037,7 @@ int cSearchTimer::createTimer(int id)
    {
       // execupe python - calc recording name
 
-      if (ptyRecName->execute(useeventsDb, namingmode) == success)
+      if (ptyRecName->execute(useeventsDb, namingmode, tmplExpression) == success)
       {
          tell(0, "Info: The recording name calculated by 'recording.py' is '%s'",
               ptyRecName->getResult());
@@ -1045,22 +1054,29 @@ int cSearchTimer::createTimer(int id)
    if (status == success)
    {
       timersDoneDb->clear();
-      timersDoneDb->setValue("STATE", "Q");          // -> reQuested via timerdistribution
+      timersDoneDb->setCharValue("STATE", tdsTimerRequested);
       timersDoneDb->setValue("SOURCE", "epgd");
       timersDoneDb->setValue("AUTOTIMERID", id);
       timersDoneDb->setValue("TIMERID", timerid);
       timersDoneDb->setValue("TITLE", useeventsDb->getStrValue("TITLE"));
       timersDoneDb->setValue("COMPTITLE", useeventsDb->getStrValue("COMPTITLE"));
-      timersDoneDb->setValue("SHORTTEXT", useeventsDb->getStrValue("SHORTTEXT"));
-      timersDoneDb->setValue("COMPSHORTTEXT", useeventsDb->getStrValue("COMPSHORTTEXT"));
-      timersDoneDb->setValue("LONGDESCRIPTION", useeventsDb->getStrValue("LONGDESCRIPTION"));
-      timersDoneDb->setValue("COMPLONGDESCRIPTION", useeventsDb->getStrValue("COMPLONGDESCRIPTION"));
+
+      if (!useeventsDb->getValue("SHORTTEXT")->isEmpty())
+         timersDoneDb->setValue("SHORTTEXT", useeventsDb->getStrValue("SHORTTEXT"));
+      if (!useeventsDb->getValue("COMPSHORTTEXT")->isEmpty())
+         timersDoneDb->setValue("COMPSHORTTEXT", useeventsDb->getStrValue("COMPSHORTTEXT"));
+
+      if (!useeventsDb->getValue("LONGDESCRIPTION")->isEmpty())
+         timersDoneDb->setValue("LONGDESCRIPTION", useeventsDb->getStrValue("LONGDESCRIPTION"));
+      if (!useeventsDb->getValue("COMPLONGDESCRIPTION")->isEmpty())
+         timersDoneDb->setValue("COMPLONGDESCRIPTION", useeventsDb->getStrValue("COMPLONGDESCRIPTION"));
 
       if (!useeventsDb->getValue("EPISODECOMPNAME")->isEmpty())
          timersDoneDb->setValue("EPISODECOMPNAME", useeventsDb->getStrValue("EPISODECOMPNAME"));
       if (!useeventsDb->getValue("EPISODECOMPSHORTNAME")->isEmpty())
          timersDoneDb->setValue("EPISODECOMPSHORTNAME", useeventsDb->getStrValue("EPISODECOMPSHORTNAME"));
       if (!useeventsDb->getValue("EPISODECOMPPARTNAME")->isEmpty())
+
          timersDoneDb->setValue("EPISODECOMPPARTNAME", useeventsDb->getStrValue("EPISODECOMPPARTNAME"));
       if (!useeventsDb->getValue("EPISODELANG")->isEmpty())
          timersDoneDb->setValue("EPISODELANG", useeventsDb->getStrValue("EPISODELANG"));
@@ -1211,7 +1227,7 @@ int cSearchTimer::modifyCreateTimer(cDbRow* timerRow, int& newid)
 //***************************************************************************
 // Check Timer Conficts
 //***************************************************************************
-
+/*
 int cSearchTimer::checkTimerConflicts(std::string& mailBody)
 {
    int conflicts = 0;
@@ -1291,11 +1307,11 @@ int cSearchTimer::checkTimerConflicts(std::string& mailBody)
 
    return conflicts;
 }
-
+*/
 //***************************************************************************
 // Get Used Transponder At
 //***************************************************************************
-
+/*
 int cSearchTimer::getUsedTransponderAt(time_t lStartTime, time_t lEndTime, std::string& mailPart)
 {
    char buf[1024+TB];
@@ -1335,7 +1351,7 @@ int cSearchTimer::getUsedTransponderAt(time_t lStartTime, time_t lEndTime, std::
 
    return count;
 }
-
+*/
 // //***************************************************************************
 // // Reject Timer
 // //***************************************************************************
diff --git a/lib/searchtimer.h b/lib/searchtimer.h
index b67eecb..c99bc6b 100644
--- a/lib/searchtimer.h
+++ b/lib/searchtimer.h
@@ -35,27 +35,27 @@ class cSearchTimer
 
       int getSearchMatches(cDbRow* searchTimer, json_t* obj);
       int getDoneFor(cDbRow* searchTimer, cDbRow* useevent, json_t* obj);
-      int checkTimerConflicts(std::string& mailBody);
-      int getUsedTransponderAt(time_t lStartTime, time_t lEndTime, std::string& mailBody);
+      // int checkTimerConflicts(std::string& mailBody);
+      // int getUsedTransponderAt(time_t lStartTime, time_t lEndTime, std::string& mailBody);
 
       int prepareDoneSelect(cDbRow* useeventsRow, int repeatfields, cDbStatement*& select);
       cDbStatement* prepareSearchStatement(cDbRow* searchTimer, cDbTable* db);
       int matchCriterias(cDbRow* searchTimer, cDbRow* event);
-   
+
    private:
 
       int createTimer(int id);
       int modifyCreateTimer(cDbRow* timerRow, int& newid);
       // int rejectTimer(cDbRow* timerRow);
 
-      // data 
+      // data
 
       Python* ptyRecName;
 
       cDbConnection* connection;
 
       cDbTable* searchtimerDb;
-      cDbTable* useeventsDb; 
+      cDbTable* useeventsDb;
       cDbTable* timersDoneDb;
       cDbTable* timerDb;
       cDbTable* mapDb;
@@ -65,10 +65,10 @@ class cSearchTimer
       cDbStatement* selectDoneTimer;
       cDbStatement* selectActiveSearchtimers;
       cDbStatement* selectSearchtimerMaxModSp;
-      cDbStatement* selectActiveVdrs;
       cDbStatement* selectAllTimer;
       cDbStatement* selectTimerByEvent;
-      cDbStatement* selectConflictingTimers;
+      // cDbStatement* selectActiveVdrs;
+      // cDbStatement* selectConflictingTimers;
 
       cDbValue startValue;
       cDbValue endValue;
diff --git a/lib/test.c b/lib/test.c
index e8a0001..6de7fdb 100644
--- a/lib/test.c
+++ b/lib/test.c
@@ -17,6 +17,7 @@
 #include "db.h"
 #include "epgservice.h"
 #include "dbdict.h"
+#include "xml.h"
 //#include "wol.h"
 
 cDbConnection* connection = 0;
@@ -47,13 +48,13 @@ void initConnection()
 void exitConnection()
 {
    cDbConnection::exit();
-   
+
    if (connection)
       delete connection;
 }
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 void chkCompress()
@@ -76,7 +77,7 @@ void chkCompress()
 }
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 void chkStatement1()
@@ -84,8 +85,8 @@ void chkStatement1()
    cDbTable* epgDb = new cDbTable(connection, "events");
 
    if (epgDb->open() != success)
-   { 
-      tell(0, "Could not access database '%s:%d' (%s)", 
+   {
+      tell(0, "Could not access database '%s:%d' (%s)",
            cDbConnection::getHost(), cDbConnection::getPort(), epgDb->TableName());
 
       return ;
@@ -94,14 +95,14 @@ void chkStatement1()
    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 < ? 
+   // 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());
@@ -110,9 +111,9 @@ void chkStatement1()
    updateDelFlg->build(" where ");
    updateDelFlg->bind(epgDb->getField("ChannelId"), cDBS::bndIn | cDBS::bndSet);
    updateDelFlg->bind(epgDb->getField("Source"), cDBS::bndIn | cDBS::bndSet, " and ");
-   
+
    updateDelFlg->bindCmp(0, endTime, ">", " and ");
-   
+
    updateDelFlg->bindCmp(0, epgDb->getField("StartTime"), 0, "<" ,  " and ");
    updateDelFlg->bindCmp(0, epgDb->getField("TableId"),   0, ">" ,  " and (");
    updateDelFlg->bindCmp(0, epgDb->getField("TableId"),   0, "=" ,  " or (");
@@ -125,7 +126,7 @@ void chkStatement1()
 }
 
 // //***************************************************************************
-// // 
+// //
 // //***************************************************************************
 
 // void chkStatement2()
@@ -140,13 +141,13 @@ void chkStatement1()
 //       return ;
 
 //    tell(0, "---------------------------------------------------");
- 
+
 //    cDbStatement* selectAllImages = new cDbStatement(imageRefDb);
 
-//    cDbValue imageData; 
+//    cDbValue imageData;
 //    imageData.setField(imageDb->getField(cTableImages::fiImage));
 
-//    // select r.imagename, r.eventid, r.lfn, i.image from imagerefs r, images i 
+//    // 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 ");
@@ -174,7 +175,7 @@ void chkStatement1()
 // }
 
 // //***************************************************************************
-// // 
+// //
 // //***************************************************************************
 
 // void chkStatement3()
@@ -192,7 +193,7 @@ void chkStatement1()
 //       return ;
 
 //    tell(0, "---------------------------------------------------");
- 
+
 //    cDbStatement* s = new cDbStatement(epgDb);
 
 //    s->build("select ");
@@ -266,7 +267,7 @@ void chkStatement1()
 // }
 
 // //***************************************************************************
-// // 
+// //
 // //***************************************************************************
 
 // void chkStatement4()
@@ -280,11 +281,11 @@ void chkStatement1()
 //    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 
+//    // 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.image is not null
 //    //         and (i.updsp > ? or r.updsp > ?);
 
 //    cDBS::FieldDef masterFld = { "masterid", cDBS::ffUInt,  0, 999, cDBS::ftData };
@@ -308,10 +309,10 @@ void chkStatement1()
 //    selectAllImages->setBindPrefix("i.");
 //    selectAllImages->bind(&imageData, cDBS::bndOut, ", ");
 //    selectAllImages->clrBindPrefix();
-//    selectAllImages->build(" from %s r, %s i, %s e where ", 
+//    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, 
+//                           eventDb->getField(cTableEvents::fiEventId)->name,
 //                           imageRefDb->getField(cTableImageRefs::fiEventId)->name,
 //                           imageDb->getField(cTableImageRefs::fiImgName)->name,
 //                           imageRefDb->getField(cTableImageRefs::fiImgName)->name,
@@ -326,7 +327,7 @@ void chkStatement1()
 //    imageRefDb->clear();
 //    imageRefDb->setValue(cTableImageRefs::fiUpdSp, 1377733333L);
 //    imageUpdSp.setValue(1377733333L);
-   
+
 //    int count = 0;
 //    for (int res = selectAllImages->find(); res; res = selectAllImages->fetch())
 //    {
@@ -336,7 +337,7 @@ void chkStatement1()
 // }
 
 // //***************************************************************************
-// // 
+// //
 // //***************************************************************************
 
 // int structure()
@@ -346,7 +347,7 @@ void chkStatement1()
 
 //    if (table->open(yes) != success)
 //       return fail;
-   
+
 //    // table->validateStructure();
 
 //    delete table;
@@ -409,10 +410,10 @@ void removeTag(char* xml, const char* tag)
 {
    std::string sTag = "<" + std::string(tag) + ">";
    std::string eTag = "</" + std::string(tag) + ">";
-   
+
    const char* s;
    const char* e;
-   
+
    if ((s = strstr(xml, sTag.c_str())) && (e = strstr(xml, eTag.c_str())))
    {
       char tmp[1000+TB];
@@ -423,11 +424,11 @@ void removeTag(char* xml, const char* tag)
 
       if (e <= s)
          return;
-      
+
       sprintf(tmp, "%.*s%s", int(s-xml), xml, e);
 
       strcpy(xml, tmp);
-   }  
+   }
 }
 
 //***************************************************************************
@@ -439,7 +440,7 @@ int insertTag(char* xml, const char* parent, const char* tag, int value)
    char tmp[1000+TB];
    std::string sTag = "<" + std::string(parent) + ">";
    const char* s;
-   
+
    if ((s = strstr(xml, sTag.c_str())))
    {
       s += strlen(sTag.c_str());
@@ -456,7 +457,7 @@ int insertTag(char* xml, const char* parent, const char* tag, int value)
 }
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 void statementrecording()
@@ -470,7 +471,7 @@ void statementrecording()
 
    recordingListDb->clear();
 
-#ifdef USEMD5   
+#ifdef USEMD5
    md5Buf md5path;
    createMd5("rec->FileName() dummy", md5path);
    recordingListDb->setValue("MD5PATH", md5path);
@@ -480,7 +481,7 @@ void statementrecording()
 
    recordingListDb->setValue("OWNER", "me");
    recordingListDb->setValue("STARTTIME", 12121212);
-   
+
    insert = !recordingListDb->find();
    recordingListDb->clearChanged();
 
@@ -502,16 +503,16 @@ void statementrecording()
    tell(0, "#3 %d changes", recordingListDb->getChanges());
 
    // don't toggle uuid if already set!
-   
+
    if (recordingListDb->getValue("VDRUUID")->isNull())
       recordingListDb->setValue("VDRUUID", "11111");
-   
+
    if (insert || recordingListDb->getChanges())
    {
       tell(0, "storing '%s' due to %d changes ", insert ? "insert" : "update", recordingListDb->getChanges());
       recordingListDb->store();
    }
-   
+
    recordingListDb->reset();
 
    tell(0, "---------------------------------");
@@ -520,7 +521,7 @@ void statementrecording()
 }
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 void statementTimer()
@@ -540,16 +541,16 @@ void statementTimer()
 
    // select t.*,
    //       e.eventid, e.channelid, e.title, e.shorttext, e.shortdescription, e.category, e.genre, e.tipp
-   //    from timers t left outer join events e 
+   //    from timers t left outer join events e
    //       on (t.eventid = e.masterid and e.updflg in (...))
-   //    where 
+   //    where
    //      t.state in (?)
 
    timerState.setField(&timerStateDef);
    timerAction.setField(&timerActionDef);
 
    cDbStatement* selectAllTimer = new cDbStatement(timerDb);
-   
+
    selectAllTimer->build("select ");
    selectAllTimer->setBindPrefix("t.");
    selectAllTimer->bindAllOut();
@@ -558,12 +559,12 @@ void statementTimer()
    selectAllTimer->bind(useeventsDb, "CHANNELID", cDBS::bndOut, ", ");
    selectAllTimer->bind(useeventsDb, "TITLE", cDBS::bndOut, ", ");
    selectAllTimer->bind(useeventsDb, "SHORTTEXT", cDBS::bndOut, ", ");
-   selectAllTimer->bind(useeventsDb, "SHORTDESCRIPTION", cDBS::bndOut, ", "); 
+   selectAllTimer->bind(useeventsDb, "SHORTDESCRIPTION", cDBS::bndOut, ", ");
    selectAllTimer->bind(useeventsDb, "CATEGORY", cDBS::bndOut, ", ");
    selectAllTimer->bind(useeventsDb, "GENRE", cDBS::bndOut, ", ");
    selectAllTimer->bind(useeventsDb, "TIPP", cDBS::bndOut, ", ");
    selectAllTimer->clrBindPrefix();
-   selectAllTimer->build(" from %s t left outer join %s e", 
+   selectAllTimer->build(" from %s t left outer join %s e",
                          timerDb->TableName(), "eventsviewplain");
    selectAllTimer->build(" on (t.eventid = e.cnt_useid) and e.updflg in (%s)", cEventState::getVisible());
 
@@ -572,7 +573,7 @@ void statementTimer()
 
    selectAllTimer->bindInChar("t", timerDb->getField("STATE")->getDbName(), &timerState, " and (");
    selectAllTimer->build(" or t.%s is null)", timerDb->getField("STATE")->getDbName());
-   
+
    selectAllTimer->bindInChar("t", timerDb->getField("ACTION")->getDbName(), &timerAction, " and (");
    selectAllTimer->build(" or t.%s is null)", timerDb->getField("ACTION")->getDbName());
 
@@ -592,7 +593,7 @@ void statementTimer()
 
    for (int found = selectAllTimer->find(); found; found = selectAllTimer->fetch())
    {
-      tell(0, "%ld) %s/%s- %s", 
+      tell(0, "%ld) %s/%s- %s",
            timerDb->getIntValue("ID"),
            timerDb->getStrValue("STATE"),
            timerDb->getStrValue("ACTION"),
@@ -624,10 +625,10 @@ std::map<std::string, int> transponders;
 int tsp(std::string transponder)
 {
    size_t endpos = transponder.find_last_of("-");
-   
+
    if (endpos == std::string::npos)
       return 0;
-   
+
    transponder = transponder.substr(0, endpos);
    transponders[transponder]++;
 
@@ -643,39 +644,54 @@ int main(int argc, char** argv)
    cEpgConfig::logstdout = yes;
    cEpgConfig::loglevel = 2;
 
-/*   const char* interface = getFirstInterface();
+
+   cXml xml;
+
+   xml.set("<epg2vdr><imagecount>3</imagecount><scrseriesid>255974</scrseriesid><year>2017</year><category>Serie</category><country>D</country><genre>Thiller</genre><director>Franzi Hörisch</director><actor>Cheryl Shepard (Sydney), Mickey Hardt (Mathis), David C. Bunners (Holger), Constantin Lücke (Patrick), Gerry Hungbauer (Thomas), Brigitte Antonius (Johanna), Hermann Toelcke (Gunter), Anja Franke (Merle), Claus Dieter Clausnitzer (Hannes), Maria Fuchs (Carla), Joachim Kretzer (Torben) [...]
+
+   for (XMLElement* e = xml.getFirst(); e; e = xml.getNext(e))
+      printf("%s [%s]\n", e->Name(), e->GetText());
+
+   printf("[%s]\n", xml.toText());
+   return 0;
+
+   tell(0, "'%s' - '%s'", getIpOf("enp3s0"), getMacOf("enp3s0"));
+
+   return 0;
+
+   const char* interface = getFirstInterface();
 
    tell(0, "%s: %s - %s [%s]", getFirstInterface(), getIpOf(""), getMaskOf(""), getMacOf(""));
    tell(0, "%s: %s - %s [%s]", getFirstInterface(), getIpOf(interface), getMaskOf(interface), getMacOf(interface));
-  
+
    // if (sendWol(argv[1], bcastAddressOf(getIpOf(interface), getMaskOf(interface))) != success)
    //    tell(0, "Error occured during sending the WOL magic packet for mac address '%s' via bcat '%s'",
    //         argv[1], bcastAddressOf(getIpOf(interface), getMaskOf(interface)));
 
    return 0;
-  
+
    tsp("S19.2E-1-1109-5402");
    tsp("S19.2E-1-1109-5404");
    tsp("S19.2E-1-1112-5404");
 
    std::map<std::string, int>::iterator it;
-   
+
    for (it = transponders.begin(); it != transponders.end(); it++)
       printf("'%s' (%d)\n", it->first.c_str(), it->second);
-   
+
    return 0;
-      
+
    printf("%s\n", getInterfaces());
 
-   
+
    if (argc > 1)
    {
       int timerId = getTimerIdOf(argv[1]);
       char aux[10000+TB];
       strcpy(aux, argv[1]);
-      
+
       tell(0, "TimerId = %d", timerId);
-      
+
       removeTag(aux, "timerid");
       tell(0, "aux: '%s'", aux);
       insertTag(aux, "epgd", "timerid", 677776);
@@ -683,14 +699,14 @@ int main(int argc, char** argv)
 
       return 0;
    }
-*/
+
    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
@@ -709,17 +725,17 @@ int main(int argc, char** argv)
       tell(0, "Invalid dictionary configuration, aborting!");
       return 1;
    }
-   
+
    // dbDict.show();
-   
+
    initConnection();
 
    /*
    cDbTable* table = new cDbTable(connection, "_test");
 
    if (table->open(yes) != success)
-   { 
-      tell(0, "Could not access database '%s:%d' (%s)", 
+   {
+      tell(0, "Could not access database '%s:%d' (%s)",
            cDbConnection::getHost(), cDbConnection::getPort(), table->TableName());
 
       return 0;
@@ -735,7 +751,7 @@ int main(int argc, char** argv)
    table->store();
 
    delete table;
-   
+
    tell(0, "---------------------------------------------------");
    */
    // structure();
diff --git a/lib/vdrlocks.h b/lib/vdrlocks.h
new file mode 100644
index 0000000..c4aafd7
--- /dev/null
+++ b/lib/vdrlocks.h
@@ -0,0 +1,78 @@
+/*
+ * vdrlocks.h
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef __VDRLOCKS_H
+#  define __VDRLOCKS_H
+
+#  ifdef VDR_PLUGIN
+
+#  include <vdr/config.h>
+#  include <vdr/tools.h>
+
+/*
+ * It must always be done in the sequence:
+ *    Timers -> Channels -> Recordings -> Schedules
+*/
+
+//***************************************************************************
+// Timer List Lock Macros
+//***************************************************************************
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#  define GET_TIMERS_READ(name) USE_LIST_LOCK_READ(Timers);  \
+   const cTimers* name = Timers;
+#else
+#  define GET_TIMERS_READ(name) cTimers* name = &Timers;
+#endif
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#  define GET_TIMERS_WRITE(name) USE_LIST_LOCK_WRITE(Timers);   \
+   cTimers* name = Timers;
+#else
+#  define GET_TIMERS_WRITE(name) cTimers* name = &Timers;
+#endif
+
+//***************************************************************************
+// Channel List Lock Macros
+//***************************************************************************
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#  define GET_CHANNELS_READ(name) USE_LIST_LOCK_READ(Channels);  \
+   const cChannels* name = Channels;
+#else
+#  define GET_CHANNELS_READ(name) cChannels* name = &Channels;
+#endif
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#  define GET_CHANNELS_WRITE(name) USE_LIST_LOCK_WRITE(Channels);   \
+   cChannels* name = Channels;
+#else
+#  define GET_CHANNELS_WRITE(name) cChannels* name = &Channels;
+#endif
+
+//***************************************************************************
+// Recording List Lock Macros
+//***************************************************************************
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#  define GET_RECORDINGS_READ(name) USE_LIST_LOCK_READ(Recordings);  \
+   const cRecordings* name = Recordings;
+#else
+#  define GET_RECORDINGS_READ(name) cRecordings* name = &Recordings;
+#endif
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#  define GET_RECORDINGS_WRITE(name) USE_LIST_LOCK_WRITE(Recordings);   \
+   cRecordings* name = Recordings;
+#else
+#  define GET_RECORDINGS_WRITE(name) cRecordings* name = &Recordings;
+#endif
+
+//***************************************************************************
+#  endif // VDR_PLUGIN
+
+#endif //___VDRLOCKS_H
diff --git a/lib/xml.c b/lib/xml.c
new file mode 100644
index 0000000..e859f5f
--- /dev/null
+++ b/lib/xml.c
@@ -0,0 +1,99 @@
+/*
+ *
+ * xml.h
+ *
+ * (c) 2017 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ */
+
+//***************************************************************************
+// Include
+//***************************************************************************
+
+#include "xml.h"
+
+//***************************************************************************
+// ctor
+//***************************************************************************
+
+cXml::cXml()
+   : printer(0, yes)
+{
+   root = 0;
+}
+
+int cXml::create(const char* rootName)
+{
+   root = doc.NewElement(rootName);
+   doc.InsertFirstChild(root);
+
+   return success;
+}
+
+int cXml::set(const char* data)
+{
+   if (doc.Parse(data) != XML_SUCCESS)
+      return fail;
+
+   root = doc.FirstChild();
+
+   return root ? success : fail;
+}
+
+//***************************************************************************
+// Append Element
+//***************************************************************************
+
+XMLNode* cXml::appendElement(const char* name, const char* value, XMLNode* node)
+{
+   XMLElement* element = doc.NewElement(name);
+
+   element->SetText(value);
+   node = node ? node : root;
+   node->InsertEndChild(element);
+
+   return element;
+}
+
+XMLNode* cXml::appendElement(const char* name, int value, XMLNode* node)
+{
+   XMLElement* element = doc.NewElement(name);
+
+   element->SetText(value);
+   node = node ? node : root;
+   node->InsertEndChild(element);
+
+   return element;
+}
+
+//***************************************************************************
+// To Text
+//***************************************************************************
+
+const char* cXml::toText()
+{
+   doc.Accept(&printer);
+
+   return printer.CStr();
+}
+
+XMLElement* cXml::getFirst(XMLNode* node)
+{
+   node = node ? node : root;
+   return node->FirstChildElement();
+}
+
+XMLElement* cXml::getNext(XMLNode* node)
+{
+   return node->NextSiblingElement();
+}
+
+XMLElement* cXml::getElementByName(const char* name, XMLElement* element)
+{
+   if (element)
+      return element->FirstChildElement(name);
+
+   return root->FirstChildElement(name);
+}
diff --git a/lib/xml.h b/lib/xml.h
new file mode 100644
index 0000000..2c3450a
--- /dev/null
+++ b/lib/xml.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * xml.h
+ *
+ * (c) 2017 Jörg Wendel
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ */
+
+#ifndef __XML_H
+#define __XML_H
+
+//***************************************************************************
+// Include
+//***************************************************************************
+
+#include <tinyxml2.h>
+
+#include "common.h"
+
+using namespace tinyxml2;
+
+//***************************************************************************
+// Class cXml
+//***************************************************************************
+
+class cXml
+{
+   public:
+
+      cXml();
+
+      int create(const char* rootName);
+      int set(const char* data);
+
+      XMLNode* appendElement(const char* name, const char* value, XMLNode* node = 0);
+      XMLNode* appendElement(const char* name, int value, XMLNode* node = 0);
+
+      XMLNode* getRoot()     { return root; }
+      XMLElement* getFirst(XMLNode* node = 0);
+      XMLElement* getNext(XMLNode* node);
+
+      XMLElement* getElementByName(const char* name, XMLElement* element = 0);
+
+      const char* toText();
+
+   private:
+
+      XMLDocument doc;
+      XMLNode* root;
+      XMLPrinter printer;
+};
+
+//***************************************************************************
+#endif // __XML_H
diff --git a/menu.c b/menu.c
index 5fdc749..d24a92a 100644
--- a/menu.c
+++ b/menu.c
@@ -19,12 +19,15 @@ cMenuDb::cMenuDb()
 {
    vdrList = 0;
    vdrCount = 0;
+   vdrUuidList = 0;
 
+   timersCacheMaxUpdsp = 0;
    dbInitialized = no;
    connection = 0;
 
    timerDb = 0;
    vdrDb = 0;
+   mapDb = 0;
    timerDoneDb = 0;
    userDb = 0;
    searchtimerDb = 0;
@@ -32,6 +35,7 @@ cMenuDb::cMenuDb()
    useeventsDb = 0;
 
    selectTimers = 0;
+   selectEventById = 0;
    selectMaxUpdSp = 0;
    selectTimerById = 0;
    selectActiveVdrs = 0;
@@ -44,6 +48,7 @@ cMenuDb::cMenuDb()
    selectDoneTimerByStateTimeOrder = 0;
    selectRecordingForEvent = 0;
    selectRecordingForEventByLv = 0;
+   selectChannelFromMap = 0;
 
    webLoginEnabled = no;
    user = "@";
@@ -83,6 +88,9 @@ int cMenuDb::initDb()
    vdrDb = new cDbTable(connection, "vdrs");
    if (vdrDb->open() != success) return fail;
 
+   mapDb = new cDbTable(connection, "channelmap");
+   if (mapDb->open() != success) return fail;
+
    timerDoneDb = new cDbTable(connection, "timersdone");
    if (timerDoneDb->open() != success) return fail;
 
@@ -163,6 +171,24 @@ int cMenuDb::initDb()
 
    status += selectTimers->prepare();
 
+   // select event by useid
+   // select * from eventsview
+   //      where useid = ?
+   //        and updflg in (.....)
+
+   selectEventById = new cDbStatement(useeventsDb);
+
+   selectEventById->build("select ");
+   selectEventById->bindAllOut();
+   selectEventById->build(" from %s where ", useeventsDb->TableName());
+   selectEventById->bind("USEID", cDBS::bndIn | cDBS::bndSet);
+   selectEventById->build(" and %s in (%s)",
+                          useeventsDb->getField("UPDFLG")->getDbName(),
+                          Us::getNeeded());
+
+   status += selectEventById->prepare();
+
+
    // select
    //    t.*,
    //    t.day + t.starttime div 100 * 60 * 60 + t.starttime % 100 * 60,
@@ -341,6 +367,20 @@ int cMenuDb::initDb()
 
    status += selectRecordingForEventByLv->prepare();
 
+   // ----------
+   // select channelname
+   //   from channelmap
+   //   where channelid = ?
+
+   selectChannelFromMap = new cDbStatement(mapDb);
+
+   selectChannelFromMap->build("select ");
+   selectChannelFromMap->bind("CHANNELNAME", cDBS::bndOut);
+   selectChannelFromMap->build(" from %s where ", mapDb->TableName());
+   selectChannelFromMap->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet);
+
+   status += selectChannelFromMap->prepare();
+
    // search timer stuff
 
    if (!status)
@@ -401,7 +441,9 @@ int cMenuDb::exitDb()
       cParameters::exitDb();
 
       delete timerDb;                  timerDb = 0;
+      delete selectEventById;          selectEventById = 0;
       delete vdrDb;                    vdrDb = 0;
+      delete mapDb;                    mapDb = 0;
       delete timerDoneDb;              timerDoneDb = 0;
       delete userDb;                   userDb = 0;
       delete searchtimerDb;            searchtimerDb = 0;
@@ -421,6 +463,7 @@ int cMenuDb::exitDb()
       delete selectDoneTimerByStateTimeOrder;  selectDoneTimerByStateTimeOrder = 0;
       delete selectRecordingForEvent;          selectRecordingForEvent = 0;
       delete selectRecordingForEventByLv;      selectRecordingForEventByLv = 0;
+      delete selectChannelFromMap;             selectChannelFromMap = 0;
 
       delete connection;           connection = 0;
 
@@ -473,7 +516,7 @@ int cMenuDb::initUserList(char**& userList, int& count, long int& loginEnabled)
 int cMenuDb::initUserTimes()
 {
    char* p;
-   char times[500+TB]; *times = 0;
+   char times[500+TB] = "";
 
    userTimes->clear();
    getParameter(user.c_str(), "quickTimes", times);
@@ -489,7 +532,7 @@ int cMenuDb::initUserTimes()
    {
       char* time;
 
-      if (time = strchr(p, '='))
+      if ((time = strchr(p, '=')))
          *time++ = 0;
 
       if (!isEmpty(time))
@@ -509,7 +552,6 @@ int cMenuDb::initUserTimes()
 
 int cMenuDb::lookupTimer(const cEvent* event, int& timerMatch, int& remote, int force)
 {
-   static int timersCacheMaxUpdsp = 0;
    int maxSp = 0;
 
    timerMatch = tmNone;
@@ -614,7 +656,7 @@ int cMenuDb::modifyTimer(cDbRow* timerRow, const char* destUuid)
    {
       // request 'D'elete of 'old' timer
 
-      timerDb->setValue("ACTION", "D");         // = taDelete
+      timerDb->setCharValue("ACTION", taDelete);
       timerDb->setValue("SOURCE", Epg2VdrConfig.uuid);
       timerDb->update();
 
@@ -623,7 +665,7 @@ int cMenuDb::modifyTimer(cDbRow* timerRow, const char* destUuid)
       timerDb->copyValues(timerRow, cDBS::ftData);     // takeover all data (can be modified by user)
       timerDb->setValue("ID", 0);                      // don't care on insert!
       timerDb->setValue("VDRUUID", destUuid);
-      timerDb->setValue("ACTION", "C");                // = taCreate
+      timerDb->setCharValue("ACTION", taCreate);
       timerDb->setValue("SOURCE", Epg2VdrConfig.uuid);
       timerDb->insert();
 
@@ -665,17 +707,89 @@ int cMenuDb::modifyTimer(cDbRow* timerRow, const char* destUuid)
 
 int cMenuDb::createTimer(cDbRow* timerRow, const char* destUuid)
 {
+   long int manualTimer2Done;
+
+   getParameter("epgd", "manualTimer2Done", manualTimer2Done);
+
    // Timer 'C'reate request ...
 
    timerDb->clear();
    timerDb->copyValues(timerRow, cDBS::ftData);
 
    timerDb->setValue("VDRUUID", destUuid);
-   timerDb->setValue("ACTION", "C");                //  taCreate
+   timerDb->setCharValue("ACTION", taCreate);
    timerDb->setValue("SOURCE", Epg2VdrConfig.uuid);
    timerDb->setValue("NAMINGMODE", tnmAuto);
 
+   if (manualTimer2Done)
+   {
+      useeventsDb->clear();
+      useeventsDb->setValue("USEID", timerRow->getIntValue("EVENTID"));
+
+      if (selectEventById->find())
+      {
+         timerDoneDb->clear();
+         timerDoneDb->setCharValue("STATE", tdsTimerRequested);
+         timerDoneDb->setValue("SOURCE", Epg2VdrConfig.uuid);
+
+         timerDoneDb->setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID"));
+         timerDoneDb->setValue("STARTTIME", useeventsDb->getIntValue("STARTTIME"));
+         timerDoneDb->setValue("DURATION", useeventsDb->getIntValue("DURATION"));
+         timerDoneDb->setValue("TITLE", useeventsDb->getStrValue("TITLE"));
+         timerDoneDb->setValue("COMPTITLE", useeventsDb->getStrValue("COMPTITLE"));
+
+         if (!useeventsDb->getValue("SHORTTEXT")->isEmpty())
+            timerDoneDb->setValue("SHORTTEXT", useeventsDb->getStrValue("SHORTTEXT"));
+         if (!useeventsDb->getValue("COMPSHORTTEXT")->isEmpty())
+            timerDoneDb->setValue("COMPSHORTTEXT", useeventsDb->getStrValue("COMPSHORTTEXT"));
+
+         if (!useeventsDb->getValue("LONGDESCRIPTION")->isEmpty())
+            timerDoneDb->setValue("LONGDESCRIPTION", useeventsDb->getStrValue("LONGDESCRIPTION"));
+         if (!useeventsDb->getValue("COMPLONGDESCRIPTION")->isEmpty())
+            timerDoneDb->setValue("COMPLONGDESCRIPTION", useeventsDb->getStrValue("COMPLONGDESCRIPTION"));
+
+         if (!useeventsDb->getValue("EPISODECOMPNAME")->isEmpty())
+            timerDoneDb->setValue("EPISODECOMPNAME", useeventsDb->getStrValue("EPISODECOMPNAME"));
+         if (!useeventsDb->getValue("EPISODECOMPSHORTNAME")->isEmpty())
+            timerDoneDb->setValue("EPISODECOMPSHORTNAME", useeventsDb->getStrValue("EPISODECOMPSHORTNAME"));
+
+         if (!useeventsDb->getValue("EPISODECOMPPARTNAME")->isEmpty())
+            timerDoneDb->setValue("EPISODECOMPPARTNAME", useeventsDb->getStrValue("EPISODECOMPPARTNAME"));
+         if (!useeventsDb->getValue("EPISODELANG")->isEmpty())
+            timerDoneDb->setValue("EPISODELANG", useeventsDb->getStrValue("EPISODELANG"));
+         if (!useeventsDb->getValue("EPISODESEASON")->isEmpty())
+            timerDoneDb->setValue("EPISODESEASON", useeventsDb->getIntValue("EPISODESEASON"));
+         if (!useeventsDb->getValue("EPISODEPART")->isEmpty())
+            timerDoneDb->setValue("EPISODEPART", useeventsDb->getIntValue("EPISODEPART"));
+
+         // lookup channelname
+
+         const char* channelname = "";
+
+         mapDb->clear();
+         mapDb->setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID"));
+
+         if (selectChannelFromMap->find())
+         {
+            channelname = mapDb->getStrValue("CHANNELNAME");
+
+            if (isEmpty(channelname))
+               channelname = useeventsDb->getStrValue("CHANNELID");
+
+            timerDoneDb->setValue("CHANNELNAME", channelname);
+         }
+
+         selectChannelFromMap->freeResult();
+      }
+
+      selectEventById->freeResult();
+
+      timerDoneDb->insert();
+      timerDb->setValue("DONEID", timerDoneDb->getLastInsertId());
+   }
+
    timerDb->insert();
+
    triggerVdrs("TIMERJOB", destUuid);
 
    tell(0, "Created 'create' request for event '%ld' at vdr '%s'",
@@ -704,7 +818,7 @@ int cMenuDb::deleteTimer(long timerid)
 
    selectTimerById->freeResult();
 
-   timerDb->setValue("ACTION", "D");         // = taDelete
+   timerDb->setCharValue("ACTION", taDelete);
    timerDb->setValue("SOURCE", Epg2VdrConfig.uuid);
    timerDb->update();
 
diff --git a/menu.h b/menu.h
index 576cf1f..23442f2 100644
--- a/menu.h
+++ b/menu.h
@@ -24,6 +24,24 @@ class cMenuEpgEditTimer;
 class cTimerData;
 
 //***************************************************************************
+//
+//***************************************************************************
+
+#define NEWTIMERLIMIT    60 // seconds until the start time of a new timer created from the Schedule menu,
+                            // within which it will go directly into the "Edit timer" menu to allow
+                            // further parameter settings
+
+#define MAXCHNAMWIDTH    16 // maximum number of characters of channels' short names shown in schedules menus
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#  define CHNUMWIDTH  (numdigits(cChannels::MaxNumber()) + 1)
+#  define CHNAMWIDTH  (std::min(MAXCHNAMWIDTH, cChannels::MaxShortChannelNameLength() + 1))
+#else
+#  define CHNUMWIDTH  (numdigits(Channels.MaxNumber()) + 1)
+#  define CHNAMWIDTH  (std::min(MAXCHNAMWIDTH, Channels.MaxShortChannelNameLength() + 1))
+#endif
+
+//***************************************************************************
 // Class cMenuDb
 //***************************************************************************
 
@@ -40,6 +58,7 @@ class cMenuDb : public cParameters
    friend class cMenuEpgMatchRecordings;
    friend class cEpgMenuSearchResult;
    friend class cMenuSetupEPG2VDR;
+   friend class cMenuEpgScheduleItem;
 
    public:
 
@@ -91,6 +110,7 @@ class cMenuDb : public cParameters
 
       cDbTable* timerDb;
       cDbTable* vdrDb;
+      cDbTable* mapDb;
       cDbTable* timerDoneDb;
       cDbTable* userDb;
       cDbTable* searchtimerDb;
@@ -98,6 +118,7 @@ class cMenuDb : public cParameters
       cDbTable* useeventsDb;
 
       cDbStatement* selectTimers;
+      cDbStatement* selectEventById;
       cDbStatement* selectMaxUpdSp;
       cDbStatement* selectTimerById;
       cDbStatement* selectActiveVdrs;
@@ -109,7 +130,8 @@ class cMenuDb : public cParameters
       cDbStatement* selectDoneTimerByStateTitleOrder;
       cDbStatement* selectDoneTimerByStateTimeOrder;
       cDbStatement* selectRecordingForEvent;
-      cDbStatement*  selectRecordingForEventByLv;
+      cDbStatement* selectRecordingForEventByLv;
+      cDbStatement* selectChannelFromMap;
 
       cSearchTimer* search;
 
@@ -130,12 +152,9 @@ class cMenuDb : public cParameters
       };
 
       cList<cTimerInfo> timers;
+      int timersCacheMaxUpdsp;
 };
 
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-#  warning "Program menu have to be ported to VDR >= 2.3.1, compiling without it"
-#else
-
 //***************************************************************************
 // Class cMenuEditTimer
 //***************************************************************************
@@ -342,7 +361,7 @@ class cMenuEpgSearchTimers : public cOsdMenu
    private:
 
       cMenuDb* menuDb;
-      int helpKeys;
+      // int helpKeys;
 };
 
 //***************************************************************************
@@ -354,7 +373,7 @@ class cMenuEpgEvent : public cOsdMenu
    public:
 
       cMenuEpgEvent(cMenuDb* db, const cEvent* Event, const cSchedules* schedules,
-                    bool DispSchedule, bool CanSwitch = false);
+                    int timerMatch, bool DispSchedule, bool CanSwitch = false);
       virtual void Display(void);
       virtual eOSState ProcessKey(eKeys Key);
       virtual const char* MenuKind() { return "MenuEvent"; }
@@ -362,7 +381,7 @@ class cMenuEpgEvent : public cOsdMenu
    private:
 
       const cEvent* getNextPrevEvent(const cEvent* event, int step);
-      int setEvent(const cEvent* Event);
+      int setEvent(const cEvent* Event, int timerMatch);
 
       cMenuDb* menuDb;
       const cSchedules* schedules;
@@ -375,6 +394,38 @@ class cMenuEpgEvent : public cOsdMenu
 };
 
 //***************************************************************************
+// Class cMenuEpgScheduleItem
+//***************************************************************************
+
+class cMenuEpgScheduleItem : public cOsdItem
+{
+   public:
+
+      enum eScheduleSortMode { ssmAllThis, ssmThisThis, ssmThisAll, ssmAllAll }; // "which event(s) on which channel(s)"
+
+      cMenuEpgScheduleItem(cMenuDb* db, const cEvent* Event, const cChannel* Channel = 0, bool WithDate = no);
+      ~cMenuEpgScheduleItem();
+
+      static void SetSortMode(eScheduleSortMode SortMode)  { sortMode = SortMode; }
+      static void IncSortMode()                            { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); }
+      static eScheduleSortMode SortMode()                  { return sortMode; }
+
+      virtual int Compare(const cListObject &ListObject) const;
+      virtual bool Update(bool Force = false);
+      virtual void SetMenuItem(cSkinDisplayMenu* DisplayMenu, int Index, bool Current, bool Selectable);
+
+      const cEvent* event;
+      const cChannel* channel;
+      bool withDate;
+      int timerMatch;
+
+   private:
+
+      cMenuDb* menuDb;
+      static eScheduleSortMode sortMode;
+};
+
+//***************************************************************************
 // Class cMenuEpgWhatsOn
 //***************************************************************************
 
@@ -401,7 +452,7 @@ class cMenuEpgWhatsOn : public cOsdMenu
       int helpKeys;
       time_t helpKeyTime;
       int helpKeyTimeMode;
-      int timerState;
+      // int timerState;
       eOSState Record();
       eOSState Switch();
       bool Update();
@@ -409,7 +460,11 @@ class cMenuEpgWhatsOn : public cOsdMenu
 
       cMenuDb* menuDb;
       const cSchedules* schedules;
-      cSchedulesLock schedulesLock;
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+      cStateKey schedulesKey;
+#else
+      cSchedulesLock* schedulesLock;
+#endif
       const cEvent* searchEvent;
       int dispSchedule;
 
@@ -417,8 +472,6 @@ class cMenuEpgWhatsOn : public cOsdMenu
       static const cEvent* scheduleEvent;
 };
 
-#endif // defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-
 //***************************************************************************
 // class cEpgMenuDones
 //***************************************************************************
diff --git a/menudone.c b/menudone.c
index 397046a..be04ca8 100644
--- a/menudone.c
+++ b/menudone.c
@@ -84,7 +84,7 @@ int cEpgMenuDones::refresh()
    for (int f = select->find(); f && menuDb->dbConnected(); f = select->fetch())
    {
       char* buf;
-      asprintf(&buf, "%s\t%s\t%s\t%s", 
+      asprintf(&buf, "%s\t%s\t%s\t%s",
                menuDb->timerDoneDb->getStrValue("STATE"),
                l2pTime(menuDb->timerDoneDb->getIntValue("STARTTIME"), "%d.%m.%y %H:%M").c_str(),
                menuDb->timerDoneDb->getStrValue("TITLE"),
@@ -111,14 +111,14 @@ int cEpgMenuDones::refresh()
 eOSState cEpgMenuDones::ProcessKey(eKeys Key)
 {
    eOSState state = cOsdMenu::ProcessKey(Key);
-   
-   if (state == osUnknown) 
+
+   if (state == osUnknown)
    {
       switch (Key)
-      {         
-         case k0: 
+      {
+         case k0:
          {
-            order = !order; 
+            order = !order;
             refresh();
             break;
          }
@@ -137,7 +137,7 @@ eOSState cEpgMenuDones::ProcessKey(eKeys Key)
             refresh();
             return osContinue;
          }
-         case kYellow: 
+         case kYellow:
          {
             cEpgMenuTextItem* item = (cEpgMenuTextItem*)Get(Current());
 
diff --git a/menusched.c b/menusched.c
index 077bca8..bcbb3fe 100644
--- a/menusched.c
+++ b/menusched.c
@@ -5,8 +5,6 @@
  *
  */
 
-#define __STL_CONFIG_H
-
 #include <string>
 
 #include <vdr/menuitems.h>
@@ -15,21 +13,77 @@
 
 #include "plgconfig.h"
 #include "menu.h"
-
 #include "ttools.h"
 
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-#  warning "Program menu have to be ported to VDR >= 2.3.1, compiling without it"
-#else
+class cMenuEpgScheduleItem;
+
+//***************************************************************************
+// Class cEpgCommandMenu
+//***************************************************************************
+
+class cEpgCommandMenu : public cOsdMenu
+{
+   public:
+
+      cEpgCommandMenu(const char* title, cMenuDb* db, const cMenuEpgScheduleItem* aItem);
+      virtual ~cEpgCommandMenu() { };
+
+      virtual eOSState ProcessKey(eKeys key);
+
+   protected:
+
+      const cMenuEpgScheduleItem* item;
+      cMenuDb* menuDb;
+};
 
-#define NEWTIMERLIMIT    60 // seconds until the start time of a new timer created from the Schedule menu,
-                            // within which it will go directly into the "Edit timer" menu to allow
-                            // further parameter settings
+cEpgCommandMenu::cEpgCommandMenu(const char* title, cMenuDb* db, const cMenuEpgScheduleItem* aItem)
+   : cOsdMenu(title)
+{
+   item = aItem;
+   menuDb = db;
+
+   SetMenuCategory(mcMain);
+   SetHasHotkeys(yes);
+   Clear();
+
+   cOsdMenu::Add(new cOsdItem(hk(tr("Search matching Events")),     osUser1));
+   cOsdMenu::Add(new cOsdItem(hk(tr("Search matching Recordings")), osUser2));
+   cOsdMenu::Add(new cOsdItem(hk(tr("Searchtimers")),               osUser3));
+
+   SetHelp(0, 0, 0, 0);
+
+   Display();
+}
+
+eOSState cEpgCommandMenu::ProcessKey(eKeys key)
+{
+   eOSState state = cOsdMenu::ProcessKey(key);
+
+   switch (state)
+   {
+      case osUser1:
+      {
+         if (item)
+            return AddSubMenu(new cMenuEpgWhatsOn(item->event));
 
-#define MAXCHNAMWIDTH    16 // maximum number of characters of channels' short names shown in schedules menus
+         break;
+      }
 
-#define CHNUMWIDTH       (numdigits(Channels.MaxNumber()) + 1)
-#define CHNAMWIDTH       (std::min(MAXCHNAMWIDTH, Channels.MaxShortChannelNameLength() + 1))
+      case osUser2:
+      {
+         if (item)
+            return AddSubMenu(new cMenuEpgMatchRecordings(menuDb, item->event));
+
+         break;
+      }
+
+      case osUser3: return AddSubMenu(new cMenuEpgSearchTimers());
+
+      default: ;
+   }
+
+   return state;
+}
 
 //***************************************************************************
 // Class cMenuEpgMatchRecordings
@@ -131,61 +185,35 @@ eOSState cMenuEpgMatchRecordings::ProcessKey(eKeys Key)
 // Class cMenuEpgScheduleItem
 //***************************************************************************
 
-class cMenuEpgScheduleItem : public cOsdItem
-{
-   public:
-
-      enum eScheduleSortMode { ssmAllThis, ssmThisThis, ssmThisAll, ssmAllAll }; // "which event(s) on which channel(s)"
-
-      cMenuEpgScheduleItem(cMenuDb* db, const cEvent* Event, const cChannel* Channel = 0, bool WithDate = no);
-
-      static void SetSortMode(eScheduleSortMode SortMode)  { sortMode = SortMode; }
-      static void IncSortMode()                            { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); }
-      static eScheduleSortMode SortMode()                  { return sortMode; }
-
-      virtual int Compare(const cListObject &ListObject) const;
-      virtual bool Update(bool Force = false);
-      virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable);
-
-      const cEvent* event;
-      const cChannel* channel;
-      bool withDate;
-      int timerMatch;
-
-   private:
-
-      cMenuDb* menuDb;
-      static eScheduleSortMode sortMode;
-};
-
-//***************************************************************************
-//
-//***************************************************************************
-
 cMenuEpgScheduleItem::eScheduleSortMode cMenuEpgScheduleItem::sortMode = ssmAllThis;
 
 //***************************************************************************
 // Object
 //***************************************************************************
 
-cMenuEpgScheduleItem::cMenuEpgScheduleItem(cMenuDb* db, const cEvent* Event, const cChannel* Channel, bool WithDate)
+cMenuEpgScheduleItem::cMenuEpgScheduleItem(cMenuDb* db, const cEvent* Event,
+                                           const cChannel* Channel, bool WithDate)
 {
    menuDb = db;
-
    event = Event;
+
    channel = Channel;
    withDate = WithDate;
    timerMatch = tmNone;
    Update(yes);
 }
 
+cMenuEpgScheduleItem::~cMenuEpgScheduleItem()
+{
+}
+
 //***************************************************************************
 // Compare
 //***************************************************************************
 
 int cMenuEpgScheduleItem::Compare(const cListObject &ListObject) const
 {
-   cMenuEpgScheduleItem *p = (cMenuEpgScheduleItem *)&ListObject;
+   cMenuEpgScheduleItem* p = (cMenuEpgScheduleItem*)&ListObject;
    int r = -1;
 
    if (sortMode != ssmAllThis)
@@ -211,7 +239,7 @@ bool cMenuEpgScheduleItem::Update(bool Force)
    int remote = no;
 
    if (event)
-      menuDb->lookupTimer(event, timerMatch, remote); // -> loads timerDb and vdrDb
+      menuDb->lookupTimer(event, timerMatch, remote); // -> loads timerDb, vdrDb and set ther timerMatch
 
    if (Force || timerMatch != oldTimerMatch)
    {
@@ -252,7 +280,7 @@ bool cMenuEpgScheduleItem::Update(bool Force)
 }
 
 //***************************************************************************
-// SetMenuItem
+// Set Menu Item
 //***************************************************************************
 
 void cMenuEpgScheduleItem::SetMenuItem(cSkinDisplayMenu* DisplayMenu,
@@ -274,7 +302,7 @@ class cMenuEpgScheduleSepItem : public cOsdItem
 {
    public:
 
-      cMenuEpgScheduleSepItem(cChannel* Channel, const cEvent* Event);
+      cMenuEpgScheduleSepItem(const cChannel* Channel, const cEvent* Event);
       ~cMenuEpgScheduleSepItem();
 
       virtual bool Update(bool Force = false);
@@ -287,7 +315,7 @@ class cMenuEpgScheduleSepItem : public cOsdItem
       cEvent* tmpEvent;
 };
 
-cMenuEpgScheduleSepItem::cMenuEpgScheduleSepItem(cChannel* Channel, const cEvent* Event)
+cMenuEpgScheduleSepItem::cMenuEpgScheduleSepItem(const cChannel* Channel, const cEvent* Event)
 {
    channel = Channel;
    event = Event;
@@ -329,7 +357,7 @@ void cMenuEpgScheduleSepItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Ind
 //***************************************************************************
 
 cMenuEpgEvent::cMenuEpgEvent(cMenuDb* db, const cEvent* Event, const cSchedules* Schedules,
-                             bool DispSchedule, bool CanSwitch)
+                             int timerMatch, bool DispSchedule, bool CanSwitch)
    : cOsdMenu(tr("Event"))
 {
    menuDb = db;
@@ -337,54 +365,60 @@ cMenuEpgEvent::cMenuEpgEvent(cMenuDb* db, const cEvent* Event, const cSchedules*
    schedules = Schedules;
    canSwitch = CanSwitch;
    SetMenuCategory(mcEvent);
-   setEvent(Event);
+   setEvent(Event, timerMatch);
 }
 
 //***************************************************************************
 // Set Event
 //***************************************************************************
 
-int cMenuEpgEvent::setEvent(const cEvent* Event)
+int cMenuEpgEvent::setEvent(const cEvent* Event, int timerMatch)
 {
    if (Event)
    {
       event = Event;
-      cChannel* channel = Channels.GetByChannelID(event->ChannelID(), true);
+
+#if APIVERSNUM >= 20301
+      LOCK_CHANNELS_READ;
+      const cChannels* channels = Channels;
+      const cChannel* channel = channels->GetByChannelID(event->ChannelID(), true);
+#else
+      cChannels* channels = &Channels;
+      cChannel* channel = channels->GetByChannelID(event->ChannelID(), true);
+#endif
 
       if (channel)
       {
-         cChannel* prevChannel = 0;
-         cChannel* nextChannel = 0;
-         eTimerMatch TimerMatch = tmNone;
+         const cChannel* prevChannel = 0;
+         const cChannel* nextChannel = 0;
 
          prevTime = "";
          nextTime = "";
 
          SetTitle(channel->Name());
-         Timers.GetMatch(event, &TimerMatch);
 
          if (menuDb->userTimes->current()->getMode() != cUserTimes::mSearch)
          {
             const cEvent* e;
 
-            if (e = getNextPrevEvent(event, -1))
+            if ((e = getNextPrevEvent(event, -1)))
             {
                if (dispSchedule)
                   prevTime = l2pTime(e->StartTime(), "%H:%M");
                else
-                  prevChannel = Channels.GetByChannelID(e->ChannelID(), true);
+                  prevChannel = channels->GetByChannelID(e->ChannelID(), true);
             }
 
-            if (e = getNextPrevEvent(event, +1))
+            if ((e = getNextPrevEvent(event, +1)))
             {
                if (dispSchedule)
                   nextTime = l2pTime(e->StartTime(), "%H:%M");
                else
-                  nextChannel = Channels.GetByChannelID(e->ChannelID(), true);
+                  nextChannel = channels->GetByChannelID(e->ChannelID(), true);
             }
          }
 
-         SetHelp(TimerMatch == tmFull ? tr("Button$Timer") : tr("Button$Record"),
+         SetHelp(timerMatch == tmFull ? tr("Button$Timer") : tr("Button$Record"),
                  dispSchedule ? prevTime.c_str() : (prevChannel ? prevChannel->Name() : 0),
                  dispSchedule ? nextTime.c_str() : (nextChannel ? nextChannel->Name() : 0),
                  canSwitch ? tr("Button$Switch") : 0);
@@ -415,7 +449,15 @@ void cMenuEpgEvent::Display()
 
 const cEvent* cMenuEpgEvent::getNextPrevEvent(const cEvent* event, int step)
 {
-   cChannel* channel = Channels.GetByChannelID(event->ChannelID(), true);
+#if APIVERSNUM >= 20301
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
+   const cChannel* channel = channels->GetByChannelID(event->ChannelID(), true);
+#else
+   cChannels* channels = &Channels;
+   cChannel* channel = channels->GetByChannelID(event->ChannelID(), true);
+#endif
+
    const cEvent* e = 0;
 
    if (dispSchedule)
@@ -434,11 +476,11 @@ const cEvent* cMenuEpgEvent::getNextPrevEvent(const cEvent* event, int step)
          int cn = channel->Number() + step;
 
          if (cn < 1)
-            cn = Channels.MaxNumber();
-         else if (cn > Channels.MaxNumber())
+            cn = channels->MaxNumber();
+         else if (cn > channels->MaxNumber())
             cn = 1;
 
-         if (!(channel = Channels.GetByNumber(cn)))
+         if (!(channel = channels->GetByNumber(cn)))
             return 0;
 
          const cSchedule* schedule = schedules->GetSchedule(channel);
@@ -492,7 +534,13 @@ eOSState cMenuEpgEvent::ProcessKey(eKeys Key)
          return osContinue;
 
       const cEvent* e = getNextPrevEvent(event, Key == kGreen ? -1 : +1);
-      setEvent(e);
+      int remote = no;
+      int timerMatch = tmNone;
+
+      // get remote and timerMatch info
+
+      menuDb->lookupTimer(e, timerMatch, remote);
+      setEvent(e, timerMatch);
 
       return osContinue;
    }
@@ -526,39 +574,66 @@ const cEvent* cMenuEpgWhatsOn::scheduleEvent = 0;
 cMenuEpgWhatsOn::cMenuEpgWhatsOn(const cEvent* aSearchEvent)
    : cOsdMenu("", CHNUMWIDTH, CHNAMWIDTH, 6, 4)
 {
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#else
+   schedulesLock = 0;
+#endif
+   schedules = 0;
    dispSchedule = no;
    canSwitch = no;
    helpKeys = 0;
    helpKeyTime = 0;
    helpKeyTimeMode = na;
-   timerState = 0;
+//   timerState = 0;
    searchEvent = aSearchEvent;
-
    menuDb = new cMenuDb;
 
-   cChannel* channel = Channels.GetByNumber(cDevice::CurrentChannel());
+#if APIVERSNUM >= 20301
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
+   const cChannel* channel = channels->GetByNumber(cDevice::CurrentChannel());
+#else
+   cChannels* channels = &Channels;
+   const cChannel* channel = channels->GetByNumber(cDevice::CurrentChannel());
+#endif
 
    if (channel)
    {
       currentChannel = channel->Number();
-      schedules = cSchedules::Schedules(schedulesLock);
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+      schedules = cSchedules::GetSchedulesRead(schedulesKey);
+#else
+      schedulesLock = new cSchedulesLock(false);
+      schedules = (cSchedules*)cSchedules::Schedules(*schedulesLock);
+#endif
    }
 
-   Timers.Modified(timerState);
+//   Timers.Modified(timerState);
 
-   menuDb->userTimes->first();     // 'whats on now' should be the first
+   if (menuDb->dbConnected())
+   {
+      menuDb->userTimes->first();     // 'whats on now' should be the first
 
-   if (searchEvent)
-      LoadQuery(searchEvent);
-   else if (menuDb->startWithSched)
-      LoadSchedule();
-   else
-      LoadAt();
+      if (searchEvent)
+         LoadQuery(searchEvent);
+      else if (menuDb->startWithSched)
+         LoadSchedule();
+      else
+         LoadAt();
+   }
 }
 
 cMenuEpgWhatsOn::~cMenuEpgWhatsOn()
 {
    delete menuDb;
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+   if (schedules)
+      schedulesKey.Remove();
+#else
+   tell(3, "LOCK free (cMenuEpgWhatsOn)");
+   delete schedulesLock;
+#endif
 }
 
 //***************************************************************************
@@ -583,7 +658,8 @@ int cMenuEpgWhatsOn::LoadAt()
       return LoadSearch(userTime);
 
    Clear();
-   Timers.Modified(timerState);
+//   Timers.Modified(timerState);
+
    SetMenuCategory(userTime->getMode() == cUserTimes::mNow ? mcScheduleNow : mcScheduleNext);
 
    if (isEmpty(userTime->getHHMMStr()))
@@ -593,9 +669,16 @@ int cMenuEpgWhatsOn::LoadAt()
 
    tell(2, "DEBUG: Loading events for '%s' %s", userTime->getTitle(), !isEmpty(userTime->getHHMMStr()) ? userTime->getHHMMStr() : "");
 
+#if APIVERSNUM >= 20301
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
+#else
+   const cChannels* channels = &Channels;
+#endif
+
    // events
 
-   for (cChannel* Channel = Channels.First(); Channel; Channel = Channels.Next(Channel))
+   for (const cChannel* Channel = channels->First(); Channel; Channel = channels->Next(Channel))
    {
       if (!Channel->GroupSep())
       {
@@ -656,8 +739,10 @@ int cMenuEpgWhatsOn::LoadSearch(const cUserTimes::UserTime* userTime)
       return fail;
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-   cChannelsLock channelsLock(false);
-   const cChannels* channels = channelsLock.Channels();
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
+   // cChannelsLock channelsLock(false);
+   // const cChannels* channels = channelsLock.Channels();
 #else
    cChannels* channels = &Channels;
 #endif
@@ -699,10 +784,18 @@ int cMenuEpgWhatsOn::LoadSchedule()
 {
    Clear();
    SetCols(7, 6, 4);
-
    dispSchedule = yes;
+
+#if APIVERSNUM >= 20301
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
+   const cChannel* channel = channels->GetByNumber(currentChannel);
+#else
+   cChannels* channels = &Channels;
+   const cChannel* channel = channels->GetByNumber(currentChannel);
+#endif
+
    cMenuEpgScheduleItem::SetSortMode(cMenuEpgScheduleItem::ssmAllThis);
-   cChannel* channel = Channels.GetByNumber(currentChannel);
    SetMenuCategory(mcSchedule);
    SetTitle(cString::sprintf(tr("Schedule - %s"), channel->Name()));
 
@@ -749,7 +842,16 @@ int cMenuEpgWhatsOn::LoadQuery(const cEvent* searchEvent)
    SetMenuCategory(mcSchedule);
    cMenuEpgScheduleItem::SetSortMode(cMenuEpgScheduleItem::ssmAllThis);
 
-   for (cChannel* channel = Channels.First(); channel; channel = Channels.Next(channel))
+#if APIVERSNUM >= 20301
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
+#else
+   const cChannels* channels = &Channels;
+#endif
+
+   tell(1, "Lookup events with title '%s'", searchEvent->Title());
+
+   for (const cChannel* channel = channels->First(); channel; channel = channels->Next(channel))
    {
       const cSchedule* schedule = schedules->GetSchedule(channel);
 
@@ -757,7 +859,7 @@ int cMenuEpgWhatsOn::LoadQuery(const cEvent* searchEvent)
       {
          for (const cEvent* event = schedule->Events()->First(); event; event = schedule->Events()->Next(event))
          {
-            if (rep(event->Title(), searchEvent->Title()) == success &&
+            if (strcasecmp(event->Title(), searchEvent->Title()) == 0 &&
                 event->StartTime() > time(0))
                Add(new cMenuEpgScheduleItem(menuDb, event, channel, yes));
          }
@@ -786,7 +888,7 @@ void cMenuEpgWhatsOn::Display()
    {
       int ni = 0;
 
-      for (cOsdItem *item = First(); item; item = Next(item))
+      for (cOsdItem* item = First(); item; item = Next(item))
          cStatus::MsgOsdEventItem(((cMenuEpgScheduleItem*)item)->event, item->Text(), ni++, Count());
    }
 #endif
@@ -902,7 +1004,7 @@ eOSState cMenuEpgWhatsOn::Record()
    if (!item)
       return osContinue;
 
-   if (timerid = menuDb->lookupTimer(item->event, timerMatch, remote)) // -> loads timerDb and vdrDb
+   if ((timerid = menuDb->lookupTimer(item->event, timerMatch, remote))) // -> loads timerDb and vdrDb
    {
       menuDb->timerDb->clear();
       menuDb->timerDb->setValue("ID", timerid);
@@ -922,7 +1024,7 @@ eOSState cMenuEpgWhatsOn::Record()
    // neuen Timer anlegen
 
    cDbRow* timerRow = newTimerRowFromEvent(item->event);
-   char timerDefaultVDRuuid[150+TB]; *timerDefaultVDRuuid = 0;
+   char timerDefaultVDRuuid[150+TB] = "";
 
    menuDb->getParameter(menuDb->user.c_str(), "timerDefaultVDRuuid", timerDefaultVDRuuid);
 
@@ -961,6 +1063,9 @@ eOSState cMenuEpgWhatsOn::ProcessKey(eKeys Key)
    bool HadSubMenu = HasSubMenu();
    eOSState state = cOsdMenu::ProcessKey(Key);
 
+   if (!menuDb->dbConnected())
+      return state;
+
    if (state == osUnknown)
    {
       if (searchEvent && Key != kOk && Key != kRed && Key != kRecord)
@@ -974,22 +1079,35 @@ eOSState cMenuEpgWhatsOn::ProcessKey(eKeys Key)
 
       switch (Key)
       {
-         case k1:       // search repeat of event
+         case k0:       // command menu
          {
             cMenuEpgScheduleItem* item = (cMenuEpgScheduleItem*)Get(Current());
 
-            if (item)
-               return AddSubMenu(new cMenuEpgWhatsOn(item->event));
+            return AddSubMenu(new cEpgCommandMenu("Commands", menuDb, item));
+         }
+
+         case k1:       // search repeat of event
+         {
+            if (Get(Current()))
+            {
+               cMenuEpgScheduleItem* item = (cMenuEpgScheduleItem*)Get(Current());
+
+               if (item)
+                  return AddSubMenu(new cMenuEpgWhatsOn(item->event));
+            }
 
             break;
          }
 
          case k2:      // search matching recordings
          {
-            cMenuEpgScheduleItem* item = (cMenuEpgScheduleItem*)Get(Current());
+            if (Get(Current()))
+            {
+               cMenuEpgScheduleItem* item = (cMenuEpgScheduleItem*)Get(Current());
 
-            if (item)
-               return AddSubMenu(new cMenuEpgMatchRecordings(menuDb, item->event));
+               if (item)
+                  return AddSubMenu(new cMenuEpgMatchRecordings(menuDb, item->event));
+            }
 
             break;
          }
@@ -1010,7 +1128,7 @@ eOSState cMenuEpgWhatsOn::ProcessKey(eKeys Key)
 
          case kYellow:
          {
-            if (!dispSchedule)
+            if (!dispSchedule && Get(Current()))
             {
                cMenuEpgScheduleItem* mi = (cMenuEpgScheduleItem*)Get(Current());
 
@@ -1054,10 +1172,19 @@ eOSState cMenuEpgWhatsOn::ProcessKey(eKeys Key)
          case kInfo:
          case kOk:
          {
-            const cEvent* event = ((cMenuEpgScheduleItem *)Get(Current()))->event;
+            if (Get(Current()))
+            {
+               cMenuEpgScheduleItem* item = (cMenuEpgScheduleItem*)Get(Current());
 
-            if (Count() && event)
-               return AddSubMenu(new cMenuEpgEvent(menuDb, event, schedules, dispSchedule, canSwitch));
+               if (item)
+               {
+                  const cEvent* event = item->event;
+
+                  if (Count() && event)
+                     return AddSubMenu(new cMenuEpgEvent(menuDb, event, schedules,
+                                                         item->timerMatch, dispSchedule, canSwitch));
+               }
+            }
 
             break;
          }
@@ -1080,5 +1207,3 @@ eOSState cMenuEpgWhatsOn::ProcessKey(eKeys Key)
 }
 
 //***************************************************************************
-
-#endif // defined (APIVERSNUM) && (APIVERSNUM >= 20301)
diff --git a/menusearchtimer.c b/menusearchtimer.c
index a492c97..cae3e42 100644
--- a/menusearchtimer.c
+++ b/menusearchtimer.c
@@ -8,7 +8,6 @@
 #include <vdr/interface.h>
 
 #include "lib/searchtimer.h"
-
 #include "menu.h"
 
 //***************************************************************************
@@ -40,7 +39,7 @@ cEpgMenuSearchTimerEdit::cEpgMenuSearchTimerEdit(cMenuDb* db, long id, bool New)
       return;
 
    cDbTableDef* def = menuDb->searchtimerDb->getTableDef();
-   
+
    for (int i = 0; i < def->fieldCount(); i++)
    {
       cDbValue* value = menuDb->searchtimerDb->getValue(def->getField(i)->getName());
@@ -77,7 +76,7 @@ eOSState cEpgMenuSearchTimerEdit::ProcessKey(eKeys Key)
 // Class cEpgMenuSearchTimerItem
 //***************************************************************************
 
-class cEpgMenuSearchTimerItem : public cOsdItem 
+class cEpgMenuSearchTimerItem : public cOsdItem
 {
    public:
 
@@ -110,19 +109,15 @@ class cEpgMenuSearchResult : public cOsdMenu
 
       cEpgMenuSearchResult(cMenuDb* db, long id);
       virtual ~cEpgMenuSearchResult();
-
       virtual eOSState ProcessKey(eKeys Key);
 
    protected:
 
       int refresh(long id);
-      
+
       cMenuDb* menuDb;
 };
 
-#define MAXCHNAMWIDTH  16 // maximum number of characters of channels' short names shown in schedules menus
-#define CHNAMWIDTH     (std::min(MAXCHNAMWIDTH, Channels.MaxShortChannelNameLength() + 1))
-
 cEpgMenuSearchResult::cEpgMenuSearchResult(cMenuDb* db, long id)
    : cOsdMenu(tr("Edit Search Timer"), 17, CHNAMWIDTH, 3, 30)
 {
@@ -141,51 +136,53 @@ cEpgMenuSearchResult::~cEpgMenuSearchResult()
 int cEpgMenuSearchResult::refresh(long id)
 {
    cDbStatement* select = 0;
-   
+
    menuDb->searchtimerDb->setValue("ID", id);
-   
+
    if (!menuDb->searchtimerDb->find())
       return done;
-   
+
    if (!(select = menuDb->search->prepareSearchStatement(menuDb->searchtimerDb->getRow(), menuDb->useeventsDb)))
       return fail;
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-   cChannelsLock channelsLock(false);
-   const cChannels* channels = channelsLock.Channels();
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
+   // cChannelsLock channelsLock(false);
+   // const cChannels* channels = channelsLock.Channels();
 #else
    cChannels* channels = &Channels;
 #endif
 
    menuDb->useeventsDb->clear();
-   
-   for (int res = select->find(); res; res = select->fetch()) 
+
+   for (int res = select->find(); res; res = select->fetch())
    {
       int cnt = 0;
       cDbStatement* selectDones = 0;
-      
+
       menuDb->useeventsDb->find();  // get all fields ..
-      
+
       if (!menuDb->search->matchCriterias(menuDb->searchtimerDb->getRow(), menuDb->useeventsDb->getRow()))
          continue;
 
       // dones ...
-   
+
       if (menuDb->search->prepareDoneSelect(menuDb->useeventsDb->getRow(),
                                             menuDb->searchtimerDb->getIntValue("REPEATFIELDS"),
                                             selectDones) == success
           && selectDones)
       {
          // first only count - #TODO show them in a sub-menu oder let delete by kYellow?
-         
+
          for (int f = selectDones->find(); f; f = selectDones->fetch())
             cnt++;
-         
+
          selectDones->freeResult();
       }
 
       //
-      
+
       const char* strChannelId = menuDb->useeventsDb->getStrValue("CHANNELID");
       const cChannel* channel = channels->GetByChannelID(tChannelID::FromString(strChannelId));
 
@@ -201,9 +198,9 @@ int cEpgMenuSearchResult::refresh(long id)
    select->freeResult();
    menuDb->searchtimerDb->reset();
 
-   SetHelp(0,0,"Delete dones",0);
+   SetHelp(0, 0, "Delete dones", 0);
    Display();
-   
+
    return success;
 }
 
@@ -215,15 +212,15 @@ eOSState cEpgMenuSearchResult::ProcessKey(eKeys Key)
 {
    eOSState state = cOsdMenu::ProcessKey(Key);
 
-   if (state == osUnknown) 
+   if (state == osUnknown)
    {
       switch (Key)
-      {         
+      {
          case kYellow:
          {
             cDbStatement* selectDones = 0;
             cEpgMenuTextItem* item = (cEpgMenuTextItem*)Get(Current());
-           
+
             if (item && Interface->Confirm(tr("Remove all done entries of this event?")))
             {
                menuDb->useeventsDb->clear();
@@ -238,17 +235,17 @@ eOSState cEpgMenuSearchResult::ProcessKey(eKeys Key)
                   {
                      for (int f = selectDones->find(); f; f = selectDones->fetch())
                         selectDones->getTable()->deleteWhere("id = %ld", selectDones->getTable()->getIntValue("ID"));
-                     
+
                      selectDones->freeResult();
                   }
                }
             }
          }
 
-         default: break; 
+         default: break;
       }
    }
-   
+
    return state;
 }
 
@@ -279,14 +276,14 @@ int cMenuEpgSearchTimers::refresh()
 
    Clear();
 
-   for (int f = menuDb->selectSearchTimers->find(); f && menuDb->dbConnected(); f = menuDb->selectSearchTimers->fetch())   
+   for (int f = menuDb->selectSearchTimers->find(); f && menuDb->dbConnected(); f = menuDb->selectSearchTimers->fetch())
    {
       char* buf;
-      asprintf(&buf, "%c\t%ld\t%s", 
+      asprintf(&buf, "%c\t%ld\t%s",
                menuDb->searchtimerDb->getIntValue("ACTIVE") ? '>' : ' ',
                menuDb->searchtimerDb->getIntValue("HITS"),
                menuDb->searchtimerDb->getStrValue("EXPRESSION"));
-      Add(new cEpgMenuSearchTimerItem(menuDb->searchtimerDb->getIntValue("ID"), 
+      Add(new cEpgMenuSearchTimerItem(menuDb->searchtimerDb->getIntValue("ID"),
                                       menuDb->searchtimerDb->getIntValue("ACTIVE"), buf));
       free(buf);
    }
@@ -326,10 +323,10 @@ eOSState cMenuEpgSearchTimers::ProcessKey(eKeys Key)
    eOSState state = cOsdMenu::ProcessKey(Key);
    cEpgMenuSearchTimerItem* item = (cEpgMenuSearchTimerItem*)Get(Current());
 
-   if (state == osUnknown) 
+   if (state == osUnknown)
    {
       switch (Key)
-      {         
+      {
          case kOk:
          {
             if (HasSubMenu() || Count() == 0)
@@ -342,7 +339,7 @@ eOSState cMenuEpgSearchTimers::ProcessKey(eKeys Key)
          case kRed:
          {
             menuDb->searchtimerDb->setValue("ID", item->getId());
-            
+
             if (menuDb->searchtimerDb->find())
             {
                menuDb->searchtimerDb->setValue("ACTIVE", !menuDb->searchtimerDb->getIntValue("ACTIVE"));
@@ -358,7 +355,7 @@ eOSState cMenuEpgSearchTimers::ProcessKey(eKeys Key)
             return AddSubMenu(new cEpgMenuSearchResult(menuDb, item->getId()));
          }
 
-         case kYellow: 
+         case kYellow:
          {
             if (item && Interface->Confirm(tr("Delete search timer?")))
             {
diff --git a/menutimers.c b/menutimers.c
index 89a537f..58bdc0c 100644
--- a/menutimers.c
+++ b/menutimers.c
@@ -8,6 +8,7 @@
 #include <vdr/menu.h>
 #include <vdr/interface.h>
 
+#include "lib/vdrlocks.h"
 #include "plgconfig.h"
 #include "menu.h"
 
@@ -63,12 +64,18 @@ cMenuEpgEditTimer::cMenuEpgEditTimer(cMenuDb* db, cEpgTimer* Timer, bool New)
    }
 
    SetHelpKeys();
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#else
    Timers.IncBeingEdited();
+#endif
 }
 
 cMenuEpgEditTimer::~cMenuEpgEditTimer()
 {
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#else
    Timers.DecBeingEdited();
+#endif
 }
 
 void cMenuEpgEditTimer::SetHelpKeys(void)
@@ -126,7 +133,14 @@ eOSState cMenuEpgEditTimer::ProcessKey(eKeys Key)
          case kOk:
          {
             int recording = no;
-            cChannel* ch = Channels.GetByNumber(channelNr);
+
+            GET_CHANNELS_READ(channels);
+
+#if APIVERSNUM >= 20301
+            const cChannel* ch = channels->GetByNumber(channelNr);
+#else
+            cChannel* ch = channels->GetByNumber(channelNr);
+#endif
 
             if (!ch)
             {
@@ -278,7 +292,8 @@ void cMenuEpgTimerItem::Set()
                             File));
 }
 
-void cMenuEpgTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
+void cMenuEpgTimerItem::SetMenuItem(cSkinDisplayMenu* DisplayMenu, int Index,
+                                    bool Current, bool Selectable)
 {
    if (!DisplayMenu->SetItemTimer(timer, Index, Current, Selectable))
       DisplayMenu->SetItem(Text(), Index, Current, Selectable);
@@ -291,8 +306,6 @@ void cMenuEpgTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bo
 // Object
 //***************************************************************************
 
-#define CHNUMWIDTH (numdigits(Channels.MaxNumber()) + 1)
-
 cMenuEpgTimers::cMenuEpgTimers()
    : cOsdMenu(tr("Timers"), 2, CHNUMWIDTH, 10, 6, 6)
 {
@@ -302,7 +315,11 @@ cMenuEpgTimers::cMenuEpgTimers()
    menuDb = new cMenuDb;
 
    SetMenuCategory(mcTimer);
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#else
    Timers.IncBeingEdited();
+#endif
 
    refresh();
 }
@@ -310,7 +327,11 @@ cMenuEpgTimers::cMenuEpgTimers()
 cMenuEpgTimers::~cMenuEpgTimers()
 {
    delete menuDb;
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+#else
    Timers.DecBeingEdited();
+#endif
 }
 
 //***************************************************************************
@@ -472,7 +493,15 @@ eOSState cMenuEpgTimers::info()
    cEpgTimer* ti = currentTimer();
 
    if (ti && ti->Event())
+   {
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+      LOCK_TIMERS_READ;
+      LOCK_CHANNELS_READ;
+      return AddSubMenu(new cMenuEvent(Timers, Channels, ti->Event()));
+#else
       return AddSubMenu(new cMenuEvent(ti->Event()));
+#endif
+   }
 
    return osContinue;
 }
diff --git a/parameters.c b/parameters.c
index 05b0eba..fdd3e4b 100644
--- a/parameters.c
+++ b/parameters.c
@@ -87,7 +87,7 @@ cParameters::Parameter* cParameters::getDefinition(const char* owner, const char
 
    if (owner[0] == '@')
       owner = "@";
-   
+
    if (strcmp(owner, "epgd") == 0 && strstr(name, ".md5"))
       name = "md5";
 
@@ -161,12 +161,12 @@ int cParameters::getParameter(const char* owner, const char* name, char* value)
 
    if (strcasecmp(owner, "uuid") == 0)
       owner = Epg2VdrConfig.uuid;
-       
+
    parametersDb->clear();
    parametersDb->setValue("OWNER", owner);
    parametersDb->setValue("NAME", name);
 
-   if (found = parametersDb->find())
+   if ((found = parametersDb->find()))
    {
       if (value)
          sprintf(value, "%s", parametersDb->getStrValue("Value"));
@@ -193,7 +193,7 @@ int cParameters::getParameter(const char* owner, const char* name, long int& val
    int found;
 
    found = getParameter(owner, name, txt);
-   
+
    if (!isEmpty(txt))
       value = atol(txt);
    else
@@ -203,7 +203,7 @@ int cParameters::getParameter(const char* owner, const char* name, long int& val
 }
 
 //***************************************************************************
-// Set String Parameter 
+// Set String Parameter
 //***************************************************************************
 
 int cParameters::setParameter(const char* owner, const char* name, const char* value)
@@ -227,7 +227,7 @@ int cParameters::setParameter(const char* owner, const char* name, const char* v
    {
       if (rep(value, definition->regexp) != success)
       {
-         tell(0, "Ignoring '%s' for parameter '%s/%s' don't match expression '%s'", 
+         tell(0, "Ignoring '%s' for parameter '%s/%s' don't match expression '%s'",
               value, owner, name, definition->regexp);
 
          return fail;
@@ -250,4 +250,3 @@ int cParameters::setParameter(const char* owner, const char* name, long int valu
 
    return setParameter(owner, name, txt);
 }
-
diff --git a/patches/pre-vdr-2.1.x--epghandler-segment-transfer.patch b/patches/pre-vdr-2.1.x--epghandler-segment-transfer.patch
deleted file mode 100644
index 8374a66..0000000
--- a/patches/pre-vdr-2.1.x--epghandler-segment-transfer.patch
+++ /dev/null
@@ -1,65 +0,0 @@
---- ../vdr-2.0.2.plain//eit.c	2012-12-04 12:10:10.000000000 +0100
-+++ eit.c	2013-05-22 16:49:37.635027462 +0200
-@@ -46,6 +46,8 @@
-      return;
-      }
- 
-+  EpgHandlers.BeginSegmentTransfer(channel, OnlyRunningStatus);
-+
-   bool handledExternally = EpgHandlers.HandledExternally(channel);
-   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
- 
-@@ -310,6 +312,7 @@
-      Schedules->SetModified(pSchedule);
-      }
-   Channels.Unlock();
-+  EpgHandlers.EndSegmentTransfer(Modified, OnlyRunningStatus);
- }
- 
- // --- cTDT ------------------------------------------------------------------
---- ../vdr-2.0.2.plain//epg.c	2013-02-17 15:12:07.000000000 +0100
-+++ epg.c	2013-05-22 16:50:29.043029281 +0200
-@@ -1537,3 +1537,19 @@
-       }
-   Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
- }
-+
-+void cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus)
-+{
-+  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
-+      if (eh->BeginSegmentTransfer(Channel, OnlyRunningStatus))
-+         return;
-+      }
-+}
-+
-+void cEpgHandlers::EndSegmentTransfer(bool Modified, bool OnlyRunningStatus)
-+{
-+  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
-+      if (eh->EndSegmentTransfer(Modified, OnlyRunningStatus))
-+         return;
-+      }
-+}
---- ../vdr-2.0.2.plain//epg.h	2012-09-24 14:53:53.000000000 +0200
-+++ epg.h	2013-05-22 16:50:16.867028850 +0200
-@@ -273,6 +273,12 @@
-   virtual bool DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version) { return false; }
-           ///< Takes a look at all EPG events between SegmentStart and SegmentEnd and
-           ///< drops outdated events.
-+  virtual bool BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus) { return false; }
-+          ///< called directly after IgnoreChannel before any other handler method called
-+          ///< designed to give handlers the ossibility to prepare a transaction 
-+  virtual bool EndSegmentTransfer(bool Modified, bool OnlyRunningStatus) { return false; }
-+          ///< called at last after the segment data is processed
-+          ///< at this oint handlers should close/commt/rollback their transactions
-   };
- 
- class cEpgHandlers : public cList<cEpgHandler> {
-@@ -295,6 +301,8 @@
-   void HandleEvent(cEvent *Event);
-   void SortSchedule(cSchedule *Schedule);
-   void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
-+  void BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus);
-+  void EndSegmentTransfer(bool Modified, bool OnlyRunningStatus);
-   };
- 
- extern cEpgHandlers EpgHandlers;
diff --git a/patches/vdr-1.7.27-to-epghandler-of-1.7.31.patch b/patches/vdr-1.7.27-to-epghandler-of-1.7.31.patch
deleted file mode 100644
index 7be1802..0000000
--- a/patches/vdr-1.7.27-to-epghandler-of-1.7.31.patch
+++ /dev/null
@@ -1,219 +0,0 @@
---- /home/wendel/vdr-1.7.27.plain//eit.c	2012-03-14 11:11:15.000000000 +0100
-+++ eit.c	2012-10-01 09:38:51.526839349 +0200
-@@ -45,6 +45,7 @@
-      return;
-      }
- 
-+  bool handledExternally = EpgHandlers.HandledExternally(channel);
-   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
- 
-   bool Empty = true;
-@@ -70,14 +71,18 @@
-       cEvent *newEvent = NULL;
-       cEvent *rEvent = NULL;
-       cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), StartTime);
--      if (!pEvent) {
-+      if (!pEvent || handledExternally) {
-          if (OnlyRunningStatus)
-             continue;
-+         if (handledExternally)
-+            if (!EpgHandlers.IsUpdate(SiEitEvent.getEventId(), StartTime, Tid, getVersionNumber()))
-+               continue;
-          // If we don't have that event yet, we create a new one.
-          // Otherwise we copy the information into the existing event anyway, because the data might have changed.
-          pEvent = newEvent = new cEvent(SiEitEvent.getEventId());
-          newEvent->SetStartTime(StartTime);
-          newEvent->SetDuration(Duration);
-+         if (!handledExternally)
-          pSchedule->AddEvent(newEvent);
-          }
-       else {
-@@ -290,6 +295,9 @@
-          channel->SetLinkChannels(LinkChannels);
-       Modified = true;
-       EpgHandlers.HandleEvent(pEvent);
-+
-+        if (handledExternally)
-+         delete pEvent;
-       }
-   if (Tid == 0x4E) {
-      if (Empty && getSectionNumber() == 0)
---- /home/wendel/vdr-1.7.27.plain//epg.c	2012-03-10 14:14:27.000000000 +0100
-+++ epg.c	2012-10-01 09:41:35.010845128 +0200
-@@ -18,6 +18,7 @@
- #include "timers.h"
- 
- #define RUNNINGSTATUSTIMEOUT 30 // seconds before the running status is considered unknown
-+#define EPGDATAWRITEDELTA   600 // seconds between writing the epg.data file
- 
- // --- tComponent ------------------------------------------------------------
- 
-@@ -1109,6 +1110,47 @@
-   return false;
- }
- 
-+// --- cEpgDataWriter ---------------------------------------------------------
-+
-+class cEpgDataWriter : public cThread {
-+private:
-+  cMutex mutex;
-+protected:
-+  virtual void Action(void);
-+public:
-+  cEpgDataWriter(void);
-+  void Perform(void);
-+  };
-+
-+cEpgDataWriter::cEpgDataWriter(void)
-+:cThread("epg data writer")
-+{
-+}
-+
-+void cEpgDataWriter::Action(void)
-+{
-+  SetPriority(19);
-+  SetIOPriority(7);
-+  Perform();
-+}
-+
-+void cEpgDataWriter::Perform(void)
-+{
-+  cMutexLock MutexLock(&mutex); // to make sure fore- and background calls don't cause parellel dumps!
-+  {
-+    cSchedulesLock SchedulesLock(true, 1000);
-+    cSchedules *s = (cSchedules *)cSchedules::Schedules(SchedulesLock);
-+    if (s) {
-+       time_t now = time(NULL);
-+       for (cSchedule *p = s->First(); p; p = s->Next(p))
-+           p->Cleanup(now);
-+       }
-+  }
-+  cSchedules::Dump();
-+}
-+
-+static cEpgDataWriter EpgDataWriter;
-+
- // --- cSchedulesLock --------------------------------------------------------
- 
- cSchedulesLock::cSchedulesLock(bool WriteLock, int TimeoutMs)
-@@ -1152,28 +1194,13 @@
-   if (Force)
-      lastDump = 0;
-   time_t now = time(NULL);
--  struct tm tm_r;
--  struct tm *ptm = localtime_r(&now, &tm_r);
--  if (now - lastCleanup > 3600) {
--     isyslog("cleaning up schedules data");
--     cSchedulesLock SchedulesLock(true, 1000);
--     cSchedules *s = (cSchedules *)Schedules(SchedulesLock);
--     if (s) {
--        for (cSchedule *p = s->First(); p; p = s->Next(p))
--            p->Cleanup(now);
--        }
--     lastCleanup = now;
--     if (ptm->tm_hour == 5)
--        ReportEpgBugFixStats(true);
--     }
--  if (epgDataFileName && now - lastDump > 600) {
--     cSafeFile f(epgDataFileName);
--     if (f.Open()) {
--        Dump(f);
--        f.Close();
-+  if (now - lastDump > EPGDATAWRITEDELTA) {
-+     if (epgDataFileName) {
-+        if (Force)
-+           EpgDataWriter.Perform();
-+        else if (!EpgDataWriter.Active())
-+           EpgDataWriter.Start();
-         }
--     else
--        LOG_ERROR;
-      lastDump = now;
-      }
- }
-@@ -1207,8 +1234,23 @@
-   cSchedulesLock SchedulesLock;
-   cSchedules *s = (cSchedules *)Schedules(SchedulesLock);
-   if (s) {
-+     cSafeFile *sf = NULL;
-+     if (!f) {
-+        sf = new cSafeFile(epgDataFileName);
-+        if (sf->Open())
-+           f = *sf;
-+        else {
-+           LOG_ERROR;
-+           delete sf;
-+           return false;
-+           }
-+        }
-      for (cSchedule *p = s->First(); p; p = s->Next(p))
-          p->Dump(f, Prefix, DumpMode, AtTime);
-+     if (sf) {
-+        sf->Close();
-+        delete sf;
-+        }
-      return true;
-      }
-   return false;
-@@ -1329,6 +1371,24 @@
-          return true;
-       }
-   return false;
-+}
-+
-+bool cEpgHandlers::HandledExternally(const cChannel *Channel)
-+{
-+  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
-+      if (eh->HandledExternally(Channel))
-+         return true;
-+      }
-+  return false;
-+}
-+
-+bool cEpgHandlers::IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
-+{
-+  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
-+      if (eh->IsUpdate(EventID, StartTime, TableID, Version))
-+         return true;
-+      }
-+  return false;
- }
- 
- void cEpgHandlers::SetEventID(cEvent *Event, tEventID EventID)
---- /home/wendel/vdr-1.7.27.plain//epg.h	2012-03-10 14:50:10.000000000 +0100
-+++ epg.h	2012-10-01 09:43:28.162849134 +0200
-@@ -207,7 +207,7 @@
-   static void Cleanup(bool Force = false);
-   static void ResetVersions(void);
-   static bool ClearAll(void);
--  static bool Dump(FILE *f, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0);
-+  static bool Dump(FILE *f = NULL, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0);
-   static bool Read(FILE *f = NULL);
-   cSchedule *AddSchedule(tChannelID ChannelID);
-   const cSchedule *GetSchedule(tChannelID ChannelID) const;
-@@ -244,6 +244,16 @@
-           ///< EPG handlers are queried to see if any of them would like to do the
-           ///< complete processing by itself. TableID and Version are from the
-           ///< incoming section data.
-+  virtual bool HandledExternally(const cChannel *Channel) { return false; }
-+          ///< If any EPG handler returns true in this function, it is assumed that
-+          ///< the EPG for the given Channel is handled completely from some external
-+          ///< source. Incoming EIT data is processed as usual, but any new EPG event
-+          ///< will not be added to the respective schedule. It's up to the EPG
-+          ///< handler to take care of this.
-+  virtual bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version) { return false; }
-+          ///<  VDR can't perform the update check (version, tid) for external handled events 
-+          ///<  therefore the handle have to take care. Otherwise the parsing of 'non' updates will 
-+          ///<  take a lot of resources
-   virtual bool SetEventID(cEvent *Event, tEventID EventID) { return false; }
-   virtual bool SetTitle(cEvent *Event, const char *Title) { return false; }
-   virtual bool SetShortText(cEvent *Event, const char *ShortText) { return false; }
-@@ -269,6 +279,8 @@
- public:
-   bool IgnoreChannel(const cChannel *Channel);
-   bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version);
-+  bool HandledExternally(const cChannel *Channel);
-+  bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version);
-   void SetEventID(cEvent *Event, tEventID EventID);
-   void SetTitle(cEvent *Event, const char *Title);
-   void SetShortText(cEvent *Event, const char *ShortText);
diff --git a/patches/vdr-1.7.28-epghandledexternally.diff b/patches/vdr-1.7.28-epghandledexternally.diff
deleted file mode 100644
index 52dfab6..0000000
--- a/patches/vdr-1.7.28-epghandledexternally.diff
+++ /dev/null
@@ -1,118 +0,0 @@
---- ./eit.c	2012/06/02 14:05:22	2.17
-+++ ./eit.c	2012/06/04 10:10:11
-@@ -45,6 +45,7 @@
-      return;
-      }
- 
-+  bool handledExternally = EpgHandlers.HandledExternally(channel);
-   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
- 
-   bool Empty = true;
-@@ -70,7 +71,7 @@
-       cEvent *newEvent = NULL;
-       cEvent *rEvent = NULL;
-       cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), StartTime);
--      if (!pEvent) {
-+      if (!pEvent || handledExternally) {
-          if (OnlyRunningStatus)
-             continue;
-          // If we don't have that event yet, we create a new one.
-@@ -78,7 +79,8 @@
-          pEvent = newEvent = new cEvent(SiEitEvent.getEventId());
-          newEvent->SetStartTime(StartTime);
-          newEvent->SetDuration(Duration);
--         pSchedule->AddEvent(newEvent);
-+         if (!handledExternally)
-+            pSchedule->AddEvent(newEvent);
-          }
-       else {
-          // We have found an existing event, either through its event ID or its start time.
-@@ -290,11 +292,8 @@
-          channel->SetLinkChannels(LinkChannels);
-       Modified = true;
-       EpgHandlers.HandleEvent(pEvent);
--
--      if (EpgHandlers.DeleteEvent(pEvent)) {
--         pSchedule->DelEvent(pEvent);
--         pEvent = NULL;
--         }
-+      if (handledExternally)
-+         delete pEvent;
-       }
-   if (Tid == 0x4E) {
-      if (Empty && getSectionNumber() == 0)
---- ./epg.c	2012/06/02 14:08:12	2.14
-+++ ./epg.c	2012/06/04 10:06:22
-@@ -1331,6 +1331,15 @@
-   return false;
- }
- 
-+bool cEpgHandlers::HandledExternally(const cChannel *Channel)
-+{
-+  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
-+      if (eh->HandledExternally(Channel))
-+         return true;
-+      }
-+  return false;
-+}
-+
- void cEpgHandlers::SetEventID(cEvent *Event, tEventID EventID)
- {
-   for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
-@@ -1429,15 +1438,6 @@
-       }
- }
- 
--bool cEpgHandlers::DeleteEvent(const cEvent *Event)
--{
--  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
--      if (eh->DeleteEvent(Event))
--         return true;
--      }
--  return false;
--}
--
- void cEpgHandlers::SortSchedule(cSchedule *Schedule)
- {
-   for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
---- ./epg.h	2012/06/02 14:07:51	2.10
-+++ ./epg.h	2012/06/04 10:05:21
-@@ -244,6 +244,12 @@
-           ///< EPG handlers are queried to see if any of them would like to do the
-           ///< complete processing by itself. TableID and Version are from the
-           ///< incoming section data.
-+  virtual bool HandledExternally(const cChannel *Channel) { return false; }
-+          ///< If any EPG handler returns true in this function, it is assumed that
-+          ///< the EPG for the given Channel is handled completely from some external
-+          ///< source. Incoming EIT data is processed as usual, but any new EPG event
-+          ///< will not be added to the respective schedule. It's up to the EPG
-+          ///< handler to take care of this.
-   virtual bool SetEventID(cEvent *Event, tEventID EventID) { return false; }
-   virtual bool SetTitle(cEvent *Event, const char *Title) { return false; }
-   virtual bool SetShortText(cEvent *Event, const char *ShortText) { return false; }
-@@ -258,9 +264,6 @@
-   virtual bool HandleEvent(cEvent *Event) { return false; }
-           ///< After all modifications of the Event have been done, the EPG handler
-           ///< can take a final look at it.
--  virtual bool DeleteEvent(const cEvent *Event) { return false; }
--          ///< After the complete processing of the Event is finished, an EPG handler
--          ///< can decide that this Event shall be deleted from its schedule.
-   virtual bool SortSchedule(cSchedule *Schedule) { return false; }
-           ///< Sorts the Schedule after the complete table has been processed.
-   virtual bool DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version) { return false; }
-@@ -272,6 +275,7 @@
- public:
-   bool IgnoreChannel(const cChannel *Channel);
-   bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version);
-+  bool HandledExternally(const cChannel *Channel);
-   void SetEventID(cEvent *Event, tEventID EventID);
-   void SetTitle(cEvent *Event, const char *Title);
-   void SetShortText(cEvent *Event, const char *ShortText);
-@@ -283,7 +287,6 @@
-   void SetVps(cEvent *Event, time_t Vps);
-   void FixEpgBugs(cEvent *Event);
-   void HandleEvent(cEvent *Event);
--  bool DeleteEvent(const cEvent *Event);
-   void SortSchedule(cSchedule *Schedule);
-   void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
-   };
diff --git a/patches/vdr-1.7.29-epgIsUpdate.diff b/patches/vdr-1.7.29-epgIsUpdate.diff
deleted file mode 100644
index 61549ca..0000000
--- a/patches/vdr-1.7.29-epgIsUpdate.diff
+++ /dev/null
@@ -1,52 +0,0 @@
---- ../vdr-1.7.29.plain//eit.c  2012-06-04 12:26:10.000000000 +0200
-+++ eit.c       2012-07-30 10:19:34.841894485 +0200
-@@ -74,6 +74,9 @@
-       if (!pEvent || handledExternally) {
-          if (OnlyRunningStatus)
-             continue;
-+         if (handledExternally)
-+            if (!EpgHandlers.IsUpdate(SiEitEvent.getEventId(), StartTime, Tid, getVersionNumber()))
-+               continue;
-          // If we don't have that event yet, we create a new one.
-          // Otherwise we copy the information into the existing event anyway, because the data might have changed.
-          pEvent = newEvent = new cEvent(SiEitEvent.getEventId());
---- ../vdr-1.7.29.plain//epg.c  2012-06-04 12:26:10.000000000 +0200
-+++ epg.c       2012-07-30 10:21:51.153899306 +0200
-@@ -1340,6 +1340,15 @@
-   return false;
- }
- 
-+bool cEpgHandlers::IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
-+{
-+  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
-+      if (eh->IsUpdate(EventID, StartTime, TableID, Version))
-+         return true;
-+      }
-+  return false;
-+}
-+
- void cEpgHandlers::SetEventID(cEvent *Event, tEventID EventID)
- {
-   for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
---- ../vdr-1.7.29.plain//epg.h  2012-06-04 12:26:10.000000000 +0200
-+++ epg.h       2012-07-30 10:20:15.705895929 +0200
-@@ -250,6 +250,10 @@
-           ///< source. Incoming EIT data is processed as usual, but any new EPG event
-           ///< will not be added to the respective schedule. It's up to the EPG
-           ///< handler to take care of this.
-+  virtual bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version) { return false; }
-+          ///<  VDR can't perform the update check (version, tid) for external handled events 
-+          ///<  therefore the handle have to take care. Otherwise the parsing of 'non' updates will 
-+          ///<  take a lot of resources
-   virtual bool SetEventID(cEvent *Event, tEventID EventID) { return false; }
-   virtual bool SetTitle(cEvent *Event, const char *Title) { return false; }
-   virtual bool SetShortText(cEvent *Event, const char *ShortText) { return false; }
-@@ -277,6 +281,7 @@
-   bool IgnoreChannel(const cChannel *Channel);
-   bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version);
-   bool HandledExternally(const cChannel *Channel);
-+  bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version);
-   void SetEventID(cEvent *Event, tEventID EventID);
-   void SetTitle(cEvent *Event, const char *Title);
-   void SetShortText(cEvent *Event, const char *ShortText);
-
diff --git a/patches/vdr-2.2.0-aux.patch b/patches/vdr-2.2.0-aux.patch
new file mode 100644
index 0000000..749fb49
--- /dev/null
+++ b/patches/vdr-2.2.0-aux.patch
@@ -0,0 +1,79 @@
+--- ../vdr-2.2.0.plain/epg.c	2013-12-28 12:33:08.000000000 +0100
++++ epg.c	2017-03-22 16:21:34.266665706 +0100
+@@ -122,6 +122,7 @@
+   shortText = NULL;
+   description = NULL;
+   components = NULL;
++  aux = NULL;
+   memset(contents, 0, sizeof(contents));
+   parentalRating = 0;
+   startTime = 0;
+@@ -135,6 +136,7 @@
+   free(title);
+   free(shortText);
+   free(description);
++  free(aux);
+   delete components;
+ }
+ 
+@@ -235,6 +237,12 @@
+   seen = time(NULL);
+ }
+ 
++void cEvent::SetAux(const char *Aux)
++{
++  free(aux);
++  aux = Aux ? strdup(Aux) : NULL;
++}
++
+ cString cEvent::ToDescr(void) const
+ {
+   char vpsbuf[64] = "";
+@@ -458,6 +466,11 @@
+         }
+      if (vps)
+         fprintf(f, "%sV %ld\n", Prefix, vps);
++     if (!isempty(aux)) {
++        strreplace(aux, '\n', '|');
++        fprintf(f, "%s@ %s\n", Prefix, aux);
++        strreplace(aux, '|', '\n');
++        }
+      if (!InfoOnly)
+         fprintf(f, "%se\n", Prefix);
+      }
+@@ -496,6 +509,9 @@
+               break;
+     case 'V': SetVps(atoi(t));
+               break;
++    case '@': strreplace(t, '|', '\n');
++              SetAux(t);
++              break;
+     default:  esyslog("ERROR: unexpected tag while reading EPG data: %s", s);
+               return false;
+     }
+--- ../vdr-2.2.0.plain/epg.h	2013-08-23 12:50:05.000000000 +0200
++++ epg.h	2017-03-22 16:23:28.096287585 +0100
+@@ -87,6 +87,7 @@
+   int duration;            // Duration of this event in seconds
+   time_t vps;              // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+   time_t seen;             // When this event was last seen in the data stream
++  char *aux;
+ public:
+   cEvent(tEventID EventID);
+   ~cEvent();
+@@ -109,6 +110,7 @@
+   time_t Vps(void) const { return vps; }
+   time_t Seen(void) const { return seen; }
+   bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; }
++  const char *Aux(void) const { return aux; }
+   bool HasTimer(void) const;
+   bool IsRunning(bool OrAboutToStart = false) const;
+   static const char *ContentToString(uchar Content);
+@@ -131,6 +133,7 @@
+   void SetDuration(int Duration);
+   void SetVps(time_t Vps);
+   void SetSeen(void);
++  void SetAux(const char *Aux);
+   cString ToDescr(void) const;
+   void Dump(FILE *f, const char *Prefix = "", bool InfoOnly = false) const;
+   bool Parse(char *s);
diff --git a/patches/vdr-2.3.2-aux.patch b/patches/vdr-2.3.2-aux.patch
new file mode 100644
index 0000000..fd3fe26
--- /dev/null
+++ b/patches/vdr-2.3.2-aux.patch
@@ -0,0 +1,79 @@
+--- ../vdr-2.3.2.plain//epg.c	2015-09-10 12:58:19.000000000 +0200
++++ epg.c	2017-03-22 11:44:51.917258845 +0100
+@@ -124,6 +124,7 @@
+   shortText = NULL;
+   description = NULL;
+   components = NULL;
++  aux = NULL;
+   memset(contents, 0, sizeof(contents));
+   parentalRating = 0;
+   startTime = 0;
+@@ -137,6 +138,7 @@
+   free(title);
+   free(shortText);
+   free(description);
++  free(aux);
+   delete components;
+ }
+
+@@ -237,6 +239,12 @@
+   seen = time(NULL);
+ }
+
++void cEvent::SetAux(const char *Aux)
++{
++  free(aux);
++  aux = Aux ? strdup(Aux) : NULL;
++}
++
+ cString cEvent::ToDescr(void) const
+ {
+   char vpsbuf[64] = "";
+@@ -469,6 +477,11 @@
+         }
+      if (vps)
+         fprintf(f, "%sV %ld\n", Prefix, vps);
++     if (!isempty(aux)) {
++        strreplace(aux, '\n', '|');
++        fprintf(f, "%s@ %s\n", Prefix, aux);
++        strreplace(aux, '|', '\n');
++        }
+      if (!InfoOnly)
+         fprintf(f, "%se\n", Prefix);
+      }
+@@ -507,6 +520,9 @@
+               break;
+     case 'V': SetVps(atoi(t));
+               break;
++    case '@': strreplace(t, '|', '\n');
++              SetAux(t);
++              break;
+     default:  esyslog("ERROR: unexpected tag while reading EPG data: %s", s);
+               return false;
+     }
+--- ../vdr-2.3.2.plain//epg.h	2015-08-09 13:25:04.000000000 +0200
++++ epg.h	2017-03-22 11:33:19.319728545 +0100
+@@ -89,6 +89,7 @@
+   int duration;            // Duration of this event in seconds
+   time_t vps;              // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+   time_t seen;             // When this event was last seen in the data stream
++  char *aux;
+ public:
+   cEvent(tEventID EventID);
+   ~cEvent();
+@@ -111,6 +112,7 @@
+   time_t Vps(void) const { return vps; }
+   time_t Seen(void) const { return seen; }
+   bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; }
++  const char *Aux(void) const { return aux; }
+   void IncNumTimers(void) const;
+   void DecNumTimers(void) const;
+   bool HasTimer(void) const { return numTimers > 0; }
+@@ -135,6 +137,7 @@
+   void SetDuration(int Duration);
+   void SetVps(time_t Vps);
+   void SetSeen(void);
++  void SetAux(const char *Aux);
+   cString ToDescr(void) const;
+   void Dump(FILE *f, const char *Prefix = "", bool InfoOnly = false) const;
+   bool Parse(char *s);
diff --git a/patches/vdr-2.3.2.patch b/patches/vdr-2.3.2.patch
new file mode 100644
index 0000000..102b362
--- /dev/null
+++ b/patches/vdr-2.3.2.patch
@@ -0,0 +1,56 @@
+--- ../vdr-2.3.2.plain//./epg.c	2015-09-10 12:58:19.000000000 +0200
++++ ./epg.c	2017-02-09 18:40:29.597671711 +0100
+@@ -1527,12 +1527,13 @@
+   Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
+ }
+ 
+-void cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel)
++bool cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel)
+ {
+   for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
+-      if (eh->BeginSegmentTransfer(Channel, false))
+-         return;
++      if (!eh->BeginSegmentTransfer(Channel, false))
++         return false;
+       }
++  return true;
+ }
+ 
+ void cEpgHandlers::EndSegmentTransfer(bool Modified)
+--- ../vdr-2.3.2.plain//./eit.c	2015-08-23 12:43:36.000000000 +0200
++++ ./eit.c	2017-02-09 18:40:29.597671711 +0100
+@@ -67,8 +67,13 @@
+      return;
+      }
+ 
++  if (!EpgHandlers.BeginSegmentTransfer(Channel)) {
++     SchedulesStateKey.Remove(false);
++     ChannelsStateKey.Remove(false);
++     return;
++     }
++
+   bool ChannelsModified = false;
+-  EpgHandlers.BeginSegmentTransfer(Channel);
+   bool handledExternally = EpgHandlers.HandledExternally(Channel);
+   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(Channel, true);
+ 
+--- ../vdr-2.3.2.plain//./epg.h	2015-08-09 13:25:04.000000000 +0200
++++ ./epg.h	2017-02-09 18:40:29.601671655 +0100
+@@ -66,7 +66,7 @@
+ 
+ class cSchedule;
+ 
+-typedef u_int16_t tEventID;
++typedef u_int32_t tEventID;
+ 
+ class cEvent : public cListObject {
+   friend class cSchedule;
+@@ -311,7 +311,7 @@
+   void HandleEvent(cEvent *Event);
+   void SortSchedule(cSchedule *Schedule);
+   void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
+-  void BeginSegmentTransfer(const cChannel *Channel);
++  bool BeginSegmentTransfer(const cChannel *Channel);
+   void EndSegmentTransfer(bool Modified);
+   };
+ 
diff --git a/plgconfig.c b/plgconfig.c
index 4dda3d2..b41983b 100644
--- a/plgconfig.c
+++ b/plgconfig.c
@@ -13,7 +13,7 @@ cEpg2VdrConfig Epg2VdrConfig;
 // cEpg2VdrConfig
 //***************************************************************************
 
-cEpg2VdrConfig::cEpg2VdrConfig() 
+cEpg2VdrConfig::cEpg2VdrConfig()
    : cEpgConfig()
 {
    mainmenuVisible = yes;
@@ -27,10 +27,11 @@ cEpg2VdrConfig::cEpg2VdrConfig()
    createTimerLocal = no;
    useCommonRecFolder = yes;
    xchgOkBlue = no;
-   
+
    replaceScheduleMenu = no;
    replaceTimerMenu = no;
    userIndex = 0;
    *user = 0;
    showEmptyChannels = no;
+   extendedEpgData2Aux = no;
 }
diff --git a/plgconfig.h b/plgconfig.h
index fce7b0d..b810b36 100644
--- a/plgconfig.h
+++ b/plgconfig.h
@@ -18,7 +18,7 @@
 struct cEpg2VdrConfig : public cEpgConfig
 {
    public:
-      
+
       cEpg2VdrConfig();
 
       int mainmenuVisible;
@@ -31,12 +31,13 @@ struct cEpg2VdrConfig : public cEpgConfig
       int createTimerLocal;
       int useCommonRecFolder;   // NAS
       int xchgOkBlue;
-   
+
       int replaceScheduleMenu;
       int replaceTimerMenu;
       int userIndex;
       char user[100+TB];
       int showEmptyChannels;
+      int extendedEpgData2Aux;
 };
 
 extern cEpg2VdrConfig Epg2VdrConfig;
diff --git a/po/de_DE.po b/po/de_DE.po
index 5e08200..fc7f868 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.7\n"
 "Report-Msgid-Bugs-To: <vdr at jwendel.de>\n"
-"POT-Creation-Date: 2016-04-21 11:01+0200\n"
+"POT-Creation-Date: 2017-03-24 07:25+0100\n"
 "PO-Revision-Date: 2009-08-27 21:40+0200\n"
 "Last-Translator: Klaus Schmidinger <kls at cadsoft.de>\n"
 "Language-Team: <vdr at linuxtv.org>\n"
@@ -70,6 +70,9 @@ msgstr "Booten für geplante Updates"
 msgid "Blacklist not configured Channels"
 msgstr "Nicht konfigurierte Kanäle 'blacklisten'"
 
+msgid "Store extended EPD Data to AUX (e.g. for Skins)"
+msgstr ""
+
 msgid "Menu"
 msgstr "Menü"
 
@@ -166,6 +169,15 @@ msgstr "Timerhistorie - Gelöscht"
 msgid "Delete timer from journal?"
 msgstr "Eintrag aus Timerhistorie löschen?"
 
+msgid "Search matching Events"
+msgstr ""
+
+msgid "Search matching Recordings"
+msgstr ""
+
+msgid "Searchtimers"
+msgstr ""
+
 msgid "Matching recordings"
 msgstr "Vergleiche Aufnahmen"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 7a574cb..77ebd5a 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.7\n"
 "Report-Msgid-Bugs-To: <vdr at jwendel.de>\n"
-"POT-Creation-Date: 2016-04-21 11:01+0200\n"
+"POT-Creation-Date: 2017-03-24 07:25+0100\n"
 "PO-Revision-Date: 2009-08-27 21:45+0100\n"
 "Last-Translator: Diego Pierotto <vdr-italian at tiscali.it>\n"
 "Language-Team:  <vdr at linuxtv.org>\n"
@@ -75,6 +75,9 @@ msgstr ""
 msgid "Blacklist not configured Channels"
 msgstr ""
 
+msgid "Store extended EPD Data to AUX (e.g. for Skins)"
+msgstr ""
+
 msgid "Menu"
 msgstr ""
 
@@ -171,6 +174,15 @@ msgstr ""
 msgid "Delete timer from journal?"
 msgstr ""
 
+msgid "Search matching Events"
+msgstr ""
+
+msgid "Search matching Recordings"
+msgstr ""
+
+msgid "Searchtimers"
+msgstr ""
+
 msgid "Matching recordings"
 msgstr ""
 
diff --git a/recinfofile.c b/recinfofile.c
index cc957da..dd9991c 100644
--- a/recinfofile.c
+++ b/recinfofile.c
@@ -33,11 +33,12 @@ const char* cEventDetails::fields[] =
    "OTHER",
    "GUEST",
    "CAMERA",
+   "LONGDESCRIPTION",
 
    "SCRSERIESID",
    "SCRSERIESEPISODE",
    "SCRMOVIEID",
-                    
+
    // just in recordinglist, not in events or useevents row
 
    "CHANNELNAME",
@@ -54,14 +55,14 @@ const char* cEventDetails::fields[] =
 
 void cEventDetails::setValue(const char* name, const char* value)
 {
-   std::map<std::string,std::string>::iterator it;
+   // std::map<std::string,std::string>::iterator it;
 
-   it = values.find(name);
-   
-   if (it == values.end() || it->first != value)
+   auto it = values.find(name);
+
+   if (it == values.end() || it->second != value)
    {
       changes++;
-      values[name] = value;
+      values[name] = value ? value : "";
    }
 }
 
@@ -76,7 +77,7 @@ void cEventDetails::setValue(const char* name, int value)
 
 int cEventDetails::updateByRow(cDbRow* row)
 {
-   std::map<std::string,std::string>::iterator it;
+   // std::map<std::string,std::string>::iterator it;
 
    for (int i = 0; fields[i]; i++)
    {
@@ -105,7 +106,7 @@ int cEventDetails::updateByRow(cDbRow* row)
             continue;
          }
 
-         it = values.find(fields[i]);
+         auto it = values.find(fields[i]);
 
          if (it == values.end() || it->second != v)
          {
@@ -119,6 +120,28 @@ int cEventDetails::updateByRow(cDbRow* row)
 }
 
 //***************************************************************************
+// Row To Xml
+//***************************************************************************
+
+int cEventDetails::row2Xml(cDbRow* row, cXml* xml)
+{
+   for (int i = 0; fields[i]; i++)
+   {
+      cDbValue* value = row->getValue(fields[i]);
+
+      if (!value || value->isEmpty())
+         continue;
+
+      if (value->getField()->hasFormat(cDBS::ffAscii) || value->getField()->hasFormat(cDBS::ffText) || value->getField()->hasFormat(cDBS::ffMText))
+         xml->appendElement(fields[i], value->getStrValue());
+      else
+         xml->appendElement(fields[i], value->getIntValue());
+   }
+
+   return success;
+}
+
+//***************************************************************************
 // Update To Row
 //***************************************************************************
 
@@ -128,6 +151,9 @@ int cEventDetails::updateToRow(cDbRow* row)
 
    for (it = values.begin(); it != values.end(); it++)
    {
+      if (!it->first.length())
+         continue;
+
       cDbValue* value = row->getValue(it->first.c_str());
 
       if (!value)
@@ -136,18 +162,12 @@ int cEventDetails::updateToRow(cDbRow* row)
          continue;
       }
 
-      if (!it->first.length())
-         continue;
-
       if (value->getField()->isString())
          value->setValue(it->second.c_str());
       else if (value->getField()->isInt())
          value->setValue(atoi(it->second.c_str()));
       else
-      {
          tell(0, "Info: Field '%s' unhandled for info.epg2vdr", it->first.c_str());
-         continue;
-      }
    }
 
    return success;
@@ -162,24 +182,29 @@ int cEventDetails::storeToFs(const char* path)
    FILE* f;
    char* fileName = 0;
    std::map<std::string,std::string>::iterator it;
-   
+
    asprintf(&fileName, "%s/info.epg2vdr", path);
-   
+
    if (!(f = fopen(fileName, "w")))
    {
       tell(0, "Error opening file '%s' failed, %s", fileName, strerror(errno));
       free(fileName);
-      
+
       return fail;
    }
-   
+
    tell(0, "Storing event details to '%s'", fileName);
-   
-   // store fields 
-   
+
+   // store fields
+
    for (it = values.begin(); it != values.end(); it++)
-      fprintf(f, "%s: %s\n", it->first.c_str(), it->second.c_str());
-   
+   {
+      char* value = strdup(it->second.c_str());
+      strReplace(value, '\n', '|');
+      fprintf(f, "%s: %s\n", it->first.c_str(), value);
+      free(value);
+   }
+
    free(fileName);
    fclose(f);
 
@@ -190,14 +215,17 @@ int cEventDetails::storeToFs(const char* path)
 // Load From Fs
 //***************************************************************************
 
-int cEventDetails::loadFromFs(const char* path)
+int cEventDetails::loadFromFs(const char* path, cDbRow* row, int doClear)
 {
    FILE* f;
    char* fileName = 0;
    std::map<std::string,std::string>::iterator it;
 
-   values.clear();
-   changes = 0;
+   if (doClear)
+   {
+      values.clear();
+      changes = 0;
+   }
 
    asprintf(&fileName, "%s/info.epg2vdr", path);
 
@@ -211,32 +239,43 @@ int cEventDetails::loadFromFs(const char* path)
    {
       tell(0, "Error opening file '%s' failed, %s", fileName, strerror(errno));
       free(fileName);
-      
+
       return fail;
    }
-   
+
    tell(3, "Loading event details from '%s'", fileName);
-   
-   // load fields 
-   
+
+   // load fields
+
    char* p;
    char* s;
    cReadLine readLine;
 
-   while (s = readLine.Read(f))
+   while ((s = readLine.Read(f)))
    {
       if (!(p = strchr(s, ':')))
       {
          tell(0, " ");
          continue;
       }
-      
+
       *(p++) = 0;
       p = skipspace(rTrim(p));
 
+      if (!row->getTableDef()->getField(s, /*silent*/ yes))
+      {
+         tell(0, "Warning: Ignoring unexpected field '%s' in '%s'", s, fileName);
+         continue;
+      }
+
       if (!isEmpty(p))
-         values[s] = p;
-   }   
+      {
+         char* value = strdup(p);
+         strReplace(value, '|', '\n');
+         values[s] = value;
+         free(value);
+      }
+   }
 
    free(fileName);
    fclose(f);
diff --git a/recording.c b/recording.c
index c3d5ac4..e572f2b 100644
--- a/recording.c
+++ b/recording.c
@@ -169,7 +169,7 @@ int cUpdate::updatePendingRecordingInfoFiles(const cRecordings* recordings)
 
       if (selectEventById->find())
       {
-         evd.loadFromFs(path.c_str());
+         evd.loadFromFs(path.c_str(), useeventsDb->getRow());
          evd.updateByRow(useeventsDb->getRow());
 
          if (evd.getChanges())
@@ -223,7 +223,7 @@ int cUpdate::storeAllRecordingInfoFiles()
 
       asprintf(&path, "%s/%s", videoBasePath, recordingListDb->getStrValue("PATH"));
 
-      evd.loadFromFs(path);
+      evd.loadFromFs(path, recordingListDb->getRow());
       evd.updateByRow(recordingListDb->getRow());
 
       if (evd.getChanges())
@@ -275,7 +275,7 @@ int cUpdate::updateRecordingInfoFiles()
 
       if (folderExists(path))
       {
-         evd.loadFromFs(path);
+         evd.loadFromFs(path, recordingListDb->getRow());
          evd.updateByRow(recordingListDb->getRow());
 
          if (evd.getChanges())
@@ -324,15 +324,15 @@ int cUpdate::updateRecordingTable(int fullReload)
    // get channel and recordings lock
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-   cChannelsLock channelsLock(false);
-   const cChannels* channels = channelsLock.Channels();
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
 #else
    cChannels* channels = &Channels;
 #endif
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-   cRecordingsLock recordingsLock(false);
-   const cRecordings* recordings = recordingsLock.Recordings();
+   LOCK_RECORDINGS_WRITE;
+   cRecordings* recordings = Recordings;
 #else
    const cRecordings* recordings = &Recordings;
 #endif
@@ -348,7 +348,7 @@ int cUpdate::updateRecordingTable(int fullReload)
    // ----------------
    // update ...
 
-   for (const cRecording* rec = recordings->First(); rec; rec = recordings->Next(rec))
+   for (cRecording* rec = recordings->First(); rec; rec = recordings->Next(rec))
    {
       int insert;
       int fsk;
@@ -357,6 +357,7 @@ int cUpdate::updateRecordingTable(int fullReload)
       int eventId = 0;
       std::string channelId = "";
       const char* description = "";
+      std::string longdescription = "";
       const char* title = rec->Name();
       const cRecordingInfo* recInfo = rec->Info();
       int pathOffset = 0;
@@ -387,8 +388,34 @@ int cUpdate::updateRecordingTable(int fullReload)
          description = recInfo->Description() ? recInfo->Description() : "";
          channel = channels->GetByChannelID(recInfo->ChannelID());
 
-         if (recInfo->Title())    title = recInfo->Title();
-         if (recInfo->GetEvent()) eventId = recInfo->GetEvent()->EventID();
+         if (recInfo->Title())
+            title = recInfo->Title();
+
+         if (recInfo->GetEvent())
+         {
+            eventId = recInfo->GetEvent()->EventID();
+
+#if (defined (APIVERSNUM) && (APIVERSNUM >= 20304))
+            if (channel)
+            {
+               cXml xml;
+               cStateKey schedulesKey;
+               const cSchedules* schedules = cSchedules::GetSchedulesRead(schedulesKey, 500/*ms*/);
+               const cSchedule* s = schedules ? (cSchedule*)schedules->GetSchedule(channel) : 0;
+               const cEvent* event = s ? s->GetEvent(eventId) : 0;
+
+               if (event && !isEmpty(event->Aux()) && xml.set(event->Aux()) == success)
+               {
+                  if (XMLElement* element = xml.getElementByName("longdescription"))
+                     if (!isEmpty(element->GetText()))
+                        longdescription = element->GetText();
+               }
+
+               if (schedules)
+                  schedulesKey.Remove();
+            }
+#endif
+         }
       }
 
       fsk = isProtected(rec->FileName());
@@ -409,14 +436,14 @@ int cUpdate::updateRecordingTable(int fullReload)
       recordingListDb->setValue("PATH", rec->FileName()+pathOffset);
       recordingListDb->setValue("NAME", rec->BaseName());
       recordingListDb->setValue("FOLDER", rec->Folder());
-      recordingListDb->setValue("LONGDESCRIPTION", description);
+      recordingListDb->setValue("DESCRIPTION", description);
       recordingListDb->setValue("DURATION", rec->LengthInSeconds() > 0 ? rec->LengthInSeconds() : 0);
       recordingListDb->setValue("EVENTID", eventId);
       recordingListDb->setValue("CHANNELID", channelId.c_str());
       recordingListDb->setValue("FSK", fsk);
 
-      if (channel)
-         recordingListDb->setValue("CHANNELNAME", channel->Name());
+      if (longdescription.length())
+         recordingListDb->setValue("LONGDESCRIPTION", longdescription.c_str());
 
       // scraping relevand data ..
 
@@ -428,39 +455,16 @@ int cUpdate::updateRecordingTable(int fullReload)
       // load event details
 
       cEventDetails evd;
-      evd.loadFromFs(rec->FileName());
+      if (channel) evd.setValue("CHANNELNAME", channel->Name());
+      evd.loadFromFs(rec->FileName(), recordingListDb->getRow(), no);
       evd.updateToRow(recordingListDb->getRow());
 
       // any scrap relevand data changed?
 
-      if (recordingListDb->getChanges() != baseChanges)
+      if (recordingListDb->getChanges() > baseChanges)
       {
-         int isSeries = recordingListDb->hasValue("CATEGORY", "Serie");
-         int changed = no;
-
-         if (isSeries)
-         {
-            if (recordingListDb->getValue("SCRSERIESID")->isEmpty() ||
-                !recordingListDb->hasValue("SCRSERIESEPISODE", recordingListDb->getIntValue("SCRINFOEPISODEID")) ||
-                !recordingListDb->hasValue("SCRSERIESID", recordingListDb->getIntValue("SCRINFOSERIESID")))
-            {
-               changed = yes;
-            }
-         }
-         else
-         {
-            if (recordingListDb->getValue("SCRMOVIEID")->isEmpty() ||
-                !recordingListDb->hasValue("SCRMOVIEID", recordingListDb->getIntValue("SCRINFOMOVIEID")))
-            {
-               changed = yes;
-            }
-         }
-
-         if (changed)
-         {
-            recordingListDb->setValue("SCRNEW", yes);     // force scrap
-            recordingListDb->setValue("SCRSP", time(0));  // force load from vdr
-         }
+         recordingListDb->setValue("SCRNEW", yes);     // force scrap
+         recordingListDb->setValue("SCRSP", time(0));  // force load from vdr
       }
 
       // don't toggle uuid if already set!
@@ -478,7 +482,6 @@ int cUpdate::updateRecordingTable(int fullReload)
       }
 
       count++;
-
       recordingListDb->reset();
    }
 
diff --git a/service.c b/service.c
index 8e54aa9..1c34f98 100644
--- a/service.c
+++ b/service.c
@@ -12,12 +12,12 @@
 // Class cEpgTimer
 //***************************************************************************
 
-cEpgTimer::cEpgTimer(bool Instant, bool Pause, cChannel* Channel)
+cEpgTimer::cEpgTimer(bool Instant, bool Pause, const cChannel* Channel)
    : cEpgTimer_Interface_V1(Instant, Pause, Channel)
 {
-   timerid = na; eventid = na; 
-   vdrName = 0; vdrUuid = 0; 
-   vdrRunning = no; 
+   timerid = na; eventid = na;
+   vdrName = 0; vdrUuid = 0;
+   vdrRunning = no;
    stateInfo = 0;
    local = yes;
 }
@@ -29,12 +29,12 @@ cEpgTimer::~cEpgTimer()
    free(stateInfo);
 }
 
-void cEpgTimer::setState(char s, const char* info)       
+void cEpgTimer::setState(char s, const char* info)
 {
-   state = s; 
-   free(stateInfo); 
+   state = s;
+   free(stateInfo);
    stateInfo = 0;
-   
+
    if (!isEmpty(info))
       stateInfo = strdup(info);
 }
@@ -50,22 +50,12 @@ void cEpgTimer::setVdr(const char* name, const char* uuid, int running)
    free(vdrUuid);
    free(vdrName);
    vdrName = strdup(name);
-   
+
    if (!isEmpty(uuid))
       vdrUuid = strdup(uuid);
-   
+
    vdrRunning = running;
 
    if (!isEmpty(vdrUuid) && strcmp(vdrUuid, Epg2VdrConfig.uuid) != 0)
       local = no;
 }
-
-//***************************************************************************
-// Class cEpgEvent
-//***************************************************************************
-
-cEpgEvent::cEpgEvent(tEventID EventID)
-   : cEpgEvent_Interface_V1(EventID)
-{
-
-}
diff --git a/service.h b/service.h
index efa77b4..729f559 100644
--- a/service.h
+++ b/service.h
@@ -5,31 +5,14 @@
  *
  */
 
-#ifndef _SERVICE_H_ 
-#define _SERVICE_H_ 
+#ifndef _SERVICE_H_
+#define _SERVICE_H_
 
 #include <vdr/timers.h>
 #include <vdr/epg.h>
 
 #include <list>
-
-//***************************************************************************
-// Timer - Skin Interface
-//***************************************************************************
-
-class cEpgEvent_Interface_V1 : public cEvent
-{
-   public:
-
-      cEpgEvent_Interface_V1(tEventID EventID)
-         : cEvent(EventID) {}
-
-      // #TODO ... getter
-      
-   protected:
-      
-      // #TODO ... attributes
-};
+#include <map>
 
 //***************************************************************************
 // Timer - Skin Interface
@@ -38,22 +21,26 @@ class cEpgEvent_Interface_V1 : public cEvent
 class cEpgTimer_Interface_V1 : public cTimer
 {
    public:
-      
-      cEpgTimer_Interface_V1(bool Instant = false, bool Pause = false, cChannel* Channel = 0)
+
+      cEpgTimer_Interface_V1(bool Instant = false, bool Pause = false, const cChannel* Channel = 0)
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
          : cTimer(Instant, Pause, Channel) {}
-      
-      long TimerId()                { return timerid; }
-      long EventId()                { return eventid; }
-      const char* VdrName()         { return vdrName ? vdrName : ""; }
-      const char* VdrUuid()         { return vdrUuid ? vdrUuid : ""; }
-      int isVdrRunning()            { return vdrRunning; }
-      int isLocal()                 { return local; }
-      int isRemote()                { return !isLocal(); }
-
-      char State()                  { return state; }
-      int  hasState(char s)   const { return state == s; }
-      const char* StateInfo()       { return stateInfo ? stateInfo : ""; }
-      char Action()                 { return action; }
+#else
+         : cTimer(Instant, Pause, (cChannel*)Channel) {}
+#endif
+
+      long TimerId()               const { return timerid; }
+      long EventId()               const { return eventid; }
+      const char* VdrName()        const { return vdrName ? vdrName : ""; }
+      const char* VdrUuid()        const { return vdrUuid ? vdrUuid : ""; }
+      int isVdrRunning()           const { return vdrRunning; }
+      int isLocal()                const { return local; }
+      int isRemote()               const { return !isLocal(); }
+
+      char State()                 const { return state; }
+      int  hasState(char s)        const { return state == s; }
+      const char* StateInfo()      const { return stateInfo ? stateInfo : ""; }
+      char Action()                const { return action; }
 
    protected:
 
@@ -64,14 +51,14 @@ class cEpgTimer_Interface_V1 : public cTimer
       char* vdrUuid;
       int local;
       int vdrRunning;
-      
+
       char state;
       char* stateInfo;
       char action;
 };
 
 //***************************************************************************
-// Timer - Service Interface
+// Timer Service Interface
 //***************************************************************************
 
 struct cEpgTimer_Service_V1
@@ -79,25 +66,39 @@ struct cEpgTimer_Service_V1
    std::list<cEpgTimer_Interface_V1*> epgTimers;
 };
 
-#define EPG2VDR_TIMER_UPDATED "Epg2Vdr_Timer_Updated-v1.0"
-#define EPG2VDR_TIMER_SERVICE "Epg2Vdr_Timer_Service-v1.0"
+//***************************************************************************
+// Has Timer
+//***************************************************************************
 
-#ifdef EPG2VDR
+struct cHas_Timer_V1
+{
+   long eventid;
+   int hastimer;
+};
 
 //***************************************************************************
-// Class cEpgEvent
+// Recording Detail Service Interface
 //***************************************************************************
 
-class cEpgEvent : public cEpgEvent_Interface_V1
+struct cEpgRecording_Details_Service_V1
 {
-   public:
-
-      cEpgEvent(tEventID EventID);
-      virtual ~cEpgEvent() {}
-      
-      // #TODO ... setter
+   int id;
+   std::string details;
 };
 
+#define EPG2VDR_TIMER_UPDATED      "Epg2Vdr_Timer_Updated-v1.0"
+#define EPG2VDR_TIMER_SERVICE      "Epg2Vdr_Timer_Service-v1.0"
+#define EPG2VDR_HAS_TIMER          "Epg2Vdr_Has_Timer_Service-v1.0"
+#define EPG2VDR_REC_DETAIL_SERVICE "Epg2Vdr_RecDetail_Service-v1.0"
+
+#ifdef EPG2VDR
+
+//***************************************************************************
+//***************************************************************************
+//***************************************************************************
+// Internal Stuff
+//***************************************************************************
+
 //***************************************************************************
 // Class cEpgTimer
 //***************************************************************************
@@ -106,7 +107,7 @@ class cEpgTimer : public cEpgTimer_Interface_V1
 {
    public:
 
-      cEpgTimer(bool Instant = false, bool Pause = false, cChannel* Channel = 0);
+      cEpgTimer(bool Instant = false, bool Pause = false, const cChannel* Channel = 0);
       virtual ~cEpgTimer();
 
       void setTimerId(long id)    { timerid = id; }
@@ -120,4 +121,4 @@ class cEpgTimer : public cEpgTimer_Interface_V1
 
 //***************************************************************************
 
-#endif // _SERVICE_H_ 
+#endif // _SERVICE_H_
diff --git a/status.c b/status.c
index 8bd7d1b..6acd011 100644
--- a/status.c
+++ b/status.c
@@ -42,16 +42,16 @@ int RecLengthInSecs(const cRecording* pRecording)
          delta = sizeof(tIndex) - delta;
          tell(0, "ERROR: invalid file size (%ld) in '%s'", buf.st_size, *fullname);
       }
-      
+
       return (buf.st_size + delta) / sizeof(tIndex) / SecondsToFrames(1);
    }
-   
+
    return -1;
 }
 
 #else
 
-struct tIndexTs 
+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
@@ -67,11 +67,11 @@ struct tIndexTs
    }
 };
 
-int RecLengthInSecs(const cRecording *pRecording)
+int RecLengthInSecs(const cRecording* pRecording)
 {
   struct stat buf;
   cString fullname = cString::sprintf("%s%s", pRecording->FileName(), IsPesRecording(pRecording) ? LOC_INDEXFILESUFFIX ".vdr" : LOC_INDEXFILESUFFIX);
-  
+
   if (pRecording->FileName() && *fullname && access(fullname, R_OK) == 0 && stat(fullname, &buf) == 0)
   {
      double frames = buf.st_size ? (buf.st_size - 1) / sizeof(tIndexTs) + 1 : 0;
@@ -101,165 +101,165 @@ void cUpdate::TimerChange(const cTimer* Timer, eTimerChange Change)
    if (!Epg2VdrConfig.shareInWeb)
       return;
 
-   tell(1, "Timer changed, trigger update");
-   
+   tell(1, "Timer changed, trigger update. Action was (%d)", Change);
+
    timerTableUpdateTriggered = yes;
    waitCondition.Broadcast();         // wakeup
 }
 
 //***************************************************************************
-// Recording Notification
+// Recording Notification (cStatus::MsgRecording(....))
 //***************************************************************************
 
 void cUpdate::Recording(const cDevice* Device, const char* Name, const char* FileName, bool On)
 {
-   cMutexLock lock(&runningRecMutex);
-   const int allowedBreakDuration = 2;
+   RecordingAction action;
 
    // Recording of 'Peter Hase' has 'started' [/srv/vdr/video.00/Peter_Hase/2014-10-08.11.05.18-0.rec]
    // Recording of '(null)' has 'stopped' [/srv/vdr/video.00/Peter_Hase/2014-10-08.11.05.18-0.rec]
 
    tell(1, "Recording of '%s' has '%s' [%s]", Name, On ? "started" : "stopped", FileName);
-   
-   // at start of recording store event details to recording directory (info.epg2vdr)
+
+   // schedule this notification to perfrom it in oure context not in the cStatus Interface context
+   //  due to the needed list locks!
+
+   action.name = notNull(Name, "");
+   action.fileName = FileName;
+   action.cardIndex = Device->CardIndex();
+   action.on = On;
+   pendingRecordingActions.push(action);
 
    if (On)
       pendingNewRecordings.push(FileName);
 
-   // get timers lock
-   
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-   cTimersLock timersLock(false);
-   const cTimers* timers = timersLock.Timers();
-#else
-   const cTimers* timers = &Timers;
-#endif
-   
-   // recording started ...
+   recordingStateChangedTrigger = yes;
+   waitCondition.Broadcast();            // wakeup
+}
+
+//***************************************************************************
+// Perform Pending Recording Notification
+//  (got by cStatus::MsgRecording(....) above)
+//***************************************************************************
 
-   if (On && Name)
+int cUpdate::performRecordingActions()
+{
+   const int allowedBreakDuration = 2;
+
+   GET_TIMERS_READ(timers);           // get timers lock
+   GET_RECORDINGS_READ(recordings);   // recordings lock
+
+   while (!pendingRecordingActions.empty())
    {
-      for (const cTimer* ti = timers->First(); ti; ti = timers->Next(ti))
+      cMutexLock lock(&runningRecMutex);
+
+      RecordingAction action = pendingRecordingActions.front();
+      pendingRecordingActions.pop();
+
+      if (action.on && action.name.length())    // recording started ...
       {
-         if (ti->Recording())                     // timer nimmt gerade auf
+         for (const cTimer* ti = timers->First(); ti; ti = timers->Next(ti))
          {
-            cRunningRecording* recording = 0;
+            if (ti->Recording())                     // timer nimmt gerade auf
+            {
+               cRunningRecording* recording = 0;
 
-            // check if already known
+               // check if already known
 
-            for (cRunningRecording* rr = runningRecordings.First(); rr;  rr = runningRecordings.Next(rr))
-            {
-               if (rr->timer == ti)
+               for (cRunningRecording* rr = runningRecordings.First(); rr;  rr = runningRecordings.Next(rr))
                {
-                  recording = rr;
-                  break;
+                  if (rr->timer == ti)
+                  {
+                     recording = rr;
+                     break;
+                  }
                }
-            }
 
-            if (recording) // already handled -> a resume?!
-            {
-               tell(1, "Info: Detected resume of '%s' on device %d", Name, Device->CardIndex());
-               continue;
-            }
+               if (recording) // already handled -> a resume?!
+               {
+                  tell(1, "Info: Detected resume of '%s' on device %d", action.name.c_str(), action.cardIndex);
+                  continue;
+               }
 
-            int doneid = na;
-            contentOfTag(ti, "doneid", doneid);
-            
-            recording = new cRunningRecording(ti, doneid);
-            runningRecordings.Add(recording);
-            tell(1, "Info: Recording '%s' with doneid %d added to running list", Name, doneid);
+               int doneid = na;
+               contentOfTag(ti, "doneid", doneid);
+
+               recording = new cRunningRecording(ti, doneid);
+               runningRecordings.Add(recording);
+               tell(1, "Info: Recording '%s' with doneid %d added to running list", action.name.c_str(), doneid);
+            }
          }
       }
-   }
-
-   // recording stopped ...
 
-   if (!On)
-   {
-      // loop over running recordings ..
+      // recording stopped ...
 
-      for (cRunningRecording* rr = runningRecordings.First(); rr;  rr = runningRecordings.Next(rr))
+      if (!action.on)
       {
-         const cTimer* pendingTimer = 0;
-         int complete;
-         int recFraction = 100;
-         long timerLengthSecs = rr->timer->StopTime() - rr->timer->StartTime();
-         bool vpsUsed = rr->timer->HasFlags(tfVps) && rr->timer->Event() && rr->timer->Event()->Vps();
-         
-         // check if timer still exists
-
-         for (pendingTimer = timers->First(); pendingTimer; pendingTimer = timers->Next(pendingTimer))
+         // loop over running recordings ..
+
+         for (cRunningRecording* rr = runningRecordings.First(); rr;  rr = runningRecordings.Next(rr))
          {
-            if (pendingTimer == rr->timer)
-               break;
-         }
+            const cRecording* pRecording = recordings->GetByName(action.fileName.c_str());
+            const cTimer* pendingTimer = 0;
+            int complete;
+            double recFraction = 100.0;
+            long timerLengthSecs = rr->timer->StopTime() - rr->timer->StartTime();
+            bool vpsUsed = rr->timer->HasFlags(tfVps) && rr->timer->Event() && rr->timer->Event()->Vps();
 
-         // still recording :o ?
-         
-         if (pendingTimer && pendingTimer->Recording())
-            continue;
+            // check if timer still exists
 
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-         cStateKey stateKey;
+            for (pendingTimer = timers->First(); pendingTimer; pendingTimer = timers->Next(pendingTimer))
+            {
+               if (pendingTimer == rr->timer)
+                  break;
+            }
 
-         if (const cRecordings* recordings = cRecordings::GetRecordingsRead(stateKey))
-         {
-            const cRecording* pRecording = recordings->GetByName(FileName);
+            // still recording :o ?
+
+            if (pendingTimer && pendingTimer->Recording())
+               continue;
 
             if (pRecording && timerLengthSecs)
             {
                int recLen = RecLengthInSecs(pRecording);
                recFraction = double(recLen) * 100 / timerLengthSecs;
             }
-            
-            stateKey.Remove();
-         }
-#else
-         const cRecording*  pRecording = Recordings.GetByName(FileName);
 
-         if (pRecording && timerLengthSecs)
-         {
-            int recLen = RecLengthInSecs(pRecording);
-            recFraction = double(recLen) * 100 / timerLengthSecs;
-         }
-#endif
-         
-         // assure timer has reached it's end or at least 90% (vps) / 98% were recorded
-         
-         complete = time(0) >= rr->timer->StopTime() || recFraction >= (vpsUsed ? 90 : 98);
-         
-         if (complete) 
-            tell(1, "Info: Finished: '%s'; recorded %d%%; VPS %s", 
-                 rr->timer->File(), recFraction, vpsUsed ? "Yes": "No");
-         else
-            tell(1, "Info: Finished: '%s' (not complete! - recorded only %d%%); VPS %s", 
-                 rr->timer->File(), recFraction, vpsUsed ? "Yes": "No");
-         
-         if (complete)
-            rr->lastBreak = 0;         // reset break
-         else if (!rr->lastBreak) 
-            rr->lastBreak = time(0);   // store first break
-         
-         if (!rr->lastBreak || (time(0) - rr->lastBreak) > allowedBreakDuration)
-         { 
-            char* infoTxt;
-            
-            asprintf(&infoTxt, "Recording '%s' finished - %s complete (%d%%)", 
-                     rr->timer->File(), complete ? "" : "NOT", recFraction);
-            
-            tell(1, "Info: %s", infoTxt);
-            
-            rr->finished = yes;
-            rr->failed = !complete;
-            rr->setInfo(infoTxt);
-            
-            free(infoTxt);
+            // assure timer has reached it's end or at least 90% (vps) / 98% were recorded
+
+            complete = recFraction >= (vpsUsed ? 90 : 98);
+
+            if (complete)
+               tell(1, "Info: Finished: '%s'; recorded %d%%; VPS %s",
+                    rr->timer->File(), (int)round(recFraction), vpsUsed ? "Yes": "No");
+            else
+               tell(1, "Info: Finished: '%s' (not complete! - recorded only %d%%); VPS %s",
+                    rr->timer->File(), (int)round(recFraction), vpsUsed ? "Yes": "No");
+
+            if (complete)
+               rr->lastBreak = 0;         // reset break
+            else if (!rr->lastBreak)
+               rr->lastBreak = time(0);   // store first break
+
+            if (!rr->lastBreak || (time(0) - rr->lastBreak) > allowedBreakDuration)
+            {
+               char* infoTxt;
+
+               asprintf(&infoTxt, "Recording '%s' finished - %s complete (%d%%)",
+                        rr->timer->File(), complete ? "" : "NOT", (int)round(recFraction));
+
+               tell(1, "Info: %s", infoTxt);
+
+               rr->finished = yes;
+               rr->failed = !complete;
+               rr->setInfo(infoTxt);
+
+               free(infoTxt);
+            }
          }
       }
    }
 
-   recordingStateChangedTrigger = yes;
-   waitCondition.Broadcast();            // wakeup
+   return done;
 }
 
 //***************************************************************************
diff --git a/svdrpclient.c b/svdrpclient.c
index 53be015..8b1ee54 100644
--- a/svdrpclient.c
+++ b/svdrpclient.c
@@ -479,7 +479,7 @@ int cSvdrpClient::connect()
    {
       // map hostname to ip
 
-      if (hostInfo = ::gethostbyname(ip))
+      if ((hostInfo = ::gethostbyname(ip)))
          memcpy((char*)&remoteAddr, hostInfo->h_addr, hostInfo->h_length);
 
       else if ((remoteAddr = inet_addr(ip)) == INADDR_NONE)
@@ -515,7 +515,7 @@ int cSvdrpClient::connect()
    {
       if (errno != EINPROGRESS)
       {
-         tell(0, "SVDRPCL: connect to %s:%hu failed: %s", ip, port, strerror(errno));
+         tell(0, "SVDRPCL: connect to %s:%d failed: %s", ip, port, strerror(errno));
          return -1;
       }
 
@@ -554,7 +554,7 @@ int cSvdrpClient::connect()
 
       if (result != 0)
       {
-         tell(0, "SVDRPCL: Error connecting to %s:%hu: %s", ip, port, strerror(errno));
+         tell(0, "SVDRPCL: Error connecting to %s:%d: %s", ip, port, strerror(errno));
          ::close(sock);
 
          return -1;
@@ -597,7 +597,7 @@ int cSvdrpClient::open()
    if (greeting.First() && greeting.First()->Text())
       msg = greeting.First()->Text();
 
-   tell(2, "SVDRPCL: connected to %s:%hu '%s'", ip, port, msg);
+   tell(2, "SVDRPCL: connected to %s:%d '%s'", ip, port, msg);
 
    return 0;
 }
diff --git a/svdrpclient.h b/svdrpclient.h
index f0f7078..d927c47 100644
--- a/svdrpclient.h
+++ b/svdrpclient.h
@@ -17,12 +17,11 @@
 #include "lib/common.h"
 
 #ifdef VDR_PLUGIN
-#  define __STL_CONFIG_H
 #  include <vdr/tools.h>
 #else
 
 //***************************************************************************
-// 
+//
 //***************************************************************************
 
 class cListObject {
@@ -109,7 +108,7 @@ public:
 // Line
 //***************************************************************************
 
-class cLine : public cListObject 
+class cLine : public cListObject
 {
    public:
 
@@ -128,7 +127,7 @@ class cLine : public cListObject
 // SVDRP Client
 //***************************************************************************
 
-class cSvdrpClient 
+class cSvdrpClient
 {
    private:
 
diff --git a/timer.c b/timer.c
index 330271c..5faa03f 100644
--- a/timer.c
+++ b/timer.c
@@ -48,7 +48,7 @@ int cUpdate::performTimerJobs()
    int deleteCount = 0;
    uint64_t start = cTimeMs::Now();
 
-   tell(1, "Checking timer actions pending ..");
+   tell(1, "Checking pending timer actions ..");
 
    // check if timer pending
    {
@@ -66,14 +66,8 @@ int cUpdate::performTimerJobs()
       selectPendingTimerActions->freeResult();
    }
 
-   // get timers lock
-
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-   cTimersLock timersLock(true);
-   cTimers* timers = timersLock.Timers();
-#else
-   cTimers* timers = &Timers;
-#endif
+   GET_TIMERS_WRITE(timers);     // get timers lock
+   GET_CHANNELS_READ(channels);  // get channels lock
 
    // get schedules lock
 
@@ -92,7 +86,7 @@ int cUpdate::performTimerJobs()
 #else
       delete schedulesLock;
 #endif
-      tell(0, "Error: Can't get lock on schedules, aborting timer update!");
+      tell(0, "Info: Can't get lock on schedules, skipping timer update!");
       return fail;
    }
 
@@ -115,17 +109,17 @@ int cUpdate::performTimerJobs()
       if (!timerDb->getValue("DONEID")->isEmpty())
          doneid = timerDb->getIntValue("DONEID");
 
-      tell(0, "DEBUG: Pending Action '%c' for timer (%d), event %ld, doneid %d", requetedAction, timerid, eventid, doneid);
+      tell(1, "DEBUG: Pending Action '%c' for timer (%d), event %ld, doneid %d", requetedAction, timerid, eventid, doneid);
 
       // --------------------------------
       // Delete timer request
 
-      if (requetedAction == taDelete)
+      if (requetedAction == taDelete || requetedAction == taReject)
       {
          if (timerDb->hasValue("VDRUUID", "any"))
          {
-            tell(0, "Error: Ignoring delete request of timer (%d) without VDRUUID", timerid);
-            timerDb->getValue("INFO")->sPrintf("Error: Ignoring delete request of timer (%d) without VDRUUID", timerid);
+            tell(0, "Error: Ignoring delete/reject request of timer (%d) without VDRUUID", timerid);
+            timerDb->getValue("INFO")->sPrintf("Error: Ignoring delete/reject request of timer (%d) without VDRUUID", timerid);
             timerDb->setCharValue("ACTION", taFailed);
             timerDb->setCharValue("STATE", tsError);
             timerDb->update();
@@ -140,7 +134,11 @@ int cUpdate::performTimerJobs()
             if (timer->Recording())
             {
                timer->Skip();
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20302)
+               cRecordControls::Process(timers, time(0));
+#else
                cRecordControls::Process(time(0));
+#endif
             }
 
             timers->Del(timer);
@@ -149,10 +147,10 @@ int cUpdate::performTimerJobs()
          }
          else
          {
-            tell(0, "Info: Timer (%d) not found, ignoring delete request", timerid);
+            tell(0, "Info: Timer (%d) not found, ignoring delete/reject request", timerid);
          }
 
-         updateTimerDone(timerid, doneid, tdsTimerDeleted);
+         updateTimerDone(timerid, doneid, requetedAction == taDelete ? tdsTimerDeleted : tdsTimerRejected);
          timerDb->setCharValue("ACTION", taAssumed);
          timerDb->setCharValue("STATE", tsDeleted);
          timerDb->update();
@@ -165,8 +163,6 @@ int cUpdate::performTimerJobs()
       {
          cSchedule* s = 0;
 
-         tell(0, "DEBUG: Got '%s' request for timer (%d), event (%ld)", timerDb->getStrValue("ACTION"), timerid, eventid);
-
          if (!timer && (requetedAction == taModify || requetedAction == taAdjust))
          {
             tell(0, "Fatal: Timer (%d) not found, skipping modify request", timerid);
@@ -183,19 +179,13 @@ int cUpdate::performTimerJobs()
 
          if (!(s = (cSchedule*)schedules->GetSchedule(channelId)))
          {
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-            cChannelsLock channelsLock(false);
-            const cChannels* channels = channelsLock.Channels();
-#else
-            cChannels* channels = &Channels;
-#endif
             const cChannel* channel = channels->GetByChannelID(channelId);
 
-            tell(0, "Error: Missing channel '%s' (%s) or channel not found, ignoring request",
-                 channel ? channel->Name() : "", timerDb->getStrValue("CHANNELID"));
+            tell(0, "Error: Time (%d), missing channel '%s' (%s) or channel not found, ignoring request",
+                 timerid, channel ? channel->Name() : "", timerDb->getStrValue("CHANNELID"));
 
-            timerDb->getValue("INFO")->sPrintf("Error: Missing channel '%s' (%s) or channel not found, ignoring request",
-                                                 channel ? channel->Name() : "", timerDb->getStrValue("CHANNELID"));
+            timerDb->getValue("INFO")->sPrintf("Error: Timer, (%d), missing channel '%s' (%s) or channel not found, ignoring request",
+                                               timerid, channel ? channel->Name() : "", timerDb->getStrValue("CHANNELID"));
             timerDb->setCharValue("ACTION", taFailed);
             timerDb->setCharValue("STATE", tsError);
             timerDb->update();
@@ -212,29 +202,21 @@ int cUpdate::performTimerJobs()
 
          if (eventid > 0 && !(event = s->GetEvent(eventid)))
          {
-            {
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-               cChannelsLock channelsLock(false);
-               const cChannels* channels = channelsLock.Channels();
-#else
-               cChannels* channels = &Channels;
-#endif
-               const cChannel* channel = channels->GetByChannelID(channelId);
+            const cChannel* channel = channels->GetByChannelID(channelId);
 
-               tell(0, "Error: Missing event '%ld' on channel '%s' (%s), ignoring request",
-                    eventid, channel->Name(), timerDb->getStrValue("CHANNELID"));
+            tell(0, "Error: Timer (%d), missing event '%ld' on channel '%s' (%s), ignoring request",
+                 timerid, eventid, channel->Name(), timerDb->getStrValue("CHANNELID"));
 
-               timerDb->getValue("INFO")->sPrintf("Error: Missing event '%ld' on channel '%s' (%s), ignoring request",
-                                                  eventid, channel->Name(), timerDb->getStrValue("CHANNELID"));
-               timerDb->setCharValue("ACTION", taFailed);
-               timerDb->setCharValue("STATE", tsError);
-               timerDb->update();
-               updateTimerDone(timerid, doneid, tdsTimerCreateFailed);
-            }
+            timerDb->getValue("INFO")->sPrintf("Error: Timer (%d), missing event '%ld' on channel '%s' (%s), ignoring request",
+                                               timerid, eventid, channel->Name(), timerDb->getStrValue("CHANNELID"));
+            timerDb->setCharValue("ACTION", taFailed);
+            timerDb->setCharValue("STATE", tsError);
+            timerDb->update();
+            updateTimerDone(timerid, doneid, tdsTimerCreateFailed);
 
             // force reload of events
 
-            tell(0, "Info: Trigger EPG reload due to missing event!");
+            tell(0, "Info: Trigger EPG full-reload due to missing event!");
             triggerEpgUpdate(yes);
 
             continue;
@@ -270,16 +252,11 @@ int cUpdate::performTimerJobs()
             else
             {
 #if APIVERSNUM >= 20301
-               LOCK_CHANNELS_READ;
-               const cChannels* channels = Channels;
                const cChannel* channel = channels->GetByChannelID(channelId);
 #else
-               cChannels* channels = &Channels;
                cChannel* channel = channels->GetByChannelID(channelId);
 #endif
-               // timer without a event
-
-               timer = new cTimer(no, no, channel);
+               timer = new cTimer(no, no, channel);  // timer without a event
             }
 
             // reset error message in 'reason'
@@ -352,8 +329,6 @@ int cUpdate::performTimerJobs()
                // adjust time to given event ..
 
                cTimer* dummyTimer = new cTimer(event);
-
-               *timer = *dummyTimer;
                timer->SetStart(dummyTimer->Start());
                timer->SetStop(dummyTimer->Stop());
                timer->SetDay(dummyTimer->Day());
@@ -405,6 +380,11 @@ int cUpdate::updateTimerDone(int timerid, int doneid, char state)
 
    if (timerDoneDb->find())
    {
+      // don't toggle 'D'eleted by user to re'J'ected
+
+      if (timerDoneDb->hasCharValue("STATE", tdsTimerDeleted) && state == tdsTimerRejected)
+         return done;
+
       timerDoneDb->setValue("TIMERID", timerid);
       timerDoneDb->setCharValue("STATE", state);
       timerDoneDb->update();
@@ -441,8 +421,10 @@ int cUpdate::updateTimerTable()
    // get timers lock
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-   cTimersLock timersLock(true);
-   cTimers* timers = timersLock.Timers();
+   LOCK_TIMERS_WRITE;
+   cTimers* timers = Timers;
+   // cTimersLock timersLock(true);
+   // cTimers* timers = timersLock.Timers();
 #else
    cTimers* timers = &Timers;
 #endif
diff --git a/ttools.c b/ttools.c
index bb2b0c0..96afed9 100644
--- a/ttools.c
+++ b/ttools.c
@@ -6,6 +6,7 @@
  */
 
 #include <string>
+#include <regex>
 
 #include "update.h"
 #include "ttools.h"
@@ -127,7 +128,7 @@ void removeTag(char* xml, const char* tag)
 
    if ((s = strstr(xml, sTag.c_str())) && (e = strstr(xml, eTag.c_str())))
    {
-      char tmp[1000+TB];
+      char tmp[10000+TB];
 
       e += strlen(eTag.c_str());
 
@@ -196,13 +197,11 @@ int insertTag(char* xml, const char* parent, const char* tag, const char* value)
 
 int setTagTo(cTimer* timer, const char* tag, int value)
 {
-   char aux[10000+TB]; *aux = 0;
+   char aux[10000+TB] = "";
 
    if (!isEmpty(timer->Aux()))
       strcpy(aux, timer->Aux());
 
-   // remove old timerid - if exist
-
    removeTag(aux, tag);
    insertTag(aux, "epgd", tag, value);
 
@@ -213,13 +212,11 @@ int setTagTo(cTimer* timer, const char* tag, int value)
 
 int setTagTo(cTimer* timer, const char* tag, const char* value)
 {
-   char aux[10000+TB]; *aux = 0;
+   char aux[10000+TB] = "";
 
    if (!isEmpty(timer->Aux()))
       strcpy(aux, timer->Aux());
 
-   // remove old timerid - if exist
-
    removeTag(aux, tag);
    insertTag(aux, "epgd", tag, value);
 
@@ -234,7 +231,7 @@ int setTagTo(cTimer* timer, const char* tag, const char* value)
 
 int setTimerId(cTimer* timer, int tid)
 {
-   char aux[10000+TB]; *aux = 0;
+   char aux[10000+TB] = "";
 
    if (!isEmpty(timer->Aux()))
       strcpy(aux, timer->Aux());
@@ -317,18 +314,20 @@ int updateRowByTimer(cDbRow* timerRow, const cTimer* t)
    int autotimerinssp = na;
    int doneid = na;
    int namingmode = na;
+   char tmplExpression[100+TB] = "";
    int childLock = no;
    int epgs = no;
-   char directory[512+TB]; *directory = 0;
-   char source[40+TB]; *source = 0;
+   char directory[512+TB] = "";
+   char source[40+TB] = "";
    cString channelId = t->Event() ? t->Event()->ChannelID().ToString() : t->Channel()->GetChannelID().ToString();
 
    contentOfTag(t, "autotimerid", autotimerid);
    contentOfTag(t, "autotimerinssp", autotimerinssp);
    contentOfTag(t, "doneid", doneid);
    contentOfTag(t, "namingmode", namingmode);
+   contentOfTag(t, "template", tmplExpression, 100);
    contentOfTag(t, "directory", directory, 512);
-   contentOfTag(t, "source", source, 20);
+   contentOfTag(t, "source", source, 40);
 
    timerRow->setValue("VDRUUID", Epg2VdrConfig.uuid);
    timerRow->setValue("EVENTID", t->Event() ? (long)t->Event()->EventID() : 0);
@@ -360,6 +359,9 @@ int updateRowByTimer(cDbRow* timerRow, const cTimer* t)
    if (namingmode != na)
       timerRow->setValue("NAMINGMODE", namingmode);
 
+   if (!isEmpty(tmplExpression))
+      timerRow->setValue("TEMPLATE", tmplExpression);
+
    if (autotimerinssp != na)
       timerRow->setValue("AUTOTIMERINSSP", autotimerinssp);
 
@@ -395,10 +397,18 @@ cEpgTimer* newTimerObjectFromRow(cDbRow* timerRow, cDbRow* vdrRow)
    const cEvent* event = 0;
    cString buf;
    tChannelID channelId = tChannelID::FromString(timerRow->getStrValue("CHANNELID"));
-   cChannel* channel = Channels.GetByChannelID(channelId);
    uint flags = tfNone;
    char* file;
 
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+   LOCK_CHANNELS_READ;
+   const cChannels* channels = Channels;
+   const cChannel* channel = channels->GetByChannelID(channelId);
+#else
+   cChannels* channels = &Channels;
+   const cChannel* channel = channels->GetByChannelID(channelId);
+#endif
+
    const char* dir = timerRow->getStrValue("DIRECTORY");
 
    file = strdup(timerRow->getStrValue("FILE"));
@@ -434,9 +444,13 @@ cEpgTimer* newTimerObjectFromRow(cDbRow* timerRow, cDbRow* vdrRow)
 
    if (channel)
    {
-      cSchedulesLock schedulesLock;
-      const cSchedules* schedules = cSchedules::Schedules(schedulesLock);
-
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+      cStateKey schedulesKey;
+      const cSchedules* schedules = cSchedules::GetSchedulesRead(schedulesKey);
+#else
+      cSchedulesLock* schedulesLock = new cSchedulesLock(false);
+      const cSchedules* schedules = (cSchedules*)cSchedules::Schedules(*schedulesLock);
+#endif
       if (schedules)
       {
          const cSchedule* schedule = schedules->GetSchedule(channel);
@@ -444,6 +458,12 @@ cEpgTimer* newTimerObjectFromRow(cDbRow* timerRow, cDbRow* vdrRow)
          if (schedule)
             event = schedule->GetEvent(timerRow->getIntValue("EVENTID"));
       }
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+      if (schedules) schedulesKey.Remove();
+#else
+      delete schedulesLock;
+#endif
    }
 
    if (event)
@@ -543,6 +563,9 @@ int updateTimerObjectFromRow(cTimer* timer, cDbRow* timerRow, const cEvent* even
    if (!timerRow->getValue("NAMINGMODE")->isNull())
       setTagTo(timer, "namingmode", timerRow->getIntValue("NAMINGMODE"));
 
+   if (!timerRow->getValue("TEMPLATE")->isNull())
+      setTagTo(timer, "template", timerRow->getStrValue("TEMPLATE"));
+
    if (!timerRow->getValue("AUTOTIMERID")->isNull())
       setTagTo(timer, "autotimerid", timerRow->getIntValue("AUTOTIMERID"));
 
diff --git a/update.c b/update.c
index 8651219..6b8ef0f 100644
--- a/update.c
+++ b/update.c
@@ -10,11 +10,68 @@
 #include <vdr/videodir.h>
 #include <vdr/tools.h>
 
+#include "lib/vdrlocks.h"
+#include "lib/xml.h"
 #include "epg2vdr.h"
 #include "update.h"
 #include "handler.h"
 
 //***************************************************************************
+// Events AUX Fields - stored as XML in cEvent:aux
+//***************************************************************************
+
+const char* cUpdate::auxFields[] =
+{
+// field name                type    max size
+
+   "imagecount",          // int
+   "scrseriesid",         // int
+   "scrseriesepisode",    // int
+   "scrmovieid",          // int
+   "numrating",           // int
+
+   "year",                // ascii     10
+   "category",            // ascii     50
+   "country",             // ascii     50
+   "audio",               // ascii     50
+
+   "txtrating",           // ascii    100
+   "genre",               // ascii    100
+   "flags",               // ascii    100
+   "commentator",         // ascii    200
+   "tipp",                // ascii    250
+   "rating",              // ascii    250
+   "moderator",           // ascii    250
+   "music",               // ascii    250
+   "screenplay",          // ascii    500
+   "shortreview",         // ascii    500
+
+   "guest",               // text    1000
+   "producer",            // text    1000
+   "camera",              // text    1000
+   "director",            // text    1000
+   "topic",               // ascii   1000
+
+   "other",               // text    2000
+   "shortdescription",    // mtext   3000
+   "shorttext",           // ascii    300
+   "actor",               // mtext   5000
+
+   "episodename",         // ascii    100
+   "episodeshortname",    // ascii    100
+   "episodepartname",     // ascii    300
+   "episodeextracol1",    // ascii    250
+   "episodeextracol2",    // ascii    250
+   "episodeextracol3",    // ascii    250
+   "episodeseason",       // int
+   "episodepart",         // int
+   "episodeparts",        // int
+   "episodenumber",       // int
+
+   0
+};
+
+//***************************************************************************
 // ctor
 //***************************************************************************
 
@@ -35,13 +92,13 @@ cUpdate::cUpdate(cPluginEPG2VDR* aPlugin)
    recordingFullReloadTrigger = no;
    manualTrigger = no;
    videoBasePath = 0;
+   dbReconnectTriggered = no;
 
    fullreload = no;
    epgdBusy = yes;
    epgdState = cEpgdState::esUnknown;
    mainActPending = no;
    eventsPending = no;
-   epgdStateChangePending = no;
    nextEpgdUpdateAt = 0;
 
    lastUpdateAt = 0;
@@ -84,7 +141,9 @@ cUpdate::cUpdate(cPluginEPG2VDR* aPlugin)
    selectMaxUpdSp = 0;
    selectPendingTimerActions = 0;
 
-   dvbDescription = 0;
+   viewDescription = 0;
+   viewMergeSource = 0;
+   viewLongDescription = 0;
 
    //
 
@@ -222,8 +281,14 @@ int cUpdate::initDb()
 
    if (vdrDb->getIntValue("DBAPI") != DB_API)
    {
-      tell(0, "Found dbapi %d, expected %d, please alter the tables first! Aborting now.",
-           (int)vdrDb->getIntValue("DBAPI"), DB_API);
+      if (vdrDb->getIntValue("DBAPI") < DB_API)
+         tell(0,  "Your database has version %d, epg2vdr expects version %d. Please make sure, epgd and "
+              "epg2vdr use the same version and the database is properly updated",
+              (int)vdrDb->getIntValue("DBAPI"), DB_API);
+      else
+         tell(0, "Found dbapi %d, expected %d, please update me! Aborting now.",
+              (int)vdrDb->getIntValue("DBAPI"), DB_API);
+
       return fail;
    }
 
@@ -271,7 +336,9 @@ int cUpdate::initDb()
    // -------------------------------------------
    // init db values
 
-   dvbDescription = new cDbValue("description", cDBS::ffText, 50000);
+   viewDescription = new cDbValue("description", cDBS::ffText, 50000);
+   viewMergeSource = new cDbValue("mergesource", cDBS::ffAscii, 25);
+   viewLongDescription = new cDbValue("longdescription", cDBS::ffText, 50000);
 
    // -------------------------------------------
    // init statements
@@ -298,8 +365,6 @@ int cUpdate::initDb()
 
    // prepare fields
 
-   // cDBS::FieldDef imageSizeDef = { "image", cDBS::ffUInt,  0, 999, cDBS::ftData };
-
    imageSize.setField(&imageSizeDef);
    imageUpdSp.setField(imageDb->getField("UpdSp"));
    masterId.setField(eventsDb->getField("MasterId"));
@@ -405,7 +470,9 @@ int cUpdate::initDb()
    selectUpdEvents->bind("PARENTALRATING", cDBS::bndOut, ", ");
    selectUpdEvents->bind("VPS",  cDBS::bndOut, ", ");
    selectUpdEvents->bind("CONTENTS",  cDBS::bndOut, ", ");
-   selectUpdEvents->bind(dvbDescription, cDBS::bndOut, ", ");
+   selectUpdEvents->bind(viewDescription, cDBS::bndOut, ", ");
+   selectUpdEvents->bind(viewMergeSource, cDBS::bndOut, ", ");
+   selectUpdEvents->bind(viewLongDescription, cDBS::bndOut, ", ");
    selectUpdEvents->build(" from eventsview where ");
    selectUpdEvents->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet);
    selectUpdEvents->bindCmp(0, "UPDSP", 0, ">", " and ");
@@ -607,7 +674,7 @@ int cUpdate::initDb()
          lastEventsUpdateAt = lastUpdateAt;
          getParameter("uuid", "lastEventsUpdateAt", lastEventsUpdateAt);
 
-         strftime(buf, 50, "%y.%m.%d %H:%M:%S", localtime(&lastUpdateAt));
+         strftime(buf, 50, "%d.%m.%y %H:%M:%S", localtime(&lastUpdateAt));
          tell(0, "Info: Last update was at '%s'", buf);
       }
 
@@ -638,6 +705,11 @@ int cUpdate::initDb()
       vdrDb->setValue("SHAREINWEB", Epg2VdrConfig.shareInWeb);
       vdrDb->setValue("USECOMMONRECFOLDER", Epg2VdrConfig.useCommonRecFolder);
 
+      int osd2WebPort = getOsd2WebPort();
+
+      if (osd2WebPort != na)
+         vdrDb->setValue("OSD2WEBP", osd2WebPort);
+
       // set svdrp port if uninitialized, we can't query ther actual port from VDR :(
 
       if (vdrDb->getIntValue("SVDRP") == 0)
@@ -649,12 +721,8 @@ int cUpdate::initDb()
    }
 
    if (status == success)
-   {
       status += cEpg2VdrEpgHandler::getSingleton()->updateExternalIdsMap(mapDb);
 
-      updateEpgdState();
-   }
-
    return status;
 }
 
@@ -705,7 +773,9 @@ int cUpdate::exitDb()
    delete recordingDirDb;         recordingDirDb = 0;
    delete recordingListDb;        recordingListDb = 0;
 
-   delete dvbDescription;         dvbDescription = 0;
+   delete viewDescription;        viewDescription = 0;
+   delete viewMergeSource;        viewMergeSource = 0;
+   delete viewLongDescription;    viewLongDescription = 0;
 
    delete connection; connection = 0;
 
@@ -727,6 +797,8 @@ int cUpdate::checkConnection(int& timeout)
    if (dbReconnectTriggered)
       exitDb();
 
+   dbReconnectTriggered = no;
+
    // check connection
 
    if (!dbConnected(yes))
@@ -755,20 +827,76 @@ int cUpdate::checkConnection(int& timeout)
 // Is Handler Master
 //***************************************************************************
 
+int cUpdate::getOsd2WebPort()
+{
+   Osd2Web_Port_v1_0 req;
+   req.webPort = na;
+
+   cPlugin* osd2webPlugin = cPluginManager::GetPlugin("osd2web");
+   int osd2webPluginUuidService = osd2webPlugin && osd2webPlugin->Service(OSD2WEB_PORT_SERVICE, 0);
+
+   if (!osd2webPluginUuidService)
+      return na;
+
+   if (!osd2webPlugin->Service(OSD2WEB_PORT_SERVICE, &req))
+   {
+      tell(0, "Error: Call of service '%s' failed.", OSD2WEB_PORT_SERVICE);
+      return na;
+   }
+
+   tell(0, "Got webPort '%d' by osd2web", req.webPort);
+
+   return req.webPort;
+}
+
+//***************************************************************************
+// Is Handler Master
+//***************************************************************************
+
 int cUpdate::isHandlerMaster()
 {
-   char buf[1+TB];
+   static int initialized = no;
+
    char flag = 0;
 
 /*
-  wenn no    - mich ausschalten und feritg (db 'N' und handler off)
-  wenn auto  - aushandeln wie gehabt
-  wenn yes   - (db 'Y' und handler on)
+  wenn no    - handler ausschalten und db auf 'N'
+  wenn auto  - aushandeln
+  wenn yes   - handler anschalten und db auf 'Y'
 
 */
    if (!dbConnected())
       return no;
 
+   // on first call detect state of epgd
+
+   if (!initialized)
+   {
+      epgdBusy = no;
+
+      vdrDb->clear();
+      vdrDb->setValue("UUID", EPGDNAME);
+
+      if (vdrDb->find())
+      {
+         nextEpgdUpdateAt = vdrDb->getIntValue("NEXTUPDATE");
+         epgdState = cEpgdState::toState(vdrDb->getStrValue("State"));
+
+         if (epgdState >= cEpgdState::esBusy && epgdState < cEpgdState::esBusyImages)
+            epgdBusy = yes;
+
+         tell(1, "Detected epgd state '%s' (%d)", vdrDb->getStrValue("State"), epgdState);
+         initialized = yes;
+      }
+      else
+      {
+         tell(0, "Info: Can't detect epgd state");
+         epgdBusy = yes;
+      }
+   }
+
+   // update handler role
+
    if (Epg2VdrConfig.masterMode == mmAuto)
    {
       tell(3, "Auto check master role");
@@ -782,8 +910,8 @@ int cUpdate::isHandlerMaster()
 
       if (!handlerMaster)
          tell(3, "Master found, uuid '%s' (%s)",
-              vdrDb->getStrValue("Uuid"),
-              vdrDb->getStrValue("Name"));
+              vdrDb->getStrValue("UUID"),
+              vdrDb->getStrValue("NAME"));
 
       selectMasterVdr->freeResult();
 
@@ -807,57 +935,58 @@ int cUpdate::isHandlerMaster()
    vdrDb->find();
 
    vdrDb->setValue("STATE", "attached");
-   vdrDb->setValue("MASTER", c2s(flag, buf));
+   vdrDb->setCharValue("MASTER", flag);
    vdrDb->store();
 
+   // set handler state
+
    int handlerState = !epgdBusy && handlerMaster;
 
    if (cEpg2VdrEpgHandler::getSingleton()->getActive() != handlerState)
    {
       tell(1, "Change handler state to '%s'", handlerState ? "active" : "standby");
-
       cEpg2VdrEpgHandler::getSingleton()->setActive(handlerState);
    }
 
    return handlerMaster;
 }
 
-// ***************************************************************************
-// Initially Init Epgd State
-// ***************************************************************************
+// // ***************************************************************************
+// // Initially Init Epgd State
+// // ***************************************************************************
 
-void cUpdate::updateEpgdState()
-{
-   epgdBusy = no;
+// void cUpdate::updateEpgdState()
+// {
+//    epgdBusy = no;
 
-   if (!dbConnected())
-      return;
+//    if (!dbConnected())
+//       return;
 
-   vdrDb->clear();
-   vdrDb->setValue("UUID", EPGDNAME);
+//    vdrDb->clear();
+//    vdrDb->setValue("UUID", EPGDNAME);
 
-   if (vdrDb->find())
-   {
-      nextEpgdUpdateAt = vdrDb->getIntValue("NEXTUPDATE");
-      epgdState = cEpgdState::toState(vdrDb->getStrValue("State"));
+//    if (vdrDb->find())
+//    {
+//       nextEpgdUpdateAt = vdrDb->getIntValue("NEXTUPDATE");
+//       epgdState = cEpgdState::toState(vdrDb->getStrValue("State"));
 
-      if (epgdState >= cEpgdState::esBusy && epgdState < cEpgdState::esBusyImages)
-         epgdBusy = yes;
+//       if (epgdState >= cEpgdState::esBusy && epgdState < cEpgdState::esBusyImages)
+//          epgdBusy = yes;
 
-      tell(1, "Detected epgd state '%s' (%d)", vdrDb->getStrValue("State"), epgdState);
-   }
-   else
-      tell(0, "Info: Can't detect epgd state");
+//       tell(1, "Detected epgd state '%s' (%d)", vdrDb->getStrValue("State"), epgdState);
+//    }
+//    else
+//       tell(0, "Info: Can't detect epgd state");
 
-   int handlerState = !epgdBusy && isHandlerMaster();
+//    int handlerState = !epgdBusy && isHandlerMaster();
 
-   if (cEpg2VdrEpgHandler::getSingleton()->getActive() != handlerState)
-      tell(0, "Set handler state initially to '%s'", handlerState ? "active" : "standby");
+//    if (cEpg2VdrEpgHandler::getSingleton()->getActive() != handlerState)
+//       tell(0, "Set handler state initially to '%s'", handlerState ? "active" : "standby");
 
-   cEpg2VdrEpgHandler::getSingleton()->setActive(handlerState);
+//    cEpg2VdrEpgHandler::getSingleton()->setActive(handlerState);
 
-   vdrDb->reset();
-}
+//    vdrDb->reset();
+// }
 
 // ***************************************************************************
 // Update VDR Data
@@ -1009,6 +1138,7 @@ void cUpdate::triggerTimerJobs()
 
 //***************************************************************************
 // Epgd State Change
+//  - called via SVDRP
 //***************************************************************************
 
 void cUpdate::epgdStateChange(const char* state)
@@ -1018,8 +1148,6 @@ void cUpdate::epgdStateChange(const char* state)
    if (!dbConnected())
       return;
 
-   epgdStateChangePending = yes;
-
    epgdState = cEpgdState::toState(state);
    epgdBusy = epgdState >= cEpgdState::esBusy && epgdState < cEpgdState::esBusyImages;
 
@@ -1102,6 +1230,9 @@ void cUpdate::Action()
 
       if (dbConnected() && recordingStateChangedTrigger)
       {
+         if (!pendingRecordingActions.empty())
+            performRecordingActions();
+
          if (Epg2VdrConfig.shareInWeb)
             recordingChanged();            // update timer state
 
@@ -1134,7 +1265,7 @@ void cUpdate::Action()
 
          if (dbConnected() && timerJobsUpdateTriggered)
          {
-            tell(1, "Updating EPG prior to 'check of timer request'");
+            tell(2, "Updating EPG prior to 'check of timer request'");
             refreshEpg(0, na);                  // refresh EPG before performing timer jobs!
             performTimerJobs();
          }
@@ -1154,7 +1285,7 @@ void cUpdate::Action()
       {
          time_t lastUpdateStartAt = time(0);
 
-         tell(mainActPending ? 0 : 2, "--- EPG %s started ---", fullreload ? "reload" : mainActPending ? "update" : "refresh");
+         tell(mainActPending ? 0 : 2, "--- EPG '%s' started ---", fullreload ? "full-reload" : mainActPending ? "update" : "refresh");
 
          if (mainActPending)
          {
@@ -1222,10 +1353,10 @@ void clearEpg()
    LOCK_TIMERS_WRITE;
    LOCK_SCHEDULES_WRITE;
 
-   for (cTimer* timer = Timers->First(); timer; timer = Timers->Next(Timer))
+   for (cTimer* timer = Timers->First(); timer; timer = Timers->Next(timer))
       timer->SetEvent(0);      // processing all timers here (local *and* remote)
 
-   for (cSchedule* schedule = Schedules->First(); schedule; schedule = Schedules->Next(Schedule))
+   for (cSchedule* schedule = Schedules->First(); schedule; schedule = Schedules->Next(schedule))
       schedule->Cleanup(INT_MAX);
 
    cEitFilter::SetDisableUntil(time(0) + 10);
@@ -1238,6 +1369,42 @@ void clearEpg()
 #endif
 }
 
+// //***************************************************************************
+// // Get Schedule Of
+// //***************************************************************************
+
+// int getScheduleOf(tChannelID channelId, const cSchedules* schedules, cSchedule*& s)
+// {
+//    cChannel* channel = 0;
+
+//    s = 0;
+
+//    // get channels lock
+
+// #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+//    cStateKey channelsKey;
+//    cChannels* channels = cChannels::GetChannelsWrite(channelsKey, 500);
+// #else
+//    cChannels* channels = &Channels;
+// #endif
+
+//    if (!channels)
+//       return fail;
+
+//    // get channel and schedule of channel
+
+//    if (channel = channels->GetByChannelID(channelId, true))
+//       s = (cSchedule*)schedules->GetSchedule(channel, true);
+//    else
+//       tell(0, "Error: Channel with ID '%s' don't exist on this VDR", (const char*)channelId.ToString());
+
+// #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+//    channelsKey.Remove();
+// #endif
+
+//    return success;
+// }
+
 //***************************************************************************
 // Refresh Epg
 //***************************************************************************
@@ -1253,6 +1420,9 @@ int cUpdate::refreshEpg(const char* forChannelId, int maxTries)
    uint64_t start = cTimeMs::Now();
    cDbStatement* select = 0;
 
+   if (Epg2VdrConfig.loglevel >= 5)
+      connection->showStat("before refresh");
+
    // lookback ...
 
    getParameter("uuid", "lastEventsUpdateAt", lastEventsUpdateAt);
@@ -1285,15 +1455,16 @@ int cUpdate::refreshEpg(const char* forChannelId, int maxTries)
       select = selectAllChannels;
 
       if (lastEventsUpdateAt)
-         tell(mainActPending ? 1 : 2, "Update EPG, loading changes since %s", l2pTime(lastEventsUpdateAt).c_str());
+         tell(2, "Update EPG, loading changes since %s", l2pTime(lastEventsUpdateAt).c_str());
       else
-         tell(1, "Update EPG, reloading all events");
+         tell(2, "Update EPG, reloading all events");
    }
 
    for (int f = select->find(); f && dbConnected(yes); f = select->fetch())
    {
       int count = 0;
       cSchedule* s = 0;
+      cChannel* channel = 0;
       tChannelID channelId = tChannelID::FromString(mapDb->getStrValue("ChannelId"));
 
       channels++;
@@ -1302,17 +1473,26 @@ int cUpdate::refreshEpg(const char* forChannelId, int maxTries)
       eventsDb->setValue("UPDSP", forChannelId ? 0 : lastEventsUpdateAt);
       eventsDb->setValue("CHANNELID", mapDb->getStrValue("CHANNELID"));
 
-      // get timers lock
+      // #1 get timers lock
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
       cStateKey timersKey;
-      tell(2, "-> Try to get timers lock");
+      tell(3, "-> Try to get timers lock");
       cTimers* timers = cTimers::GetTimersWrite(timersKey, 500/*ms*/);
 #else
       cTimers* timers = &Timers;
 #endif
 
-      // get schedules lock
+      // #2 get channels lock
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+      cStateKey channelsKey;
+      cChannels* channels = cChannels::GetChannelsWrite(channelsKey, 500);
+#else
+      cChannels* channels = &Channels;
+#endif
+
+      // #3 get schedules lock
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
       cStateKey schedulesKey;
@@ -1324,13 +1504,21 @@ int cUpdate::refreshEpg(const char* forChannelId, int maxTries)
       tell(3, "LOCK (refreshEpg)");
 #endif
 
-      if (!schedules || !timers)
+      // get channel and schedule of channel
+
+      if (channels && schedules && (channel = channels->GetByChannelID(channelId, true)))
+         s = (cSchedule*)schedules->GetSchedule(channel, true);
+      else if (channels && schedules)
+         tell(0, "Error: Channel with ID '%s' don't exist on this VDR", mapDb->getStrValue("ChannelId"));
+
+      if (!schedules || !channels || !timers)
       {
-         tell(0, "Info: Can't get write lock on '%s'", !schedules ? "schedules" : "timers");
+         tell(3, "Info: Can't get write lock on '%s'", !schedules ? "schedules" : !timers ? "timers" : "channels");
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
          if (schedules) schedulesKey.Remove();
          if (timers)    timersKey.Remove();
+         if (channels) channelsKey.Remove();
 #else
          delete schedulesLock;
 #endif
@@ -1338,11 +1526,11 @@ int cUpdate::refreshEpg(const char* forChannelId, int maxTries)
          if (tries++ > maxTries)
          {
             select->freeResult();
-            tell(0, "Warning: Aborting refresh after %d tries", tries);
+            tell(3, "Warning: Aborting refresh after %d tries", tries);
             break;
          }
 
-         tell(0, "Retrying in 3 seconds");
+         tell(3, "Retrying in 3 seconds");
          sleep(3);
 
          continue;
@@ -1350,118 +1538,120 @@ int cUpdate::refreshEpg(const char* forChannelId, int maxTries)
 
       tries = 0;
 
-      // get schedule (channel)
+      // lookup schedules object
 
-      if (!(s = (cSchedule*)schedules->GetSchedule(channelId)))
-         s = schedules->AddSchedule(channelId);
+      if (s)
+      {
+         // -----------------------------------------
+         // iterate over all events of this schedule
 
-      // -----------------------------------------
-      // iterate over all events of this channel
+         for (int found = selectUpdEvents->find(); found && dbConnected(); found = selectUpdEvents->fetch())
+         {
+            cTimer* timer = 0;
+            char updFlg = toupper(eventsDb->getStrValue("UPDFLG")[0]);
 
-      for (int found = selectUpdEvents->find(); found && dbConnected(); found = selectUpdEvents->fetch())
-      {
-         cTimer* timer = 0;
-         char updFlg = toupper(eventsDb->getStrValue("UPDFLG")[0]);
+            updFlg = updFlg == 0 ? 'P' : updFlg;               // fix missing flag
 
-         updFlg = updFlg == 0 ? 'P' : updFlg;               // fix missing flag
+            // ignore unneded event rows ..
 
-         // ignore unneded event rows ..
+            if (!Us::isNeeded(updFlg))
+               continue;
 
-         if (!Us::isNeeded(updFlg))
-            continue;
+            // get event / timer
 
-         // get event / timer
+            if ((event = s->GetEvent(eventsDb->getIntValue("USEID"))))
+            {
+               if (Us::isRemove(updFlg))
+                  tell(2, "Remove event %uld of channel '%s' due to updflg %c",
+                       event->EventID(), (const char*)event->ChannelID().ToString(), updFlg);
 
-         if (event = s->GetEvent(eventsDb->getIntValue("USEID")))
-         {
-            if (Us::isRemove(updFlg))
-               tell(2, "Remove event %uld of channel '%s' due to updflg %c",
-                    event->EventID(), (const char*)event->ChannelID().ToString(), updFlg);
+               if (event->HasTimer())
+               {
+                  for (timer = timers->First(); timer; timer = timers->Next(timer))
+                     if (timer->Event() == event)
+                        break;
+               }
+
+               if (timer)
+                  timer->SetEvent(0);
 
-            if (event->HasTimer())
+               s->DelEvent((cEvent*)event);
+            }
+
+            if (!Us::isRemove(updFlg))
+               event = s->AddEvent(createEventFromRow(eventsDb->getRow()));
+            else if (event)
             {
-               for (timer = timers->First(); timer; timer = timers->Next(timer))
-                  if (timer->Event() == event)
-                     break;
+               event = 0;
+               dels++;
             }
 
-            if (timer)
-               timer->SetEvent(0);
+            if (timer && event)
+            {
+               timer->SetEvent(event);
+               timer->Matches(event);
+               timerChanges++;
+            }
+            else if (timer)
+            {
+               tell(0, "Info: Timer '%s', has no event anymore", *timer->ToDescr());
+            }
 
-            s->DelEvent((cEvent*)event);
+            count++;
          }
 
-         if (!Us::isRemove(updFlg))
-            event = s->AddEvent(createEventFromRow(eventsDb->getRow()));
-         else if (event)
-         {
-            event = 0;
-            dels++;
-         }
+         selectUpdEvents->freeResult();
 
-         if (timer && event)
-         {
-            timer->SetEvent(event);
-            timer->Matches(event);
-            timerChanges++;
-         }
-         else if (timer)
-         {
-            tell(0, "Info: Timer '%s', has no event anymore", *timer->ToDescr());
-         }
-
-         count++;
-      }
+         // Kanal fertig machen ..
 
-      selectUpdEvents->freeResult();
+         s->Sort();
+         s->SetModified();
 
-      // Kanal fertig machen ..
+         tell(2, "Processed channel '%s' - '%s' with %d updates",
+              eventsDb->getStrValue("CHANNELID"),
+              mapDb->getStrValue("CHANNELNAME"),
+              count);
 
-      s->Sort();
-      s->SetModified();
+         total += count;
+      }
 
       // schedules lock freigeben
 
 #if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
       schedulesKey.Remove();
       tell(3, "-> Released schedules lock");
+      channelsKey.Remove();
+      tell(3, "-> Released channels lock");
       timersKey.Remove();
       tell(3, "-> Released timers lock");
 #else
       tell(3, "LOCK free (refreshEpg)");
       delete schedulesLock;
 #endif
-
-      tell(2, "Processed channel '%s' - '%s' with %d updates",
-           eventsDb->getStrValue("ChannelId"),
-           mapDb->getStrValue("ChannelName"),
-           count);
-
-      total += count;
    }
 
    select->freeResult();
 
    if (timerChanges)
    {
-#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
-      LOCK_TIMERS_WRITE;
-      cTimers* timers = Timers;
-#else
-      cTimers* timers = &Timers;
-#endif
+      GET_TIMERS_WRITE(timers);
       timers->SetModified();
    }
 
    if (lastEventsUpdateAt)
-      tell(mainActPending ? 0 : 1, "Updated changes since '%s'; %d channels, "
+      tell(1, "Updated changes since '%s'; %d channels, "
            "%d events (%d deletions) in %s",
            forChannelId ? "-" : l2pTime(lastEventsUpdateAt).c_str(),
            channels, total, dels, ms2Dur(cTimeMs::Now()-start).c_str());
    else
-      tell(0, "Updated all %d channels, %d events (%d deletions) in %s",
+      tell(1, "Updated all %d channels, %d events (%d deletions) in %s",
            channels, total, dels, ms2Dur(cTimeMs::Now()-start).c_str());
 
+   // print sql statistic for statement debugging
+
+   if (Epg2VdrConfig.loglevel >= 5)
+      connection->showStat("refresh");
+
    return dbConnected(yes) ? success : fail;
 }
 
@@ -1481,9 +1671,10 @@ cEvent* cUpdate::createEventFromRow(const cDbRow* row)
    e->SetDuration(row->getIntValue("DURATION"));
    e->SetParentalRating(row->getIntValue("PARENTALRATING"));
    e->SetVps(row->getIntValue("VPS"));
-   e->SetDescription(dvbDescription->getStrValue());
+   e->SetDescription(viewDescription->getStrValue());
    e->SetComponents(0);
 
+   // ------------
    // contents
 
    uchar contents[MaxEventContents] = { 0 };
@@ -1499,6 +1690,7 @@ cEvent* cUpdate::createEventFromRow(const cDbRow* row)
 
    e->SetContents(contents);
 
+   // ------------
    // components
 
    if (row->hasValue("SOURCE", "vdr"))
@@ -1526,6 +1718,50 @@ cEvent* cUpdate::createEventFromRow(const cDbRow* row)
          delete components;
    }
 
+#if (defined (APIVERSNUM) && (APIVERSNUM >= 20304)) || (WITH_AUX_PATCH)
+
+   // ------------
+   // aux
+
+   if (Epg2VdrConfig.extendedEpgData2Aux)
+   {
+      useeventsDb->clear();
+      useeventsDb->setValue("USEID", row->getIntValue("USEID"));
+
+      if (selectEventById->find())
+      {
+         cXml xml;
+
+         xml.create("epg2vdr");
+
+         for (int i = 0; auxFields[i]; i++)
+         {
+            cDbValue* value = useeventsDb->getValue(auxFields[i]);
+
+            if (!value || value->isEmpty())
+               continue;
+
+            if (value->getField()->hasFormat(cDBS::ffAscii) || value->getField()->hasFormat(cDBS::ffText) || value->getField()->hasFormat(cDBS::ffMText))
+               xml.appendElement(auxFields[i], value->getStrValue());
+            else
+               xml.appendElement(auxFields[i], value->getIntValue());
+         }
+
+         // finally add some fields of the view
+
+         xml.appendElement("source", viewMergeSource->getStrValue());
+         xml.appendElement("longdescription", viewLongDescription->getStrValue());
+
+         // set to events aux field
+
+         e->SetAux(xml.toText());
+      }
+
+      selectEventById->freeResult();
+   }
+
+#endif // WITH_AUX_PATCH
+
    return e;
 }
 
diff --git a/update.h b/update.h
index 32687c6..aca9a87 100644
--- a/update.h
+++ b/update.h
@@ -11,13 +11,13 @@
 #include <mysql/mysql.h>
 #include <queue>
 
-#define __STL_CONFIG_H
-
 #include <vdr/status.h>
 
 #include "lib/common.h"
 #include "lib/db.h"
 #include "lib/epgservice.h"
+#include "lib/vdrlocks.h"
+#include "lib/xml.h"
 
 #include "epg2vdr.h"
 #include "parameters.h"
@@ -67,7 +67,7 @@ class cRunningRecording : public cListObject
 };
 
 //***************************************************************************
-// Event Details
+// Event Details (Recording Info Files)
 //***************************************************************************
 
 class cEventDetails
@@ -75,7 +75,7 @@ class cEventDetails
    public:
 
       cEventDetails()     { changes = 0; }
-      ~cEventDetails()    { }
+      ~cEventDetails()    {}
 
       int getChanges()    { return changes; }
       void clearChanges() { changes = 0; }
@@ -84,10 +84,12 @@ class cEventDetails
       void setValue(const char* name, int value);
 
       int storeToFs(const char* path);
-      int loadFromFs(const char* path);
+      int loadFromFs(const char* path, cDbRow* row, int doClear = yes);
       int updateByRow(cDbRow* row);
       int updateToRow(cDbRow* row);
 
+      static int row2Xml(cDbRow* row, cXml* xml);
+
    private:
 
       int changes;
@@ -148,6 +150,16 @@ class cUpdate : public cThread, public cStatus, public cParameters
          char channelId[100];
       };
 
+      // struct to store a recording action delieverd by the status interface
+
+      struct RecordingAction
+      {
+         std::string name;
+         std::string fileName;
+         int cardIndex;
+         bool on;
+      };
+
       // functions
 
       int initDb();
@@ -155,7 +167,6 @@ class cUpdate : public cThread, public cStatus, public cParameters
 
       void Action(void);
       int isHandlerMaster();
-      void updateEpgdState();
       void updateVdrData();
       int updateRecFolderOption();
       int dbConnected(int force = no)
@@ -168,6 +179,7 @@ class cUpdate : public cThread, public cStatus, public cParameters
       int storePicturesToFs();
       int cleanupPictures();
       int pictureLinkNeeded(const char* linkName);
+      int getOsd2WebPort();
 
       tChannelID toChanID(const char* chanIdStr)
       {
@@ -191,6 +203,7 @@ class cUpdate : public cThread, public cStatus, public cParameters
       int cleanupDeletedRecordings(int force = no);
       int updateRecordingDirectory(const cRecording* recording);
       int updatePendingRecordingInfoFiles(const cRecordings* recordings);
+      int performRecordingActions();
       int storeAllRecordingInfoFiles();
       int updateRecordingInfoFiles();
 
@@ -228,7 +241,6 @@ class cUpdate : public cThread, public cStatus, public cParameters
       int epgdBusy;
       int eventsPending;
       int mainActPending;
-      int epgdStateChangePending;
       const char* videoBasePath;
       int timersTableMaxUpdsp;
 
@@ -272,10 +284,15 @@ class cUpdate : public cThread, public cStatus, public cParameters
       cDbValue imageSize;
       cDbValue masterId;
 
-      cDbValue* dvbDescription;
+      cDbValue* viewDescription;
+      cDbValue* viewMergeSource;
+      cDbValue* viewLongDescription;
 
-      std::queue<std::string> pendingNewRecordings; // recordings to store details
+      std::queue<std::string> pendingNewRecordings; // recordings to store details (obsolete if pendingRecordingActions implemented finally)
+      std::queue<RecordingAction> pendingRecordingActions; // recordings actions (start/stop)
       std::vector<TimerId> deletedTimers;
+
+      static const char* auxFields[];
 };
 
 //***************************************************************************

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vdr-dvb/vdr-plugin-epg2vdr.git



More information about the pkg-vdr-dvb-changes mailing list