summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvideo src <videosrc@vdr.muc.bieringer.de>2008-12-20 08:50:04 +0100
committervideo src <videosrc@vdr.muc.bieringer.de>2008-12-20 08:50:04 +0100
commit21fd75ff07fe1f3655a758603571b03f18737157 (patch)
treefeda8b931a103cde74f86694fe14f744a5bd9375
parent8850eea8baddb579346d6977a2980f8f587687e4 (diff)
downloadvdr-plugin-tvtv-21fd75ff07fe1f3655a758603571b03f18737157.tar.gz
vdr-plugin-tvtv-21fd75ff07fe1f3655a758603571b03f18737157.tar.bz2
Import of last official release 0.3.3
-rw-r--r--COPYING340
-rw-r--r--HISTORY205
-rw-r--r--Makefile90
-rw-r--r--README88
-rw-r--r--channelmap.c89
-rw-r--r--channelmap.h35
-rw-r--r--config.c33
-rw-r--r--config.h45
-rw-r--r--examples/tvtv_channelmap.conf_DVB-S506
-rw-r--r--examples/tvtv_channelmap.conf_DVB_Mch62
-rw-r--r--i18n.c590
-rw-r--r--i18n.h16
-rw-r--r--md5tools.c207
-rw-r--r--md5tools.h47
-rw-r--r--tvtv.c308
-rw-r--r--update.c703
-rw-r--r--update.h82
17 files changed, 3446 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..018e70e
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,205 @@
+VDR Plugin 'tvtv' Revision History
+-----------------------------------
+
+2007-02-06: Version 0.3.3
+- The plugin needed to be restarted if username or password were changed
+ since the "Packed String" (contains username and password) was only
+ generated once during start-up
+- The time shift bugfix is now configurable via a new setup option
+ solving problems with timers not using CET/CEST as timezone (e.g. BBC)
+- In case a channel is not found in the tvtv_channelmap.conf file, the
+ channels name as well as its ID is printed in the logfile. This should
+ help adding new channels to the config file.
+- tvtv_channelmap.conf updated
+
+2007-01-02: Version 0.3.2
+- Code clean up
+- Deleted timer object might cause VDR to be killed if timer is deleted
+ within TVTV. (thx to monty35)
+
+2006-10-23: Version 0.3.1
+- added some #IFs making it compile on older VDR versions again.
+- Code clean up
+- tvtv_channelmap.conf updated
+- "not confirmed" message removed from logfile by using an own routine
+ to display OSD-Message
+- TVTV bugfix now also works with VPS-Timers
+- empty entry in config file may lead to segfault (thx to monty35 for
+ reporting this one)
+
+2006-10-09: Version 0.3.0
+- new SVDRP command "UPDATE" to update TVTV timers.
+- ChannelMap.h replaced by config file tvtv_channelmap.conf making it
+ easier to maintain. The format of the configfile is slightly different
+ to the older ChannelMap.h now supporting different satellites and
+ countries. The channel map can be reloaded by the plugins setup menu.
+- The new SVDRP command "RELOAD" reloads the channel map configuration
+ file.
+- Timer updates are rejected if shifted 1 or 2 hours earlier (depending
+ on the current time zone). This behaviour can be controlled by a new
+ setup option.
+- Replaced comparison of string::npos with "unsigned int" in routine
+ ProcessImportedFile() since it may lead to problems on 64bit machines.
+
+
+2006-09-25: Version 0.2.14
+- i18n.c updated
+- ChannelMap.h updated
+- Timer updates are rejected if shifted 2 hours earlier. Successful and
+ rejected updates are documented in the logfile (thx to monty35).
+
+2006-04-25: Version 0.2.13a
+- Function ShowName() in class cChannel is not available on VDR
+ older than 1.3.15. Added an ifdef to solve this (thanks to GTRDRIVER
+ for reporting this one)
+
+2006-04-18: Version 0.2.13
+- new setup option decides whether to use the description coming
+ with the data from TVTV or not (just available for VDR 1.3.44+).
+ Starting with VDR 1.3.44 timer description is only taken from
+ EPG so there is no need for the description from TVTV anymore.
+- Added logging of downloaded data to /var/log/vdr_tvtv.log for
+ debugging purpose (suggested by Schlappo)
+- Station name of timer is now added to File Name of recording.
+ This behaviour can be controlled by a new setup option.
+- Adapted Makefile due to new requirements of VDR 1.3.47+
+
+2006-03-16: Version 0.2.12
+- added basic support for VDR 1.3.44. Timer description coming
+ from TVTV is now put into the Aux field of Timer-Object. Need
+ to investigate how this works with recordings without EPG.
+- Non-initialized data structure for HTTP download might cause
+ VDR to be killed. Proper initialization should solve this.
+- Missing define statements disabled VPS support in 0.2.11
+- added BBC channels to ChannelMap.h
+
+2006-02-17: Version 0.2.11
+- split the source code into separate files making it easier to
+ maintain.
+- TVTV-Server can now be selected via OSD
+- libcurl is now used to download data from TVTV adding proxy
+ support to the plugin.
+- do not delete timers that are currently recording, even if it's
+ just updated.
+- Plugin may hang on 64bit machines in routine calc_field_cnt() due
+ to compare values with different types (thx to bunghole)
+
+2005-12-15: Version 0.2.10a
+- VDR crashes if a timer with an empty summary exists and a timer
+ is added by TVTV (thx to mag128)
+
+2005-12-14: Version 0.2.10
+- ostringstream is used instead of snprintf() to create timer
+ entries.
+- Buffer for http download increased to 500kB
+- Timer entries are now created with full date if VDR 1.3.23+
+ is used
+- Debug messages added
+
+2005-11-07: Version 0.2.9
+- Updated for VDR 1.3.36: Timer entry is no longer limited
+ and description is not cut off if VDR-Version 1.3.36+ is
+ detected
+
+2005-11-02: Version 0.2.8a
+- Timer entry needs an additional cut off
+
+2005-11-01: Version 0.2.8
+- TVTV crashes if timer entry exceeds 10kB. Description is
+ cut off to keep it shorter (thx to monty35)
+- Global constant MAXPARSEBUFFER is used instead of
+ plain values
+
+2005-10-16: Version 0.2.7
+- ChannelMap.h updated
+
+2005-10-03: Version 0.2.6b
+- Timers are deleted even if they have changed since the
+ last update (thx to monty35)
+- Debug messages updated (thx to monty35)
+
+2005-10-03: Version 0.2.6a
+- ChannelMap.h updated ('NICK' added, thx to wilderigel)
+
+2005-10-03: Version 0.2.6
+- Timers may be updated by adding the UID coming from
+ TVTV at the end of the description (thx to monty35)
+- Timers with colon in title will be handled as suggested in
+ VDRs manual
+
+2005-09-28: Version 0.2.5
+- ChannelMap.h updated ('Das Vierte' added)
+- split_csv() routine might cause vdr to be killed
+ (thx to ck-one for reporting this one)
+
+2005-09-24: Version 0.2.4
+- English translations updated
+- Debug messages updated (thx to monty35)
+- Fixed handling of VPS-Timers starting at midnight
+ (thx to uclara)
+
+2005-05-15: Version 0.2.3
+- ChannelMap.h updated
+- Number of timers is no longer limited by the plugin.
+- "Delete-Patch" replaced by following the action coming
+ with the timer.
+- Filename of created timer does not contain "TVTV+" anymore
+- New setup option "Use VPS" decides whether timers created
+ via TVTV use VPS or not. Timers set via EPG are not effected.
+- Compiles on VDR older than 1.3.18
+- German translations updated
+
+2005-05-09: Version 0.2.2
+- Timer is now created by using the time zone information
+ to be able to record e.g. english channels.
+ (thx to Briandorling for bringing this up)
+- does not use clear() method anymore
+
+2005-05-08: Version 0.2.1
+- ChannelMap.h updated (thx to monty35)
+- Ignore folder setting if nature is empty (thx to monty35)
+
+2005-05-06: Version 0.2.0
+- Data format changed to CSV (Comma Separated Values)
+
+
+2003-01-28: Version 0.1.6
+- Plugin can make a HTTP-Request without "wget".
+ And XML File will be not more created ;-))
+- OSD-Setup Menu Item "Show/Hide TVTV in Main menu"
+
+2002-12-01: Version 0.1.5
+
+- You are need a no changes (tools.h) in VDR Version >= 1.1.18
+- For XML file will be used your "Video" Directory
+
+2002-11-24: Version 0.1.4
+
+- IMPORTANT: Command Line settings is default settings.
+ after Save in OSD Setup, it will be skipped.
+ You can delete now your default settings.
+ Your settings will be saved in (/video)/setup.conf
+- Support VDR 1.1.17
+ You are need VDR Version >= 1.1.17
+- DefaultPriority & DefaultLifeTime Settings will be used
+- Timers After (Now + 1 Month) will be imported later
+- Extended File Format Setup Line inserted
+ If you want to have Nature like Main-Folder then set it to "yes"
+ (Default "no")
+
+2002-11-23: Version 0.1.3
+
+- MarginStart & MarginStop will be used
+- EPG Text Data will be saved too
+- File name format will be so one: Nature~Title ,-)
+
+2002-11-20: Version 0.1.2
+
+- Autoupdate
+- OSD Plugin Setup. Command Line will be used as Default Settings.
+
+2002-11-19: Version 0.1.0
+
+- Initial revision.
+
+This ist a Initial Version.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..60d668b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,90 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = tvtv
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual
+
+### The directory environment:
+
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Libraries
+
+LIBS = -lcurl
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR's plugin API (taken from VDR's "config.h"):
+
+APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+# This is required for VDRs older than 1.3.47
+ifeq ($(strip $(APIVERSION)),)
+ APIVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+endif
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o i18n.o config.o update.o md5tools.o channelmap.o
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: libvdr-$(PLUGIN).so
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
+ @cp $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
diff --git a/README b/README
new file mode 100644
index 0000000..916c622
--- /dev/null
+++ b/README
@@ -0,0 +1,88 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Mario Gross <vdr-tvtv-plugin@arcor.de>
+ (based on Version 0.1.6 created by Gerald Berwolf <genka@vdrportal.de>)
+
+Project's homepage: http://home.arcor.de/grossmar
+
+
+See the file COPYING for license information.
+
+
+Requirements:
+
+ - VDR 1.2.0+ (VPS requires VDR 1.3.5+)
+ - libcurl 7.10+
+ - a valid tvtv subscription with "Linux PVR" as registered product
+
+
+Description:
+
+This plugin can be used to create timers from the job list of TVTVs website.
+Unlike the job list on the website, only the most recent 32 timer jobs within
+the upcoming 2 weeks are transfered by TVTV. Programs already on air cannot
+be added to the timer list since they are not sent by TVTV.
+
+
+Setup Menu:
+
+ TVTV Server
+ TVTV Server to download the data from
+
+ TVTV User Name
+ TVTV Password
+ Username and Password as used on the website
+
+ Use HTTP Proxy
+ HTTP Proxy
+ Use HTTP Proxy to connect to TVTV
+
+ Auto Update
+ Updatetime (min)
+ Perform automatic update (default: 30 minutes)
+
+ Show In Main Menu
+ Shows entry in main menu to perform update manually
+
+ Use VPS
+ Add timers using VPS if available (only VDR 1.3.5+)
+
+ Use Nature within File Name
+ Use Station Name within File Name
+ Nature and/or Station Name are added to the filename as separate
+ directory.
+
+ Use TVTV description in info.vdr
+ Starting with VDR 1.3.44, the description of a recording is
+ taken from EPG. This switch decides whether or not the description
+ coming from TVTV will be added as a comment to info.vdr.
+
+ Time shift bugfix
+ Time shift (hrs)
+ Sometimes a timer is wrongly updated due to a bug in the TVTV database.
+ It is shifted 1 hour (2 hours during daylight saving time) ealier. This can
+ be avoided by enabling this fix.
+
+ Reload ChannelMap
+ If channel map is changed it can be reloaded with this command
+
+
+SVDRP Commands:
+
+ The plugin provides SVDRP commands to control the plugin via command line or
+ shell scripts.
+
+ RELOAD - Reloads the channel map config file
+ UPDATE - update timer list from TVTV
+
+
+Installation:
+
+cd /put/your/path/here/VDR/PLUGINS/src
+tar -xzf /put/your/path/here/vdr-tvtv-X.Y.Z.tgz
+ln -s tvtv-X.Y.Z tvtv
+cp tvtv/examples/tvtv_channelmap.conf VDR_Config_Dir/plugins
+cd /put/your/path/here/VDR
+make
+make plugins
+./vdr -P tvtv
diff --git a/channelmap.c b/channelmap.c
new file mode 100644
index 0000000..5b4e5fb
--- /dev/null
+++ b/channelmap.c
@@ -0,0 +1,89 @@
+/*
+ * channelmap.c: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "channelmap.h"
+
+
+cChannelMap::cChannelMap() {
+ chanmap.clear();
+ read_config_file();
+}
+
+cChannelMap::~cChannelMap() {
+ chanmap.clear();
+}
+
+
+void cChannelMap::remove_whitespaces(char *s) // Whitespaces werden entfernt
+{
+ unsigned int i=0;
+ char *dum;
+ if (s != NULL) {
+ dum=(char *)calloc(1,strlen(s)+1);
+ for (i=0;i<strlen(s);i++) if (!isspace(s[i])) strncat(dum,&(s[i]),1);
+ memset(s,0,strlen(s));
+ strcpy(s,dum);
+ free(dum);
+ }
+}
+
+
+int cChannelMap::read_config_file() {
+ ifstream cmfile;
+ string s;
+ size_t p;
+ int tvtvid;
+ int n;
+ char *cfg_fname = NULL;
+
+ asprintf(&cfg_fname, "%s/tvtv_channelmap.conf", cPlugin::ConfigDirectory());
+ cmfile.open(cfg_fname);
+ if (!cmfile) {
+ esyslog("TVTV: Error reading '%s'!", cfg_fname);
+ return -1;
+ }
+ isyslog("TVTV: Loading '%s'", cfg_fname);
+ n=0;
+ while (!cmfile.eof()) {
+ getline(cmfile, s);
+
+ if (!s.empty()) {
+ remove_whitespaces((char *)s.c_str());
+
+ // remove comments
+ p=s.find_first_of("//");
+ if (p != string::npos) s.erase(p);
+
+ // split line
+ p=s.find_first_of("=");
+ if (p != string::npos) {
+ tvtvid=atoi(s.substr(0,p).c_str());
+ chanmap[tvtvid] = (char *)calloc(s.substr(p+1).length()+1, sizeof(char));
+ strcpy(chanmap[tvtvid], s.substr(p+1).c_str());
+ n++;
+ }
+ }
+ }
+ cmfile.close();
+ isyslog("TVTV: %d channel mappings read.", n);
+ return n;
+}
+
+char *cChannelMap::GetChanStr(int tvtvid) {
+ return chanmap[tvtvid];
+}
+
+tChannelID cChannelMap::GetChanID(int tvtvid) {
+ if (chanmap[tvtvid] == NULL) return tChannelID::InvalidID;
+ return tChannelID::FromString(chanmap[tvtvid]);
+}
+
+
+int cChannelMap::ReloadChannelMap() {
+ chanmap.clear();
+ return read_config_file();
+}
diff --git a/channelmap.h b/channelmap.h
new file mode 100644
index 0000000..de40039
--- /dev/null
+++ b/channelmap.h
@@ -0,0 +1,35 @@
+/*
+ * channelmap.h: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _CHANNELMAP__H
+#define _CHANNELMAP__H
+
+#include <map>
+#include <fstream>
+#include <string>
+#include <stdlib.h>
+
+#include <vdr/plugin.h>
+#include <vdr/tools.h>
+
+using namespace std;
+
+
+class cChannelMap {
+private:
+ map<int, char *> chanmap;
+ void remove_whitespaces(char *s);
+ int read_config_file();
+public:
+ cChannelMap();
+ ~cChannelMap();
+ int ReloadChannelMap();
+ char *GetChanStr(int tvtvid);
+ tChannelID GetChanID(int tvtvid);
+};
+
+#endif
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..7edb2bd
--- /dev/null
+++ b/config.c
@@ -0,0 +1,33 @@
+/*
+ * config.c: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "config.h"
+
+cTVTVConfig TVTVConfig;
+
+cTVTVConfig::cTVTVConfig(void) {
+ tvtv_server=0;
+ strn0cpy(username, "", sizeof(username));
+ strn0cpy(password, "", sizeof(password));
+ autoupdate=0;
+ updatetime=30;
+ usegenre=1;
+ usestation=1;
+ show_in_mainmenu=1;
+ useproxy=0;
+ strn0cpy(httpproxy, "127.0.0.1:8000", sizeof(httpproxy));
+// VPS was introduced with VDR 1.3.5
+#if VDRVERSNUM >= 10305
+ usevps=1;
+#endif
+#if VDRVERSNUM >= 10344
+ usetvtvdescr=0;
+#endif
+ tvtv_bugfix=0;
+ tvtv_bugfix_hrs=1;
+}
+
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..d24d744
--- /dev/null
+++ b/config.h
@@ -0,0 +1,45 @@
+/*
+ * config.h: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __TVTV_CONFIG_H
+#define __TVTV_CONFIG_H
+
+#include <vdr/interface.h>
+#include <vdr/plugin.h>
+#include <vdr/tools.h>
+#include <vdr/status.h>
+
+struct cTVTVConfig
+{
+public:
+ cTVTVConfig(void);
+
+ int tvtv_server;
+ char username[30];
+ char password[30];
+ int updatetime;
+ int autoupdate;
+ int usegenre;
+ int usestation;
+ int show_in_mainmenu;
+ int useproxy;
+ char httpproxy[256];
+// VPS was introduced with VDR 1.3.5
+#if VDRVERSNUM >= 10305
+ int usevps;
+#endif
+#if VDRVERSNUM >= 10344
+ int usetvtvdescr;
+#endif
+ int tvtv_bugfix;
+ int tvtv_bugfix_hrs;
+};
+
+extern cTVTVConfig TVTVConfig;
+
+#endif // __TVTV_CONFIG_H
diff --git a/examples/tvtv_channelmap.conf_DVB-S b/examples/tvtv_channelmap.conf_DVB-S
new file mode 100644
index 0000000..827d5fd
--- /dev/null
+++ b/examples/tvtv_channelmap.conf_DVB-S
@@ -0,0 +1,506 @@
+//////////////////////////////////////////////////////////////
+// ChannelMap for TVTV-Plugin
+//////////////////////////////////////////////////////////////
+//
+// TVTV ChannelID = VDR ChannelID (Src-NID-TID-SID)
+//
+//
+// ARD/ZDF
+1 = S19.2E-1-1101-28106 // ARD
+2 = S19.2E-1-1079-28006 // ZDF
+962 = S19.2E-1-1073-28201 // Eins Extra
+955 = S19.2E-1-1073-28202 // Eins Festival
+956 = S19.2E-1-1073-28203 // EinsPlus
+963 = S19.2E-1-1079-28011 // ZDF infokanal
+960 = S19.2E-1-1079-28014 // ZDF dokukanal
+961 = S19.2E-1-1079-28016 // ZDF theaterkanal
+//
+// 3. Programme
+//17 = S19.2E-1-1101-28111 // WDR Köln
+//17 = S19.2E-1-1201-28305 // WDR Aachen
+//17 = S19.2E-1-1201-28306 // WDR Bielefeld
+//17 = S19.2E-1-1201-28307 // WDR Dortmund
+17 = S19.2E-1-1201-28308 // WDR Düsseldorf
+//17 = S19.2E-1-1201-28309 // WDR Essen
+//17 = S19.2E-1-1201-28310 // WDR Münster
+//17 = S19.2E-1-1201-28311 // WDR Siegen
+//17 = S19.2E-1-1201-28312 // WDR Wuppertal
+//19 = S19.2E-1-1073-28224 // NDR FS MV
+19 = S19.2E-1-1073-28225 // NDR FS HH
+//19 = S19.2E-1-1073-28226 // NDR FS NDS
+//19 = S19.2E-1-1073-28227 // NDR FS SH
+18 = S19.2E-1-1101-28107 // BR3
+29 = S19.2E-1-1101-28113 // SWR BW
+30 = S19.2E-1-1073-28231 // SWR RP
+31 = S19.2E-1-1101-28110 // SR
+26 = S19.2E-1-1101-28108 // HR
+33 = S19.2E-1-1073-28204 // MDR
+//33 = S19.2E-1-1073-28228 // MDR SACHSEN
+//33 = S19.2E-1-1073-28229 // MDR S-ANHALT
+//33 = S19.2E-1-1073-28230 // MDR THÜRINGEN
+//35 = S19.2E-1-1073-28205 // rbb Brandenburg
+35 = S19.2E-1-1073-28206 // rbb Berlin
+118 = S19.2E-1-1079-28007 // 3sat
+63 = S19.2E-1-1079-28008 // KiKa
+//
+// Privatsender [de]
+4 = S19.2E-1-1089-12003 // RTL
+5 = S19.2E-133-33-46 // SAT.1
+6 = S19.2E-133-33-898 // ProSieben
+8 = S19.2E-133-33-899 // Kabel eins
+9 = S19.2E-1-1089-12020 // RTL2
+179 = S19.2E-1-1089-12040 // Super RTL
+11 = S19.2E-1-1089-12060 // VOX
+885 = S19.2E-133-5-1793 // Das Vierte
+105 = S19.2E-133-33-51 // TELE-5
+59 = S19.2E-133-33-897 // 9Live
+704 = S19.2E-133-5-776 // GigaTV
+842 = S19.2E-133-5-659 // TV Gusto
+882 = S19.2E-1-1078-28680 // NICK
+716 = S19.2E-1-1078-28677 // Comedy Central
+//
+// Doku
+57 = S19.2E-1-1101-28112 // BR-alpha
+206 = S19.2E-1-1101-28114 // Phoenix
+10 = S19.2E-1-1101-28109 // ARTE
+227 = S19.2E-1-1113-12602 // DMAX
+974 = S19.2E-1-1094-17033 // Terra Nova
+837 = S19.2E-1-1057-61900 // bibel TV
+831 = S19.2E-1-1022-6907 // EWTN
+846 = S19.2E-1-1113-12601 // K-TV
+801 = S19.2E-1-1091-31210 // HSE24
+973 = S19.2E-1-1108-12100 // QVC
+802 = S19.2E-1-1089-12080 // RTL Shop
+//
+// Nachrichten
+7 = S19.2E-1-1089-12090 // n-tv
+953 = S19.2E-133-33-47 // N24
+13 = S19.2E-1-1091-31220 // EuroNews
+901 = S19.2E-1-1026-28707 // Sky News
+126 = S19.2E-1-1068-28522 // CNN
+112 = S19.2E-1-1026-10050 // BBC World Germany
+168 = S19.2E-1-1026-10030 // CNBC
+954 = S19.2E-1-1108-12160 // Bloomberg TV
+//
+// Sport
+12 = S19.2E-133-33-900 // DSF
+107 = S19.2E-1-1091-31200 // Eurosport
+//
+// Premiere
+986 = S19.2E-133-1-8 // Premiere Start
+910 = S19.2E-133-2-10 // Premiere 1
+983 = S19.2E-133-2-11 // Premiere 2
+984 = S19.2E-133-2-43 // Premiere 3
+911 = S19.2E-133-2-9 // Premiere 4
+912 = S19.2E-133-3-20 // Premiere Filmfest
+985 = S19.2E-133-3-41 // Prem. Filmclassics
+935 = S19.2E-133-2-16 // Premiere Serie
+913 = S19.2E-133-4-42 // 13th Street
+934 = S19.2E-133-4-36 // SCI FI
+941 = S19.2E-133-4-515 // MGM
+936 = S19.2E-133-2-23 // Premiere Krimi
+940 = S19.2E-133-3-516 // Premiere Nostalgie
+942 = S19.2E-133-1-24 // Classica
+916 = S19.2E-133-1-22 // Heimatkanal
+979 = S19.2E-133-4-27 // Premiere Win
+931 = S19.2E-133-17-34 // Disney Channel
+976 = S19.2E-133-17-28 // Jetix
+930 = S19.2E-133-17-19 // Junior
+933 = S19.2E-133-17-14 // Discovery
+903 = S19.2E-133-17-13 // Discovery Geschichte
+943 = S19.2E-133-17-12 // Animal Planet
+988 = S19.2E-133-17-15 // Focus Gesundheit
+932 = S19.2E-133-1-168 // hit24
+975 = S19.2E-133-1-518 // Goldstar TV
+944 = S19.2E-133-17-21 // BEATE-UHSE.TV
+952 = S19.2E-133-1-671 // Blue Movie Gay
+//
+// arena
+// 847 = S19.2E-1-1102-13203 // AXN
+902 = S19.2E-1-1102-13204 // Cartoon
+865 = S19.2E-1-1102-13205 // EuroSport 2
+854 = S19.2E-1-1102-13201 // The History Channel
+844 = S19.2E-1-1102-13206 // Kinowelt
+867 = S19.2E-1-1115-13109 // NASN Germany
+// 845 = S19.2E-1-1102-13202 // National Geographic
+149 = S19.2E-1-1068-28520 // TCM
+//
+// Regionalsender
+829 = S19.2E-1-1113-12614 // rheinmaintv
+825 = S19.2E-133-7-768 // RNFplus
+889 = S19.2E-1-1092-10106 // München.TV
+//
+// Oesterreich
+14 = S19.2E-1-1117-13001 // ORF1
+15 = S19.2E-1-1117-13002 // ORF2
+//15 = S19.2E-1-1117-13003 // ORF2 W
+//15 = S19.2E-1-1117-13004 // ORF2 N
+//15 = S19.2E-1-1117-13005 // ORF2 B
+//15 = S19.2E-1-1117-13006 // ORF2 O
+//15 = S19.2E-1-1117-13007 // ORF2 S
+//15 = S19.2E-1-1117-13008 // ORF2 T
+//15 = S19.2E-1-1117-13009 // ORF2 V
+//15 = S19.2E-1-1117-13010 // ORF2 St
+//15 = S19.2E-1-1117-13011 // ORF2 K
+//15 = S19.2E-1-1117-13014 // ORF2E
+958 = S19.2E-1-1117-13012 // ATVplus
+959 = S19.2E-1-1115-13101 // TW1
+836 = S19.2E-1-1091-28800 // RTL AT
+896 = S19.2E-1-1091-28810 // RTL 2 AT
+835 = S19.2E-1-1082-20002 // ProSieben AT
+834 = S19.2E-1-1082-20005 // SAT.1 AT
+866 = S19.2E-1-1082-20004 // Kabel eins AT
+914 = S19.2E-133-7-53 // Premiere Austria
+887 = S19.2E-1-1115-13102 // gotv
+//
+// Easy.TV
+845 = S19.2E-1-1115-13104 // National Geographic
+838 = S19.2E-1-1115-13105 // Silverline
+847 = S19.2E-1-1115-13106 // AXN
+849 = S19.2E-1-1115-13107 // Extreme Sports DE
+839 = S19.2E-1-1115-13108 // Erotik First
+//
+// MTV
+121 = S19.2E-1-1078-28676 // VIVA
+109 = S19.2E-1-1078-28673 // MTV
+2151 = S19.2E-1-1066-28666 // VH1.
+897 = S19.2E-1-1066-28657 // VH1 Classic
+//
+// Italien
+3062 = S13.0E-64511-10100-9472 // Boomerang
+3001 = S13.0E-272-6000-2 // CANALE 5
+3003 = S13.0E-64511-6300-8129 // Cartoon Network
+3007 = S13.0E-64511-5900-11440 // Classica
+3008 = S13.0E-64511-6300-8137 // Cult Network
+3009 = S13.0E-64511-6600-11113 // Discovery Channel
+3065 = S13.0E-64511-5700-11303 // Discovery Scienza
+3010 = S13.0E-64511-6200-10928 // Disney Channel
+3071 = S13.0E-64511-6600-11111 // E! Entertainment Int
+3072 = S13.0E-64511-6700-11511 // ESPN Classic Sports
+3012 = S13.0E-64511-5700-11305 // Eurosport
+3068 = S13.0E-64511-10100-9474 // Fox
+3015 = S13.0E-272-6000-1 // ITALIA 1
+3061 = S13.0E-64511-6300-8131 // Jetix
+3000 = S13.0E-64511-5600-3530 // Jimmy
+3051 = S13.0E-64511-5900-11445 // La 7
+3073 = S13.0E-64511-5800-3631 // Leonardo
+3019 = S13.0E-64511-5900-11426 // MTV
+3067 = S13.0E-64511-10100-9470 // National Geographic
+//3034 = S19.2E-1-1024-9015 // Rai Uno (Astra 1 FTA)
+3034 = S13.0E-318-5200-3401 // Rai Uno (Eutelsat FTA)
+3023 = S13.0E-318-5200-3402 // Rai Due
+3033 = S13.0E-318-5200-3403 // Rai Tre
+//3034 = S13.0E-318-12400-8511 // Rai Uno (Eutelsat encrypted)
+//3023 = S13.0E-318-12400-8512 // Rai Due
+//3033 = S13.0E-318-12400-8513 // Rai Tre
+3070 = S13.0E-318-5400-3301 // RaiNews24
+3026 = S13.0E-64511-8600-11718 // Raisat Cinema
+3058 = S13.0E-318-5400-3305 // Raisat Sport
+3031 = S13.0E-64511-8600-11727 // Raisat YOYO
+3035 = S13.0E-272-6000-3 // RETE 4
+3074 = S13.0E-64511-5600-3578 // Sailing Channel (IT)
+3060 = S13.0E-64511-6600-11101 // Sky Cinema 1
+3045 = S13.0E-64511-8600-11724 // Sky Cinema 16:9
+3048 = S13.0E-64511-6600-11105 // Sky Cinema 3
+3049 = S13.0E-64511-6600-11103 // Sky Cinema Mania
+3050 = S13.0E-64511-6600-11115 // Sky Cinema Max
+3063 = S13.0E-64511-6200-10920 // Sky Sport 1
+3064 = S13.0E-64511-6200-10921 // Sky Sport 2
+3042 = S13.0E-64511-10100-9477 // Studio Universal
+3066 = S13.0E-64511-10100-9471 // The History Channel
+//
+// Luxemburg
+1055 = S19.2E-1-1108-3994 // RTL Tele Letzeburg
+//
+// Niederlande
+1050 = S19.2E-53-1097-2020 // Animal Planet
+1032 = S19.2E-53-1109-5025 // BVN-TV
+1074 = S19.2E-53-1097-2030 // Cartoon Network NL
+1051 = S19.2E-53-1097-2015 // Discovery Channel
+1052 = S19.2E-53-1097-2025 // Eurosport
+1082 = S19.2E-53-1105-4005 // Film1.1
+1083 = S19.2E-53-1105-4007 // Film1.2
+1042 = S19.2E-53-1097-2035 // Hallmark
+1045 = S19.2E-1-1078-28679 // MTV NL
+1053 = S19.2E-53-1105-4015 // National Geographic
+88 = S19.2E-53-1105-4011 // Nederland 1
+89 = S19.2E-53-1105-4012 // Nederland 2
+90 = S19.2E-53-1105-4013 // Nederland 3
+1047 = S19.2E-53-1109-5004 // Net 5
+1048 = S19.2E-53-1109-5010 // Nickelod./Talpa
+1057 = S19.2E-53-1105-4014 // Nickelod/The Box
+1054 = S19.2E-53-1097-2004 // RTL 4
+1061 = S19.2E-53-1097-2005 // RTL 5
+1071 = S19.2E-53-1097-2006 // RTL 7
+1062 = S19.2E-53-1109-5005 // SBS 6
+1065 = S19.2E-53-1097-2030 // TCM
+1056 = S19.2E-53-1109-5015 // TMF
+1070 = S19.2E-53-1109-5020 // Veronica
+//
+// Frankreich
+2507 = S19.2E-1-1080-8801 // France 2
+2508 = S19.2E-1-1080-8802 // France 3
+2555 = S19.2E-1-1110-9301 // France 4
+2535 = S19.2E-1-1090-8501 // France 5 Sat
+2546 = S19.2E-1-1024-9012 // TV5 Monde
+2559 = S19.2E-1-1112-12240 // TV5 Europe
+2500 = S19.2E-1-1024-9019 // Arte
+2501 = S19.2E-1-1072-8201 // Canal+
+2522 = S19.2E-1-1072-8202 // Canal+ Décalé
+2523 = S19.2E-1-1072-8203 // Canal+ Cinema
+2524 = S19.2E-1-1072-8208 // Canal+ Sport
+2584 = S19.2E-1-1072-8213 // Canal+ Hi-Tech
+2576 = S19.2E-1-1074-8351 // Cinécinéma Auteur
+2526 = S19.2E-1-1072-8206 // Cinécinéma Premier
+2568 = S19.2E-1-1070-8002 // Cinécinéma Emotion
+2569 = S19.2E-1-1070-8003 // Cinécinéma Frisson
+2558 = S19.2E-1-1100-8709 // Cinécinéma Classic
+2575 = S19.2E-1-1100-8711 // Cinécinéma Famiz
+2567 = S19.2E-1-1068-28521 // Cartoon Network
+2517 = S19.2E-1-1068-28525 // TCM
+2591 = S19.2E-1-1070-8001 // Seasons
+2564 = S19.2E-1-1070-8004 // EuroNews
+2512 = S19.2E-1-1070-8005 // Paris Première
+2503 = S19.2E-1-1070-8006 // Jimmy
+2540 = S19.2E-1-1070-8007 // Mezzo
+2543 = S19.2E-1-1070-8009 // Sport+
+2606 = S19.2E-1-1070-8010 // I>Télé
+2533 = S19.2E-1-1070-8011 // Histoire
+2531 = S19.2E-1-1072-8207 // Disney Channel
+2511 = S19.2E-1-1074-8352 // MCM
+2587 = S19.2E-1-1074-8354 // MCM Pop
+2595 = S19.2E-1-1074-8365 // MCM Top
+2538 = S19.2E-1-1066-28652 // MTV France
+2537 = S19.2E-1-1074-8357 // W 9
+2607 = S19.2E-1-1074-8358 // Europe 2 TV
+2506 = S19.2E-1-1076-8151 // Eurosport
+2519 = S19.2E-1-1076-8152 // TMC
+2513 = S19.2E-1-1076-8153 // Planète
+2548 = S19.2E-1-1076-8155 // Voyage
+2534 = S19.2E-1-1076-8156 // LCI
+2502 = S19.2E-1-1076-8157 // Canal J
+2585 = S19.2E-1-1076-8160 // Cuisine.TV
+2592 = S19.2E-1-1076-8162 // TiJi
+2514 = S19.2E-1-1080-8805 // RTL 9
+2530 = S19.2E-1-1100-8702 // Comédie !
+2520 = S19.2E-1-1100-8703 // 13ème Rue
+2532 = S19.2E-1-1100-8705 // Jetix
+2589 = S19.2E-1-1100-8708 // National Geographic
+2597 = S19.2E-1-1100-8710 // NRJ 12
+2510 = S19.2E-1-1108-12122 // M6
+2609 = S19.2E-1-1114-8903 // Gulli
+2516 = S19.2E-1-1114-8905 // Téva
+2588 = S19.2E-1-1114-8910 // Motors TV
+2563 = S19.2E-1-1094-17022 // AB Animaux
+2521 = S19.2E-1-1094-17021 // AB 1
+2525 = S19.2E-1-1094-17024 // XXL
+//
+// Spanien
+3268 = S19.2E-1-1032-30202 // Andalucía TV
+3270 = S19.2E-1-1056-29956 // AXN
+3271 = S19.2E-1-1046-30510 // Calle 13
+3254 = S19.2E-1-1038-30400 // Canal+
+3290 = S19.2E-1-1034-30651 // Canal Clásico
+3301 = S19.2E-1-1056-29953 // Canal Cocina
+3298 = S19.2E-1-1034-30658 // Canal de Historia
+3275 = S19.2E-1-1032-30206 // Canal Estrellas
+3278 = S19.2E-1-1034-30657 // Canal Hollywood
+3307 = S19.2E-1-1034-30663 // Cuatro
+3295 = S19.2E-1-1038-30406 // Discovery Channel
+3277 = S19.2E-1-1038-30403 // Disney Channel
+3297 = S19.2E-1-1038-30401 // Docu TVE
+3284 = S19.2E-1-1034-30680 // Eurosport
+3272 = S19.2E-1-1056-29955 // Fox TV
+3293 = S19.2E-1-1008-29801 // Jetix
+3302 = S19.2E-1-1032-30204 // La Otra
+3308 = S19.2E-1-1054-30356 // La Sexta
+3286 = S19.2E-1-1066-28651 // MTV España
+3299 = S19.2E-1-1060-30605 // National Geographic
+3294 = S19.2E-1-1066-28660 // Nickelodeon
+3273 = S19.2E-1-1046-30506 // Paramount Comedy
+3285 = S19.2E-1-1038-30404 // Sportmania
+3281 = S19.2E-1-1068-28526 // TCM
+3276 = S19.2E-1-1034-30656 // Telenovelas
+3303 = S19.2E-1-1032-30201 // TVC Internacional
+3250 = S19.2E-1-1054-30355 // TVE 1
+3274 = S19.2E-1-1034-30654 // TVE 50 Años
+3251 = S19.2E-1-1046-30509 // TVE La 2
+3262 = S19.2E-1-1032-30222 // TV Galicia
+//
+// Portugal
+2562 = S19.2E-1-1024-9017 // RTPi
+//
+// Schweiz
+24 = S13.0E-318-8500-901 // SF 1
+900 = S13.0E-318-8500-907 // SF zwei
+820 = S13.0E-318-8500-911 // SF info
+2544 = S13.0E-318-1700-14002 // TSR 1
+2545 = S13.0E-318-1700-14008 // TSR 2
+3043 = S13.0E-318-1700-14003 // TSI 1
+3053 = S13.0E-318-1700-14009 // TSI 2
+2207 = S19.2E-1-1114-8909 // BBC Prime (CET), ch
+855 = S19.2E-1-1082-20001 // ProSieben Schweiz
+832 = S19.2E-133-7-48 // SAT.1 Schweiz
+//4 = S13.0E-318-12700-8003 // RTL
+//9 = S13.0E-318-15500-609 // RTL2
+//179 = S13.0E-318-15500-610 // Super RTL
+//11 = S13.0E-318-12700-8002 // VOX
+//
+// Türkei
+40 = S19.2E-1-1026-10003 // TRT International
+//
+// UK
+2195 = S28.2E-2-2051-9010 // ABC1
+2006 = S28.2E-2-2316-53595 // Adult Channel
+2130 = S28.2E-2-2025-5703 // Adventure One
+2169 = S28.2E-2-2009-6202 // Animal Planet dig.
+2008 = S28.2E-2-2045-6301 // BBC 1 London
+//2008 = S28.2E-2-2046-10301 // BBC 1 W Mids
+//2008 = S28.2E-2-2046-10302 // BBC 1 N West
+//2008 = S28.2E-2-2046-10303 // BBC 1 Yrks&Lin
+//2008 = S28.2E-2-2046-10304 // BBC 1 Yorks
+//2008 = S28.2E-2-2046-10305 // BBC 1 E Mids
+//2008 = S28.2E-2-2046-10306 // BBC 1 East (E)
+//2008 = S28.2E-2-2048-10351 // BBC 1 West
+//2008 = S28.2E-2-2048-10352 // BBC 1 S East
+//2008 = S28.2E-2-2048-10353 // BBC 1 South
+//2008 = S28.2E-2-2048-10354 // BBC 1 S West
+//2008 = S28.2E-2-2048-10355 // BBC 1 NE & C
+//2008 = S28.2E-2-2048-10356 // BBC 1 Oxford
+//2008 = S28.2E-2-2050-6903 // BBC 1 East (W)
+//2008 = S28.2E-2-2050-6904 // BBC 1 CI
+2002 = S28.2E-2-2047-6411 // BBC 1 Wales
+2001 = S28.2E-2-2047-6421 // BBC 1 Scotland
+2000 = S28.2E-2-2045-6331 // BBC 1 NI
+2009 = S28.2E-2-2045-6302 // BBC 2 England
+2003 = S28.2E-2-2047-6432 // BBC2 NI
+2005 = S28.2E-2-2047-6412 // BBC 2W
+2004 = S28.2E-2-2047-6422 // BBC 2 Scotland
+2020 = S28.2E-2-2045-6319 // BBC Three
+2070 = S28.2E-2-2045-6316 // BBC Four
+2042 = S28.2E-2-2013-6705 // BBC Parliament
+2037 = S28.2E-2-2045-6304 // BBC News 24
+2191 = S28.2E-2-2017-7250 // Bid TV
+2011 = S28.2E-2-2306-52550 // Bloomberg TV
+2162 = S28.2E-2-2018-7102 // Boomerang
+2013 = S28.2E-2-2002-6004 // Bravo (Digital)
+2027 = S28.2E-2-2025-5710 // British Eurosport
+2060 = S28.2E-2-2018-7100 // Cartoon Network
+2182 = S28.2E-2-2045-6317 // CBBC
+2183 = S28.2E-2-2045-6318 // CBeebies
+2170 = S28.2E-2-2002-6002 // Challenge dig.
+2015 = S28.2E-2-2024-9201 // Channel 4
+2090 = S28.2E-2-2044-10071 // CITV
+2024 = S28.2E-2-2311-52111 // CNBC Europe
+2025 = S28.2E-2-2018-7140 // CNN
+2188 = S28.2E-2-2027-5802 // Community Channel
+2081 = S28.2E-2-2009-6207 // Disc. Civilisations
+2079 = S28.2E-2-2009-6201 // Discovery Channel
+2083 = S28.2E-2-2009-6209 // Discovery Kids
+2084 = S28.2E-2-2009-6206 // Discovery Science
+2086 = S28.2E-2-2009-6219 // Discovery Wings
+2082 = S28.2E-2-2009-6203 // Discov. Real Time
+2085 = S28.2E-2-2009-6205 // Disc. Travel Living
+2026 = S28.2E-2-2051-9001 // Disney
+2228 = S28.2E-2-2051-9002 // Disney Cinemagic
+2209 = S28.2E-2-2025-5770 // E!
+2160 = S28.2E-2-2042-8305 // E4
+2214 = S28.2E-2-2042-8300 // E4 +1
+2087 = S28.2E-2-2602-54104 // Euronews
+2164 = S28.2E-2-2403-4164 // Fashion TV
+2210 = S28.2E-2-2409-50850 // Fashion TV
+2028 = S28.2E-2-2042-8335 // Film4
+2219 = S28.2E-2-2042-8330 // Film4 +1
+2016 = S28.2E-2-2031-9342 // Five
+2190 = S28.2E-2-2303-52231 // Ftn
+2229 = S28.2E-2-2305-52408 // Golf Channel UK
+2091 = S28.2E-2-2023-5010 // Hallmark UK
+2173 = S28.2E-2-2408-4653 // History Channel dig.
+2192 = S28.2E-2-2017-7200 // Ideal World
+2017 = S28.2E-2-2044-10060 // ITV1 London
+2018 = S28.2E-2-2044-10100 // ITV1 Central
+2067 = S28.2E-2-2049-10040 // ITV1 West Country
+2036 = S28.2E-2-2053-10140 // ITV1 Meridian
+2068 = S28.2E-2-2053-10160 // ITV1 Yorkshire
+2007 = S28.2E-2-2041-10180 // ITV1 Anglia
+2061 = S28.2E-2-2041-10190 // ITV1 Tyne Tees
+2034 = S28.2E-2-2049-10030 // ITV1 West
+2032 = S28.2E-2-2044-10080 // ITV1 Granada
+2012 = S28.2E-2-2049-10050 // ITV1 Border
+2033 = S28.2E-2-2049-10020 // ITV1 Wales
+2035 = S28.2E-2-2044-10070 // ITV2
+2194 = S28.2E-2-2054-10260 // ITV3
+2215 = S28.2E-2-2044-10072 // ITV4
+2030 = S28.2E-2-2031-9350 // Jetix
+2095 = S28.2E-2-2002-6001 // LIVINGtv
+2171 = S28.2E-2-2037-7560 // Men & Motors
+2211 = S28.2E-2-2042-8340 // More4
+2158 = S19.2E-1-1066-28665 // MTV Base.
+// 2158 = S28.2E-2-2010-7007 // MTV Base
+2159 = S19.2E-1-1066-28669 // MTV2.
+// 2159 = S28.2E-2-2010-7003 // MTV 2
+2225 = S28.2E-2-2010-7014 // MTV Dance
+2156 = S19.2E-1-1066-28664 // MTV Hits.
+// 2156 = S28.2E-2-2010-7006 // MTV Hits
+2096 = S28.2E-2-2010-7001 // MTV UK
+2097 = S28.2E-2-2025-5701 // MUTV
+2189 = S28.2E-2-2409-50839 // NASN
+2174 = S28.2E-2-2030-4607 // Nat. Geographic dig.
+2216 = S28.2E-2-2030-4608 // Nat. Geogrpahic +1
+2099 = S28.2E-2-2029-5602 // Nickelodeon (dig.)
+2100 = S28.2E-2-2029-5605 // Nick Junior
+2041 = S28.2E-2-2025-5904 // Paramount Comedy
+2103 = S28.2E-2-2305-52402 // Performance
+2104 = S28.2E-2-2403-4131 // Playboy TV
+2222 = S28.2E-2-2051-9004 // Playhouse Disney
+2198 = S28.2E-2-2017-7252 // price-drop.tv
+2044 = S28.2E-2-2017-7230 // QVC
+2221 = S28.2E-2-2409-50836 // Racing UK
+2193 = S28.2E-2-2612-8652 // Reality TV UK
+2045 = S28.2E-2-2043-9601 // RT One
+2046 = S28.2E-2-2043-9602 // RT Two
+2073 = S28.2E-2-2022-7301 // S4C digidol
+2107 = S28.2E-2-2023-4905 // Sci-Fi Satelite
+2112 = S28.2E-2-2027-4705 // Sky One, uk
+2217 = S28.2E-2-2027-5103 // Sky Three, uk
+2054 = S28.2E-2-2007-4303 // Sky Movies 1, uk
+2051 = S28.2E-2-2007-4302 // Sky Movies 2, uk
+2114 = S28.2E-2-2007-4403 // Sky Movies 3, uk
+2111 = S28.2E-2-2007-4304 // Sky Movies 4, uk
+2179 = S28.2E-2-2007-4305 // Sky Movies 5, uk
+2176 = S28.2E-2-2007-4306 // Sky Movies 6, uk
+2180 = S28.2E-2-2030-4603 // Sky Movies 7, uk
+2178 = S28.2E-2-2007-5502 // Sky Movies 8, uk
+2177 = S28.2E-2-2030-4602 // Sky Movies 9, uk
+2226 = S28.2E-2-2030-4604 // Sky Movies 10, uk
+2056 = S28.2E-2-2023-4914 // Sky Sports 1, uk
+2057 = S28.2E-2-2052-9314 // Sky Sports 2, uk
+2058 = S28.2E-2-2021-4219 // Sky Sports 3, uk
+2131 = S28.2E-2-2023-4907 // Sky Sports News, uk
+2050 = S28.2E-2-2408-4651 // Sky Cinema, uk
+2175 = S28.2E-2-2408-4652 // Sky Movies Cinema 2, uk
+2116 = S28.2E-2-2052-5005 // Sky Travel, uk
+2053 = S28.2E-2-2026-4706 // Sky News, uk
+2117 = S28.2E-2-2014-7420 // Sony Entertainment
+2123 = S28.2E-2-2025-5714 // TCM
+2218 = S28.2E-2-2037-7500 // Teachers' TV
+2059 = S28.2E-2-2043-9603 // TG4
+2119 = S28.2E-2-2027-5206 // The Box
+2200 = S28.2E-2-2019-9349 // The Hits
+2199 = S28.2E-2-2010-7009 // TMF (UK)
+2223 = S28.2E-2-2018-7103 // Toonami
+2121 = S28.2E-2-2002-6003 // Trouble Digital
+2062 = S28.2E-2-2043-9605 // TV3
+2163 = S28.2E-2-2052-9313 // TV Travel Shop
+2186 = S28.2E-2-2015-7635 // UKTV Bright Ideas
+2127 = S28.2E-2-2006-6502 // UKTV Documentary
+2125 = S28.2E-2-2006-6521 // UKTV Drama
+2197 = S28.2E-2-2006-6508 // UKTV Food
+2126 = S28.2E-2-2006-6506 // UKTV G2
+2063 = S28.2E-2-2006-6504 // UKTV Gold
+2184 = S28.2E-2-2006-6505 // UKTV History
+2187 = S28.2E-2-2015-7611 // UKTV People
+2128 = S28.2E-2-2006-6501 // UKTV Style
+2066 = S28.2E-2-2010-7002 // VH1
+2157 = S28.2E-2-2010-7008 // VH1 Classic
+2129 = S28.2E-2-2014-7460 // Zee TV
diff --git a/examples/tvtv_channelmap.conf_DVB_Mch b/examples/tvtv_channelmap.conf_DVB_Mch
new file mode 100644
index 0000000..f63252c
--- /dev/null
+++ b/examples/tvtv_channelmap.conf_DVB_Mch
@@ -0,0 +1,62 @@
+//////////////////////////////////////////////////////////////
+// ChannelMap for TVTV-Plugin
+//////////////////////////////////////////////////////////////
+//
+// TVTV ChannelID = VDR ChannelID (Src-NID-TID-SID)
+//
+//// Kabelfernsehen Muenchen
+//
+// 394 MHz
+2 = C-1-1079-28006 // ZDF
+118 = C-1-1079-28007 // 3sat
+963 = C-1-1079-28011 // ZDF infokanal
+960 = C-1-1079-28014 // ZDF dokukanal
+961 = C-1-1079-28016 // ZDF theaterkanal
+63 = C-1-1079-28008 // KiKa
+//
+// 410 MHz
+1 = C-1-1101-28106 // ARD
+10 = C-1-1101-28109 // ARTE
+206 = C-1-1101-28114 // Phoenix
+17 = C-1-1101-28111 // WDR Köln
+26 = C-1-1101-28108 // HR
+57 = C-1-1101-28112 // BR-alpha
+18 = C-1-1101-28107 // BR3
+29 = C-1-1101-28113 // SWR BW
+//
+// 426 MHz
+962 = C-1-1073-28201 // Eins Extra
+955 = C-1-1073-28202 // Eins Festival
+956 = C-1-1073-28203 // EinsPlus
+//35 = C-1-1073-28205 // rbb Brandenburg
+35 = C-1-1073-28206 // rbb Berlin
+30 = C-1-1073-28231 // SWR RP
+//19 = C-1-1073-28224 // NDR FS MV
+19 = C-1-1073-28225 // NDR FS HH
+//19 = C-1-1073-28226 // NDR FS NDS
+//19 = C-1-1073-28227 // NDR FS SH
+33 = C-1-1073-28204 // MDR
+//33 = C-1-1073-28228 // MDR SACHSEN
+//33 = C-1-1073-28229 // MDR S-ANHALT
+//33 = C-1-1073-28230 // MDR THÜRINGEN
+//
+//
+//// DVB-T Muenchen
+//
+// Kanal 34
+4 = T-8468-12801-16405 // RTL
+9 = T-12338-0-16406 // RTL2
+11 = T-12338-0-16418 // VOX
+179 = T-12338-0-16411 // Super RTL
+//
+// Kanal 48
+5 = T-8468-13057-16408 // SAT.1
+6 = T-8468-13057-16403 // ProSieben
+8 = T-8468-13057-16394 // Kabel eins
+953 = T-8468-13313-16420 // N24
+//
+// Kanal 66
+105 = T-8468-13313-16413 // TELE-5
+107 = T-8468-13313-16420 // Eurosport
+40 = T-8468-13313-16393 // HSE24
+889 = T-8468-13313-1856 // München.TV
diff --git a/i18n.c b/i18n.c
new file mode 100644
index 0000000..5a2d042
--- /dev/null
+++ b/i18n.c
@@ -0,0 +1,590 @@
+/*
+ * i18n.c: Internationalization
+ *
+ */
+
+#include "i18n.h"
+
+const tI18nPhrase Phrases[] = {
+ { "TVTV", // English
+ "TVTV", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "TVTV Timers update", // English
+ "TVTV Timers update", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "TVTV User Name", // English
+ "TVTV Benutzername", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "TVTV Password", // English
+ "TVTV Passwort", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Auto Update", // English
+ "Automatisches Update", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Updatetime (min)", // English
+ "Updatezeit (min)", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Use Nature within File Name", // English
+ "Genre im Dateinamen verwenden", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Use Station Name within File Name", // English
+ "Sendernamen im Dateinamen verwenden", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "TVTV update will be started", // English
+ "TVTV update wird ausgeführt", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Show In Main Menu", // English
+ "Im Hauptmenü anzeigen", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Use VPS", // English
+ "VPS benutzen", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "TVTV Server", // English
+ "TVTV Server", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Use HTTP Proxy", // English
+ "HTTP Proxy benutzen", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { " HTTP Proxy", // English
+ " HTTP Proxy", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Use TVTV description in info.vdr", // English
+ "Beschreibung in info.vdr übernehmen", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Time shift bugfix", // English
+ "\"Zeitproblem\"-Fix aktivieren", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { " Time shift (hrs)", // English
+ " Zeitverschiebung (Std)", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Reload ChannelMap", // English
+ "ChannelMap neu laden", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "Really reload ChannelMap?", // English
+ "ChannelMap wirklich neu laden?", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { "TVTV Download Error", // English
+ "TVTV: Fehler beim Download", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Català
+#if VDRVERSNUM > 10302
+ "", // Russian
+#if VDRVERSNUM > 10307
+ "", // Hrvatski
+#if VDRVERSNUM > 10313
+ "", // Eesti
+#if VDRVERSNUM > 10316
+ "", // Dansk
+#endif
+#endif
+#endif
+#endif
+ },
+ { NULL }
+ };
diff --git a/i18n.h b/i18n.h
new file mode 100644
index 0000000..577628a
--- /dev/null
+++ b/i18n.h
@@ -0,0 +1,16 @@
+/*
+ * i18n.h: Internationalization
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: i18n.h 1.2 2002/05/11 14:48:16 kls Exp $
+ */
+
+#ifndef _I18N__H
+#define _I18N__H
+
+#include <vdr/i18n.h>
+
+extern const tI18nPhrase Phrases[];
+
+#endif //_I18N__H
diff --git a/md5tools.c b/md5tools.c
new file mode 100644
index 0000000..9021537
--- /dev/null
+++ b/md5tools.c
@@ -0,0 +1,207 @@
+/*
+ * md5tools.c: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+
+#include "md5tools.h"
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+*/
+void MD5Transform(uint32_t buf[4], uint32_t in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+*/
+void MD5Init(MD5_CTX *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+*/
+void MD5Update(MD5_CTX *ctx, unsigned char *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+*/
+void MD5Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+ ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
diff --git a/md5tools.h b/md5tools.h
new file mode 100644
index 0000000..a52bf25
--- /dev/null
+++ b/md5tools.h
@@ -0,0 +1,47 @@
+/*
+ * md5tools.h: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef __MD5TOOLS_H
+#define __MD5TOOLS_H
+
+#include <stdint.h>
+#include <string.h>
+
+
+/* ---- MD5 ---- */
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+// MD5
+
+typedef unsigned long uint32;
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+};
+
+typedef struct MD5Context MD5_CTX;
+#define byteReverse(buf, len) /* Nothing */
+
+
+void MD5Init(MD5_CTX *ctx);
+void MD5Update(MD5_CTX *ctx, unsigned char *buf, unsigned len);
+void MD5Final(unsigned char digest[16], MD5_CTX *ctx);
+
+
+#endif //__MD5TOOLS_H
diff --git a/tvtv.c b/tvtv.c
new file mode 100644
index 0000000..3aaa6a3
--- /dev/null
+++ b/tvtv.c
@@ -0,0 +1,308 @@
+/*
+ * tvtv.c: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "i18n.h"
+#include "config.h"
+#include "update.h"
+
+
+static const char *VERSION = "0.3.3";
+static const char *DESCRIPTION = "TVTV Timers update";
+static const char *MAINMENUENTRY = "TVTV";
+
+cUpdate *oUpdate;
+
+class cPluginTVTV : public cPlugin {
+private:
+#if VDRVERSNUM >= 10307
+ cSkinDisplayMessage *displayMessage;
+#endif
+public:
+ cPluginTVTV(void);
+ virtual ~cPluginTVTV();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return tr(DESCRIPTION); }
+ virtual const char *CommandLineHelp(void);
+#if VDRVERSNUM >= 10331
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *Cmd, const char *Option, int &ReplyCode);
+#endif
+ virtual bool Start(void);
+ virtual const char *MainMenuEntry(void) { return (TVTVConfig.show_in_mainmenu ? MAINMENUENTRY : NULL); }
+ virtual cOsdMenu *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+
+ void DisplayMessage(const char *s);
+ };
+
+// --- cMenuSetupTVTV -------------------------------------------------------
+
+class cMenuSetupTVTV : public cMenuSetupPage {
+private:
+ cTVTVConfig data;
+ virtual void Setup(void);
+protected:
+ virtual eOSState ProcessKey(eKeys Key);
+ virtual void Store(void);
+public:
+ cMenuSetupTVTV(void);
+ };
+
+cMenuSetupTVTV::cMenuSetupTVTV(void) {
+ data = TVTVConfig;
+ Setup();
+}
+
+void cMenuSetupTVTV::Setup(void) {
+ int current = Current();
+
+ Clear();
+
+ Add(new cMenuEditStraItem( tr("TVTV Server"), &data.tvtv_server, TVTVSRV_CNT, TVTV_SERVERS));
+ Add(new cMenuEditStrItem( tr("TVTV User Name"), data.username, sizeof(data.username), tr(FileNameChars)));
+ Add(new cMenuEditStrItem( tr("TVTV Password"), data.password, sizeof(data.password), tr(FileNameChars)));
+ Add(new cMenuEditBoolItem( tr("Use HTTP Proxy"), &data.useproxy));
+ if (data.useproxy)
+ Add(new cMenuEditStrItem( tr(" HTTP Proxy"), data.httpproxy, sizeof(data.httpproxy), tr(FileNameChars)));
+ Add(new cMenuEditBoolItem( tr("Auto Update"), &data.autoupdate));
+ Add(new cMenuEditIntItem( tr("Updatetime (min)"), &data.updatetime));
+ Add(new cMenuEditBoolItem( tr("Show In Main Menu"), &data.show_in_mainmenu));
+
+// VPS was introduced with VDR 1.3.5
+#if VDRVERSNUM >= 10305
+ Add(new cMenuEditBoolItem( tr("Use VPS"), &data.usevps));
+#endif
+
+ Add(new cMenuEditBoolItem( tr("Use Nature within File Name"), &data.usegenre));
+ Add(new cMenuEditBoolItem( tr("Use Station Name within File Name"), &data.usestation));
+
+// Starting with VDR 1.3.44, the description of a recording is
+// taken from EPG. This switch decides whether or not the description
+// coming from TVTV will be added as a comment to info.vdr.
+#if VDRVERSNUM >= 10344
+ Add(new cMenuEditBoolItem( tr("Use TVTV description in info.vdr"), &data.usetvtvdescr));
+#endif
+
+ Add(new cMenuEditBoolItem( tr("Time shift bugfix"), &data.tvtv_bugfix));
+ if (data.tvtv_bugfix)
+ Add(new cMenuEditIntItem( tr(" Time shift (hrs)"), &data.tvtv_bugfix_hrs, 1, 4));
+
+ Add(new cOsdItem(tr("Reload ChannelMap"),osUser9));
+
+ SetCurrent(Get(current));
+ Display();
+}
+
+
+eOSState cMenuSetupTVTV::ProcessKey(eKeys Key) {
+ int olduseproxy = data.useproxy;
+ int oldtvtvbugfix = data.tvtv_bugfix;
+ eOSState state = cMenuSetupPage::ProcessKey(Key);
+
+ switch(state) {
+ case osUser9:
+ state=osContinue;
+ if(Interface->Confirm(tr("Really reload ChannelMap?"))) {
+ if (oUpdate) oUpdate->ReloadChannelMap();
+ state=osEnd;
+ }
+ break;
+
+ case osContinue:
+ if(NORMALKEY(Key)==kUp || NORMALKEY(Key)==kDown) {
+ cOsdItem *item=Get(Current());
+ if(item) item->ProcessKey(kNone);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (Key != kNone && ((data.useproxy != olduseproxy) || (data.tvtv_bugfix != oldtvtvbugfix))) Setup();
+ return state;
+}
+
+
+void cMenuSetupTVTV::Store(void)
+{
+ dsyslog("TVTV: cMenuSetupTVTV::Store()");
+ TVTVConfig = data;
+ SetupStore("TVTVServer", TVTVConfig.tvtv_server);
+ SetupStore("UserName", TVTVConfig.username);
+ SetupStore("Password", TVTVConfig.password);
+ SetupStore("UseProxy", TVTVConfig.useproxy);
+ SetupStore("HTTPProxy", TVTVConfig.httpproxy);
+ SetupStore("AutoUpdate", TVTVConfig.autoupdate);
+ SetupStore("UpdateTime", TVTVConfig.updatetime);
+ SetupStore("UseGenreInFName", TVTVConfig.usegenre);
+ SetupStore("UseStationInFName", TVTVConfig.usestation);
+ SetupStore("ShowInMainMenu", TVTVConfig.show_in_mainmenu);
+
+// VPS was introduced with VDR 1.3.5
+#if VDRVERSNUM >= 10305
+ SetupStore("UseVps", TVTVConfig.usevps);
+#endif
+
+#if VDRVERSNUM >= 10344
+ SetupStore("UseTVTVDescr", TVTVConfig.usetvtvdescr);
+#endif
+
+ SetupStore("TVTVBugfix", TVTVConfig.tvtv_bugfix);
+ SetupStore("TVTVBugfixHrs", TVTVConfig.tvtv_bugfix_hrs);
+
+ if (TVTVConfig.autoupdate)
+ if (oUpdate)
+ oUpdate->StartUpdate();
+}
+
+// --- cPluginTVTV ----------------------------------------------------------
+
+cPluginTVTV::cPluginTVTV(void)
+{
+ // Initialize any member variables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+ oUpdate = NULL;
+}
+
+cPluginTVTV::~cPluginTVTV()
+{
+ // Clean up after yourself!
+ if (oUpdate)
+ delete oUpdate;
+}
+
+
+void cPluginTVTV::DisplayMessage(const char *s) {
+
+ isyslog("TVTV: %s", s);
+
+#if VDRVERSNUM >= 10307
+ if (!Skins.Current()) return;
+ if (!cSkinDisplay::Current() && !displayMessage)
+ displayMessage = Skins.Current()->DisplayMessage();
+ cSkinDisplay::Current()->SetMessage(mtWarning, s);
+ cSkinDisplay::Current()->Flush();
+ cStatus::MsgOsdStatusMessage(s);
+ Interface->Wait(0);
+ if (displayMessage) {
+ delete displayMessage;
+ displayMessage = NULL;
+ cStatus::MsgOsdClear();
+ }
+ else {
+ cSkinDisplay::Current()->SetMessage(mtWarning, NULL);
+ cStatus::MsgOsdStatusMessage(NULL);
+ }
+#else
+ Interface->Open(Setup.OSDwidth, -1);
+ Interface->Status(s, clrBlack, clrYellow);
+
+ // Wait some seconds
+ time_t timeout = time(NULL) + Setup.OSDMessageTime;
+ for (;;) { if (time(NULL) > timeout) break; }
+
+ Interface->Status(NULL);
+ Interface->Close();
+#endif
+}
+
+
+const char *cPluginTVTV::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return NULL;
+}
+
+#if VDRVERSNUM >= 10331
+const char **cPluginTVTV::SVDRPHelpPages(void) {
+ static const char *HelpPages[] = {
+ "RELOAD\n"
+ " Reloads the channel map configuration file.",
+ "UPDATE\n"
+ " Update TVTV timer list.",
+ NULL
+ };
+ return HelpPages;
+}
+
+cString cPluginTVTV::SVDRPCommand(const char *Cmd, const char *Option, int &ReplyCode) {
+ if (strcasecmp(Cmd, "RELOAD") == 0) {
+ oUpdate->ReloadChannelMap();
+ return "Channel map reloaded.";
+ }
+ else if (strcasecmp(Cmd, "UPDATE") == 0) {
+ oUpdate->StartUpdate();
+ return "TVTV update done.";
+ }
+ return NULL;
+}
+#endif
+
+bool cPluginTVTV::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ RegisterI18n(Phrases);
+
+ oUpdate = new cUpdate();
+
+ if (oUpdate && TVTVConfig.autoupdate)
+ oUpdate->StartUpdate();
+
+ return (oUpdate != NULL);
+}
+
+cOsdMenu *cPluginTVTV::MainMenuAction(void)
+{
+ // Perform the action when selected from the main VDR menu.
+ if (oUpdate) oUpdate->StartUpdate();
+ DisplayMessage(tr("TVTV update will be started"));
+ return NULL;
+}
+
+
+cMenuSetupPage *cPluginTVTV::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return new cMenuSetupTVTV;
+}
+
+bool cPluginTVTV::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+
+ if (!strcasecmp(Name, "TVTVServer")) TVTVConfig.tvtv_server = atoi(Value);
+ else if (!strcasecmp(Name, "UserName")) strcpy(TVTVConfig.username, Value);
+ else if (!strcasecmp(Name, "Password")) strcpy(TVTVConfig.password, Value);
+ else if (!strcasecmp(Name, "UseProxy")) TVTVConfig.useproxy = atoi(Value);
+ else if (!strcasecmp(Name, "HTTPProxy")) strcpy(TVTVConfig.httpproxy, Value);
+ else if (!strcasecmp(Name, "AutoUpdate")) TVTVConfig.autoupdate = atoi(Value);
+ else if (!strcasecmp(Name, "UpdateTime")) TVTVConfig.updatetime = atoi(Value);
+ else if (!strcasecmp(Name, "UseGenreInFName")) TVTVConfig.usegenre = atoi(Value);
+ else if (!strcasecmp(Name, "UseStationInFName")) TVTVConfig.usestation = atoi(Value);
+ else if (!strcasecmp(Name, "ShowInMainMenu")) TVTVConfig.show_in_mainmenu = atoi(Value);
+// VPS was introduced with VDR 1.3.5
+#if VDRVERSNUM >= 10305
+ else if (!strcasecmp(Name, "UseVps")) TVTVConfig.usevps = atoi(Value);
+#endif
+
+#if VDRVERSNUM >= 10344
+ else if (!strcasecmp(Name, "UseTVTVDescr")) TVTVConfig.usetvtvdescr = atoi(Value);
+#endif
+
+ else if (!strcasecmp(Name, "TVTVBugfix")) TVTVConfig.tvtv_bugfix = atoi(Value);
+ else if (!strcasecmp(Name, "TVTVBugfixHrs")) TVTVConfig.tvtv_bugfix_hrs = atoi(Value);
+
+ else
+ return false;
+ return true;
+}
+
+VDRPLUGINCREATOR(cPluginTVTV); // Don't touch this!
+
diff --git a/update.c b/update.c
new file mode 100644
index 0000000..3019bb6
--- /dev/null
+++ b/update.c
@@ -0,0 +1,703 @@
+/*
+ * update.c: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "update.h"
+
+
+cUpdate::cUpdate()
+{
+ active = false;
+ data.memory = NULL;
+ data.size = 0;
+ chanmap = new cChannelMap();
+}
+
+cUpdate::~cUpdate()
+{
+ if (active) {
+ active = false;
+ Cancel(3);
+ }
+
+ if (data.memory) {
+ free(data.memory);
+ data.memory = NULL;
+ data.size = 0;
+ }
+}
+
+void cUpdate::StartUpdate()
+{
+ if (!active)
+ Start();
+ else {
+ active = false;
+ Cancel(3);
+ usleep(250);
+ Start();
+ }
+}
+
+int cUpdate::ReloadChannelMap() {
+ return chanmap->ReloadChannelMap();
+}
+
+void cUpdate::Action(void)
+{
+ dsyslog("TVTV: Timer Thread started (pid=%d)", getpid());
+ active = true;
+ do {
+ MakeTimerUpdate();
+ if (TVTVConfig.autoupdate && TVTVConfig.updatetime > 0)
+ for (long i = 0; active && i < TVTVConfig.updatetime * 60 * 2; i++)
+ usleep(500000);
+ }
+ while (active && TVTVConfig.autoupdate && TVTVConfig.updatetime > 0);
+
+ active = false;
+ dsyslog("TVTV: Timer Thread ended (pid=%d)", getpid());
+ Cancel(0);
+}
+
+
+void cUpdate::Get_Packed_String(char *sOut)
+{
+ char *s;
+ unsigned char signature[17];
+
+ asprintf(&s, "EPGSync%s%s", TVTVConfig.username, TVTVConfig.password);
+
+ MD5_CTX ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char *)s, strlen(s));
+ MD5Final(signature, &ctx);
+ signature[16] = 0;
+
+ char aStr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ char aOut[30];
+ int j = 0;
+
+ aOut[16] = '\0';
+
+ for (int i = 0; i < 16; i+= 3) {
+ uint8_t x, y;
+
+ x = signature[i];
+ x >>= 2;
+ x = x & 0x3f;
+ aOut[j++] = aStr[x];
+
+ y = signature[i];
+ y = y & 0x3;
+ y <<= 4;
+
+ x = signature[i + 1];
+ x >>= 4;
+ x = x & 0x0f;
+ x = x ^ y;
+ aOut[j++] = aStr[x];
+
+ if (i + 1 < 16) {
+ y = signature[i + 1];
+ y = y & 0x0f;
+ y <<= 2;
+
+ // 3
+ x = signature[i + 2];
+ x = x & 0xff;
+ x >>= 6;
+ x = x ^ y;
+ aOut[j++] = aStr[x];
+
+ // 4
+ x = signature[i + 2];
+ x &= 0x3f;
+ aOut[j++] = aStr[x];
+ }
+ else {
+ aOut[j++] = (char) 0x3d;
+ aOut[j++] = (char) 0x3d;
+ }
+ aOut[j] = '\0';
+ }
+
+ strcpy(sOut, aOut);
+ free(s);
+}
+
+
+static size_t WriteMemoryCallback( void *ptr, size_t size, size_t nmemb, void *data) {
+ size_t realsize = size * nmemb;
+ struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+ if (mem->memory)
+ mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+ else
+ mem->memory = (char *)malloc(mem->size + realsize + 1);
+
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+
+int cUpdate::DownloadCSVData(const char *url) {
+ CURL *curl_handle;
+
+ // Initialize 'data' struct
+ if (data.memory) free(data.memory);
+ data.memory = NULL;
+ data.size = 0;
+
+ if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
+ esyslog("TVTV: Something went wrong with curl_global_init()");
+ return -1;
+ }
+ curl_handle = curl_easy_init();
+ if (curl_handle == NULL) {
+ esyslog("TVTV: Unable to get handle from curl_easy_init()");
+ return -1;
+ }
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url); // Specify URL to get
+
+ if (TVTVConfig.useproxy) {
+ curl_easy_setopt(curl_handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+ curl_easy_setopt(curl_handle, CURLOPT_PROXY, TVTVConfig.httpproxy); // Specify HTTP proxy
+ }
+
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); // Send all data to this function
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&data); // Pass our 'data' struct to the callback function
+ curl_easy_setopt(curl_handle, CURLOPT_MAXFILESIZE, 1048576); // Set maximum file size to get (bytes)
+ curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1); // No progress meter
+ curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); // No signaling
+ curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 30); // Set timeout to 30 seconds
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, TVTV_USERAGENT); // Some servers don't like requests that are made without a user-agent field
+
+ if (curl_easy_perform(curl_handle) != 0) {
+ curl_easy_cleanup(curl_handle); // Cleanup curl stuff
+ if (data.memory) {
+ free(data.memory);
+ data.memory = NULL;
+ data.size = 0;
+ }
+ esyslog("TVTV: Couldn't download the stream");
+ return -1;
+ }
+
+ curl_easy_cleanup(curl_handle); // Cleanup curl stuff
+ return data.size;
+}
+
+
+uint8_t cUpdate::toHex(const uint8_t &x)
+{
+ return x > 9 ? x + 55: x + 48;
+}
+
+void cUpdate::URLEncode(const char *sIn, char *sOut)
+{
+ register char *pOutTmp = NULL;
+ char *pOutBuf = NULL;
+ register char *pInTmp = NULL;
+ char *pInBuf = (char *)sIn;
+
+ //alloc out buffer
+ pOutBuf = sOut;
+
+ if(pOutBuf) {
+ pInTmp = pInBuf;
+ pOutTmp = pOutBuf;
+
+ // do encoding
+ while (*pInTmp) {
+ if(isalnum(*pInTmp))
+ *pOutTmp++ = *pInTmp;
+ else
+ if(isspace(*pInTmp))
+ *pOutTmp++ = '+';
+ else {
+ *pOutTmp++ = '%';
+ *pOutTmp++ = toHex(*pInTmp>>4);
+ *pOutTmp++ = toHex(*pInTmp%16);
+ }
+ pInTmp++;
+ }
+ *pOutTmp = '\0';
+ }
+}
+
+
+bool cUpdate::MakeTimerUpdate(void)
+{
+ char *sEnCryptS = NULL;
+ char *sPackedString = NULL;
+ char *url = NULL;
+
+ sPackedString = (char *)calloc(30, sizeof (char));
+ sEnCryptS = (char *)calloc(100, sizeof (char));
+
+ Get_Packed_String(sPackedString);
+ dsyslog("TVTV: Packed String: %s", sPackedString);
+ URLEncode(sPackedString, sEnCryptS);
+
+ asprintf(&url, "http://%s/cgi-bin/WebObjects/TVSync.woa/wa/getjobs?&serial=0&account=%s&product=35&target=%s&doctype=csv&access=%s",
+ TVTV_SERVERS[TVTVConfig.tvtv_server], TVTVConfig.username, TVTV_SERVERS[TVTVConfig.tvtv_server], sEnCryptS);
+
+ isyslog("TVTV: Timer Update started");
+
+ int iFileSize = DownloadCSVData(url);
+
+ if (iFileSize > 0) {
+ dsyslog("TVTV: Received %d Bytes", iFileSize);
+ /* process CSV file */
+ ProcessImportedFile(data.memory);
+ isyslog("TVTV: Timer File processed");
+ }
+ else {
+ esyslog("TVTV: Download Error");
+ }
+
+ // Clean up
+ if (data.memory) {
+ free(data.memory);
+ data.memory = NULL;
+ data.size = 0;
+ }
+ if (url) free(url);
+ if (sEnCryptS) free(sEnCryptS);
+ if (sPackedString) free(sPackedString);
+
+ return true;
+}
+
+
+int cUpdate::calc_field_cnt(string *s) {
+ int n=0;
+ size_t p = 0;
+
+ if ((s == NULL) || (s->empty())) return 0;
+
+ p = s->find(',', 0);
+ while (p != string::npos) {
+ n++;
+ p = s->find(',', p+1);
+ }
+ n++;
+ return n;
+}
+
+char *cUpdate::strip_str(char *s) {
+ char *m=NULL;
+ unsigned int i=0;
+
+ if (s==NULL) return NULL;
+
+ m=(char *)calloc(strlen(s)+1, sizeof(char));
+
+ for (i=0; i<strlen(s); i++) {
+ if (s[i] == '"') {
+ if (s[i+1] == '"') {
+ strncat(m, s+i, 1);
+ i++;
+ }
+ } else if ((s[i] == '\\')&&(s[i+1] == 'n')) {
+ strncat(m, "\n", 1);
+ i++;
+ } else {
+ strncat(m, s+i, 1);
+ }
+ }
+ return m;
+}
+
+
+char **cUpdate::split_csv(const char *job_line, int field_cnt) {
+
+ char **arr=NULL;
+ char *sp=NULL, *ep=NULL;
+ char *s=NULL;
+ int i=0;
+ int n=0;
+ int fini=0;
+
+ if ((job_line == NULL)||(field_cnt == 0)) { return NULL; }
+
+ arr=(char **)calloc(field_cnt, sizeof(char *));
+ sp=(char *)job_line;
+
+ while ((n < field_cnt)&&(fini == 0)) {
+ if (*sp == '"') {
+ sp++;
+ ep=index(sp, '"');
+ if (ep != NULL) {
+ while ( *(ep+1) == '"' ) { ep=index(ep+2, '"'); }
+ ep++;
+ } else {
+ ep=(char *)job_line + strlen(job_line);
+ fini=1;
+ }
+ } else {
+ ep=index(sp, ',');
+ if (ep == NULL) {
+ ep=(char *)job_line + strlen(job_line);
+ fini=1;
+ }
+ }
+ s=(char *)calloc(ep-sp+1, sizeof(char));
+ strncpy(s, sp, ep-sp);
+ arr[n]=strip_str(s);
+ free(s);
+
+ sp=ep+1;
+ n++;
+
+ if ((n < field_cnt)&&(fini==1)) {
+ for(i=0; i<n; i++) free(arr[i]);
+ free(arr);
+ arr=NULL;
+ }
+
+ }
+ return arr;
+}
+
+
+string *cUpdate::read_line_from_buffer(const char *buf, unsigned int *idx) {
+ string s;
+
+ if (buf == NULL) return NULL;
+
+ while ((buf[*idx] != 0)&&(buf[*idx] != '\n')) {
+ if (!iscntrl(buf[*idx])) s.append(1, buf[*idx]);
+ (*idx)++;
+ }
+ while ( (buf[*idx] != 0) && (iscntrl(buf[*idx]) )) (*idx)++;
+ return new string(s);
+}
+
+void cUpdate::ProcessImportedFile(const char *sBuffer)
+{
+ char **fields=NULL;
+ char **tvtvjob=NULL;
+ int field_cnt=0;
+ unsigned int p=0;
+ size_t rp=0;
+ bool vps = false;
+ tChannelID vdrch;
+ bool timer_update=false;
+#if VDRVERSNUM < 10336
+ unsigned int max_desc_len=0;
+#endif
+
+ time_t tStartTime, tVpsTime, tEndTime;
+ struct tm tStart, tVps, tEnd;
+ int StartTZ, VpsTZ, EndTZ;
+
+ cTimer *oTimer = NULL;
+ cTimer *ti = NULL;
+ cChannel *oTVTVChannel = NULL;
+
+ map<string, string> tvtv_timer;
+ string *sLine=NULL;
+ string s;
+
+ const string sTokenTvtvUid = "TVTV-UID: ";
+
+ dsyslog("TVTV: Processing CSV data");
+
+ // skip header
+ do {
+ sLine = read_line_from_buffer(sBuffer, &p);
+ field_cnt = calc_field_cnt(sLine);
+ } while (( field_cnt < 10 ) && ( !sLine->empty() ));
+ fields=split_csv(sLine->c_str(), field_cnt);
+ dsyslog("TVTV: Found %d fields in CSV data", field_cnt);
+
+ // read timer jobs and create timers
+ dsyslog("TVTV: Start reading timer jobs");
+ sLine = read_line_from_buffer(sBuffer, &p);
+ while (!sLine->empty()) {
+ dsyslog("TVTV: Received '%s...'", sLine->substr(0,80).c_str());
+ tvtvjob=split_csv(sLine->c_str(), field_cnt);
+ if (tvtvjob != NULL) {
+ for (int i=0; i<field_cnt; i++) {
+ tvtv_timer[fields[i]] = tvtvjob[i];
+ free(tvtvjob[i]);
+ }
+ free(tvtvjob);
+ }
+
+ // replace newline characters by '|'
+ rp = tvtv_timer[DEF_TVTV_SCHEDULE_DESC].find('\n',0 );
+ while (rp != string::npos) { tvtv_timer[DEF_TVTV_SCHEDULE_DESC].replace(rp, 1, 1, '|'); rp = tvtv_timer[DEF_TVTV_SCHEDULE_DESC].find('\n',0 ); }
+ rp = tvtv_timer[DEF_TVTV_SCHEDULE_PERS].find('\n',0 );
+ while (rp != string::npos) { tvtv_timer[DEF_TVTV_SCHEDULE_PERS].replace(rp, 1, 1, '|'); rp = tvtv_timer[DEF_TVTV_SCHEDULE_PERS].find('\n',0 ); }
+
+ // Get ChannelID from channel map
+ vdrch=chanmap->GetChanID(atoi(tvtv_timer[DEF_TVTV_SCHEDULE_CHID].c_str()));
+ if (!(vdrch == tChannelID::InvalidID)) {
+
+#if VDRVERSNUM >= 10305
+ oTVTVChannel = Channels.GetByChannelID(vdrch, true, true);
+#else
+ oTVTVChannel = Channels.GetByChannelID(vdrch, true);
+#endif
+ if (oTVTVChannel) {
+
+ oTimer = new cTimer;
+
+ // Calc Start and Stop Time
+ sscanf(tvtv_timer[DEF_TVTV_SCHEDULE_STM].c_str(), "%04d-%02d-%02d %02d:%02d:%02d %d",
+ &tStart.tm_year, &tStart.tm_mon, &tStart.tm_mday,
+ &tStart.tm_hour, &tStart.tm_min, &tStart.tm_sec, &StartTZ);
+
+ sscanf(tvtv_timer[DEF_TVTV_SCHEDULE_VTM].c_str(), "%04d-%02d-%02d %02d:%02d:%02d %d",
+ &tVps.tm_year, &tVps.tm_mon, &tVps.tm_mday,
+ &tVps.tm_hour, &tVps.tm_min, &tVps.tm_sec, &VpsTZ);
+
+ sscanf(tvtv_timer[DEF_TVTV_SCHEDULE_ETM].c_str(), "%04d-%02d-%02d %02d:%02d:%02d %d",
+ &tEnd.tm_year, &tEnd.tm_mon, &tEnd.tm_mday,
+ &tEnd.tm_hour, &tEnd.tm_min, &tEnd.tm_sec, &EndTZ);
+
+ tStart.tm_year -= 1900; tStart.tm_mon -= 1; tStart.tm_isdst = -1;
+ tVps.tm_year -= 1900; tVps.tm_mon -= 1; tVps.tm_isdst = -1;
+ tEnd.tm_year -= 1900; tEnd.tm_mon -= 1; tEnd.tm_isdst = -1;
+
+ tStartTime = timegm(&tStart);
+ tVpsTime = timegm(&tVps);
+ tEndTime = timegm(&tEnd);
+
+// VPS was introduced with VDR 1.3.5
+#if VDRVERSNUM >= 10305
+ vps = Setup.UseVps && (tVpsTime != -1) && TVTVConfig.usevps;
+#else
+ vps = false;
+#endif
+
+ tStartTime -= StartTZ*36;
+ tEndTime -= EndTZ*36;
+ if (vps) tVpsTime -= VpsTZ*36;
+
+ tStartTime -= Setup.MarginStart * 60;
+ if (!vps) tEndTime += Setup.MarginStop * 60;
+ localtime_r(&tStartTime, &tStart);
+ localtime_r(&tVpsTime, &tVps);
+ localtime_r(&tEndTime, &tEnd);
+
+ // Adding Genre and Station Name to File Name to make it more unique
+ if (TVTVConfig.usegenre && ! tvtv_timer[DEF_TVTV_SCHEDULE_NAT].empty())
+ s = tvtv_timer[DEF_TVTV_SCHEDULE_NAT] + "~";
+ else
+ s = "";
+
+ if (TVTVConfig.usestation)
+#if VDRVERSNUM >= 10315
+ s = s + oTVTVChannel->ShortName(true) + "~";
+#else
+ s = s + oTVTVChannel->Name() + "~";
+#endif
+ s += tvtv_timer[DEF_TVTV_SCHEDULE_TIT];
+
+
+ // Colons should be replaced by '|' in title
+ rp = s.find(':',0 );
+ while (rp != string::npos) { s.replace(rp, 1, 1, '|'); rp = s.find(':',0 ); }
+
+ // create timer string
+ ostringstream oss(ostringstream::out);
+ oss.fill('0');
+#if VDRVERSNUM >= 10305
+ oss << (tfActive|(vps?tfVps:tfNone)) << ":";
+#else
+ oss << "1:";
+#endif
+ oss << oTVTVChannel->GetChannelID().ToString() << ":";
+
+// Starting with VDR 1.3.23 timer entries support a full date in ISO notation.
+#if VDRVERSNUM >= 10323
+ oss.width(4); oss << ((vps?(tVps.tm_year):(tStart.tm_year))+1900) << "-";
+ oss.width(2); oss << ((vps?(tVps.tm_mon):(tStart.tm_mon))+1) << "-";
+#endif
+ oss.width(2); oss << (vps?(tVps.tm_mday):(tStart.tm_mday)) << ":";
+ oss.width(4); oss << (vps?(tVps.tm_hour * 100 + tVps.tm_min):(tStart.tm_hour * 100 + tStart.tm_min)) << ":";
+ oss.width(4); oss << tEnd.tm_hour * 100 + tEnd.tm_min << ":";
+ oss << Setup.DefaultPriority << ":";
+ oss << Setup.DefaultLifetime << ":";
+ oss << s.c_str() << ":";
+
+ dsyslog("TVTV: Timer entry '%s'", oss.str().c_str());
+ s=oss.str();
+
+// Starting with VDR 1.3.44 the description within info.vdr is taken
+// just from EPG. A description coming with a timer is added as a comment
+// to info.vdr. This setup parameter decides whether to add this description
+// as a comment or not.
+#if VDRVERSNUM >= 10344
+ if (TVTVConfig.usetvtvdescr) {
+#endif
+
+// Recording summary changed in VDR 1.3.25
+#if VDRVERSNUM < 10325
+ s += tvtv_timer[DEF_TVTV_SCHEDULE_TIT];
+ if ((tvtv_timer[DEF_TVTV_SCHEDULE_PERS].length() > 0)||(tvtv_timer[DEF_TVTV_SCHEDULE_DESC].length() > 0)) s += "||";
+#endif
+
+ if (tvtv_timer[DEF_TVTV_SCHEDULE_PERS].length() > 0) {
+ s += "Darsteller: " + tvtv_timer[DEF_TVTV_SCHEDULE_PERS];
+ if (tvtv_timer[DEF_TVTV_SCHEDULE_DESC].length() > 0) s += "||" + tvtv_timer[DEF_TVTV_SCHEDULE_DESC];
+ } else {
+ if (tvtv_timer[DEF_TVTV_SCHEDULE_DESC].length() > 0) s += tvtv_timer[DEF_TVTV_SCHEDULE_DESC];
+ }
+
+// Length of timer entry is limited to less than 10240 characters. Keeping off another
+// 15 to avoid problems with additional characters (like newline).
+// This is not needed for VDR 1.3.36+ anymore!
+#if VDRVERSNUM < 10336
+ max_desc_len = MAXPARSEBUFFER - sTokenTvtvUid.length() - tvtv_timer[DEF_TVTV_SCHEDULE_UID].length() - 15;
+ if (s.length() > max_desc_len) {
+ dsyslog("TVTV: received description too long, cutted (%d -> %d)", s.length(), max_desc_len);
+ s.erase(max_desc_len);
+ }
+#endif
+
+#if VDRVERSNUM >= 10344
+ }
+#endif
+ // Add Timer's UID to description to identify it later on
+ if (tvtv_timer[DEF_TVTV_SCHEDULE_UID].length() > 0) s += "||" + sTokenTvtvUid + tvtv_timer[DEF_TVTV_SCHEDULE_UID];
+
+
+ if (oTimer->Parse(s.c_str())) {
+ cTimer *t = Timers.GetTimer(oTimer);
+ if (t) { // Timer exists
+ if (tvtv_timer[DEF_TVTV_SCHEDULE_ACT] == "delete") {
+ if (t->Recording()) {
+ isyslog("TVTV: timer %d recording, will be deleted after recording has ended (%s)", t->Index() + 1, t->File());
+ } else {
+ isyslog("TVTV: timer %d deleted [%s/%s/%d/%04d-%04d/%s]", oTimer->Index() + 1,
+ tvtv_timer[DEF_TVTV_SCHEDULE_UID].c_str(),
+ tvtv_timer[DEF_TVTV_SCHEDULE_CHN].c_str(),
+ vps ? (tVps.tm_mday):(tStart.tm_mday),
+ vps ? (tVps.tm_hour * 100 + tVps.tm_min):(tStart.tm_hour * 100 + tStart.tm_min),
+ tEnd.tm_hour * 100 + tEnd.tm_min,
+ vps ? "VPS":"-");
+ Timers.Del(t);
+ }
+ } else {
+ isyslog("TVTV: timer %d exist (%s)", t->Index() + 1, t->File());
+ }
+ delete oTimer;
+ } else { // if (t)
+
+ // try to figure out if timer needs to be updated
+ timer_update=false;
+ ti = Timers.First();
+ while (ti != NULL) {
+#if VDRVERSNUM >= 10344
+ if (ti->Aux() != NULL) {
+ s=ti->Aux();
+#else
+ if (ti->Summary() != NULL) {
+ s=ti->Summary();
+#endif
+ if (s.find(sTokenTvtvUid + tvtv_timer[DEF_TVTV_SCHEDULE_UID], 0) != string::npos) {
+ timer_update=true;
+ break;
+ } else {
+ ti = Timers.Next(ti);
+ }
+ } else {
+ ti = Timers.Next(ti);
+ }
+ }
+
+ if (timer_update) { // Timer exists
+ if (tvtv_timer[DEF_TVTV_SCHEDULE_ACT] == "rec") {
+ // avoid Timer updates if Timer is shifted exactly 1 or 2 hours earlier (TVTV Problem)
+ if (TVTVConfig.tvtv_bugfix && ((((ti->StartTime() - tStartTime) == TVTVConfig.tvtv_bugfix_hrs*3600) &&
+ ((ti->StopTime() - tEndTime) == TVTVConfig.tvtv_bugfix_hrs*3600)) ||
+ (vps && ((ti->StopTime() - tEndTime) == TVTVConfig.tvtv_bugfix_hrs*3600)) )) {
+ isyslog("TVTV: timer %d update rejected (%s) [%s/%s/%d/%04d-%04d/%s]", ti->Index() + 1,
+ ti->File(),
+ tvtv_timer[DEF_TVTV_SCHEDULE_UID].c_str(),
+ tvtv_timer[DEF_TVTV_SCHEDULE_CHN].c_str(),
+ vps ? (tVps.tm_mday):(tStart.tm_mday),
+ vps ? (tVps.tm_hour * 100 + tVps.tm_min):(tStart.tm_hour * 100 + tStart.tm_min),
+ tEnd.tm_hour * 100 + tEnd.tm_min,
+ vps ? "VPS":"-"
+ );
+ } else {
+ if (ti->Recording()) {
+ isyslog("TVTV: timer %d recording, will be deleted after recording has ended (%s)", ti->Index() + 1, ti->File());
+ } else {
+ Timers.Del(ti);
+ }
+ Timers.Add(oTimer);
+ isyslog("TVTV: timer %d updated (%s) [%s/%s/%d/%04d-%04d/%s]", oTimer->Index() + 1,
+ oTimer->File(),
+ tvtv_timer[DEF_TVTV_SCHEDULE_UID].c_str(),
+ tvtv_timer[DEF_TVTV_SCHEDULE_CHN].c_str(),
+ vps ? (tVps.tm_mday):(tStart.tm_mday),
+ vps ? (tVps.tm_hour * 100 + tVps.tm_min):(tStart.tm_hour * 100 + tStart.tm_min),
+ tEnd.tm_hour * 100 + tEnd.tm_min,
+ vps ? "VPS":"-"
+ );
+ }
+ } else if (tvtv_timer[DEF_TVTV_SCHEDULE_ACT] == "delete") {
+ if (ti->Recording()) {
+ isyslog("TVTV: timer %d recording, will be deleted after recording has ended (%s)", ti->Index() + 1, ti->File());
+ } else {
+ isyslog("TVTV: timer %d deleted [%s/%s/%d/%04d-%04d/%s]", oTimer->Index() + 1,
+ tvtv_timer[DEF_TVTV_SCHEDULE_UID].c_str(),
+ tvtv_timer[DEF_TVTV_SCHEDULE_CHN].c_str(),
+ vps ? (tVps.tm_mday):(tStart.tm_mday),
+ vps ? (tVps.tm_hour * 100 + tVps.tm_min):(tStart.tm_hour * 100 + tStart.tm_min),
+ tEnd.tm_hour * 100 + tEnd.tm_min,
+ vps ? "VPS":"-");
+ Timers.Del(ti);
+ }
+ }
+ } else { // if (timer_update)
+ if (tvtv_timer[DEF_TVTV_SCHEDULE_ACT] == "rec") {
+ Timers.Add(oTimer);
+ isyslog("TVTV: timer %d added (%s) [%s/%s/%d/%04d-%04d/%s]", oTimer->Index() + 1,
+ oTimer->File(),
+ tvtv_timer[DEF_TVTV_SCHEDULE_UID].c_str(),
+ tvtv_timer[DEF_TVTV_SCHEDULE_CHN].c_str(),
+ vps ? (tVps.tm_mday):(tStart.tm_mday),
+ vps ? (tVps.tm_hour * 100 + tVps.tm_min):(tStart.tm_hour * 100 + tStart.tm_min),
+ tEnd.tm_hour * 100 + tEnd.tm_min,
+ vps ? "VPS":"-");
+ }
+ }
+
+ }
+ } else {
+ isyslog("TVTV: Timer Error: %s", s.c_str());
+ }
+
+ } else { // if (oTVTVChannel)
+ isyslog("TVTV: ChannelID <%s> for Channel <%s> was not found in channels.conf!", (const char *)vdrch.ToString(), tvtv_timer[DEF_TVTV_SCHEDULE_CHN].c_str());
+ }
+
+ } else { // if (!(vdrch == tChannelID::InvalidID))
+ isyslog("TVTV: Channel <%s (ID: %d)> is not found!", tvtv_timer[DEF_TVTV_SCHEDULE_CHN].c_str(), atoi(tvtv_timer[DEF_TVTV_SCHEDULE_CHID].c_str()));
+ }
+
+ // read next line
+ sLine = read_line_from_buffer(sBuffer, &p);
+ tvtv_timer.clear();
+
+ } // while (!sLine->empty())
+
+ Timers.Save();
+}
+
diff --git a/update.h b/update.h
new file mode 100644
index 0000000..561fb9e
--- /dev/null
+++ b/update.h
@@ -0,0 +1,82 @@
+/*
+ * update.h: TVTV plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef __UPDATE_H
+#define __UPDATE_H
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include "i18n.h"
+#include "config.h"
+#include "channelmap.h"
+#include "md5tools.h"
+
+using namespace std;
+
+
+#define TVTV_USERAGENT "libcurl-agent/1.0"
+
+#define DEF_TVTV_SCHEDULE_UID "uid"
+#define DEF_TVTV_SCHEDULE_CHN "channel"
+#define DEF_TVTV_SCHEDULE_CHID "channelid"
+#define DEF_TVTV_SCHEDULE_STM "starttime"
+#define DEF_TVTV_SCHEDULE_VTM "vps"
+#define DEF_TVTV_SCHEDULE_ETM "endtime"
+#define DEF_TVTV_SCHEDULE_TIT "title"
+#define DEF_TVTV_SCHEDULE_NAT "nature"
+#define DEF_TVTV_SCHEDULE_DESC "description"
+#define DEF_TVTV_SCHEDULE_PERS "persons"
+#define DEF_TVTV_SCHEDULE_FRM "format"
+#define DEF_TVTV_SCHEDULE_FSK "pg"
+#define DEF_TVTV_SCHEDULE_ACT "action"
+
+
+#define TVTVSRV_CNT 8
+static const char *TVTV_SERVERS[TVTVSRV_CNT] = {"www.tvtv.de", "www.tvtv.at", "www.tvtv.co.uk", "www.tvtv.fr",
+ "www.tvtv.it", "www.tvtv.ch", "www.tvtv.es", "www.tvtv.nl"};
+
+
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+
+class cUpdate : public cThread {
+private:
+ bool active;
+ struct MemoryStruct data;
+ cChannelMap *chanmap;
+
+ uint8_t toHex(const uint8_t &x);
+ void URLEncode(const char *sIn, char *sOut);
+ void Get_Packed_String(char *sOut);
+
+ void Action(void);
+ int DownloadCSVData(const char *url);
+ bool MakeTimerUpdate(void);
+ void ProcessImportedFile(const char *sBuffer);
+
+ int calc_field_cnt(string *s);
+ char *strip_str(char *s);
+ char **split_csv(const char *job_line, int field_cnt);
+ string *read_line_from_buffer(const char *buf, unsigned int *idx);
+public:
+ cUpdate();
+ ~cUpdate();
+ void StartUpdate();
+ int ReloadChannelMap();
+};
+
+#endif //__UPDATE_H