initial commit

This commit is contained in:
louis 2014-04-12 17:10:43 +02:00
commit 801610d259
31 changed files with 9104 additions and 0 deletions

340
COPYING Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General
Public License instead of this License.

6
HISTORY Normal file
View File

@ -0,0 +1,6 @@
VDR Plugin 'scraper2vdr' Revision History
-----------------------------------------
2014-03-02: Version 0.0.1
- Initial revision.

132
Makefile Normal file
View File

@ -0,0 +1,132 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id$
# External image lib to use: imagemagick, graphicsmagick
IMAGELIB = imagemagick
PLUGIN = scraper2vdr
### The version number of this plugin (taken from the main source file):
VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).h | awk '{ print $$6 }' | sed -e 's/[";]//g')
### The directory environment:
# Use package data if installed...otherwise assume we're under the VDR source directory:
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
LIBDIR = $(call PKGCFG,libdir)
LOCDIR = $(call PKGCFG,locdir)
PLGCFG = $(call PKGCFG,plgcfg)
#
TMPDIR ?= /tmp
### The compiler options:
export CFLAGS = $(call PKGCFG,cflags)
export CXXFLAGS = $(call PKGCFG,cxxflags)
### The version number of VDR's plugin API:
APIVERSION = $(call PKGCFG,apiversion)
### Allow user defined options to overwrite defaults:
-include $(PLGCFG)
LIBS = -lmysqlclient_r -luuid
### The name of the distribution archive:
ARCHIVE = $(PLUGIN)-$(VERSION)
PACKAGE = vdr-$(ARCHIVE)
### The name of the shared object file:
SOFILE = libvdr-$(PLUGIN).so
### Includes and Defines (add further entries here):
INCLUDES +=
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
DEFINES += -DVDR_PLUGIN -DUSEUUID
ifeq ($(IMAGELIB), imagemagick)
INCLUDES += $(shell pkg-config --cflags Magick++)
LIBS += $(shell pkg-config --libs Magick++)
else ifeq ($(IMAGELIB), graphicsmagick)
INCLUDES += $(shell pkg-config --cflags GraphicsMagick++)
LIBS += $(shell pkg-config --libs GraphicsMagick++)
endif
### The object files (add further files here):
OBJS = $(PLUGIN).o config.o setup.o update.o scrapmanager.o tvdbseries.o moviedbmovie.o tools.o lib/db.o lib/tabledef.o lib/common.o lib/config.o
### The main target:
all: $(SOFILE) i18n
### Implicit rules:
%.o: %.c
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
### Dependencies:
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
-include $(DEPFILE)
### Internationalization (I18N):
PODIR = po
I18Npo = $(wildcard $(PODIR)/*.po)
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
I18Npot = $(PODIR)/$(PLUGIN).pot
%.mo: %.po
msgfmt -c -o $@ $<
$(I18Npot): $(wildcard *.c)
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
%.po: $(I18Npot)
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
@touch $@
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
install -D -m644 $< $@
.PHONY: i18n
i18n: $(I18Nmo) $(I18Npot)
install-i18n: $(I18Nmsgs)
### Targets:
$(SOFILE): $(OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
install-lib: $(SOFILE)
install -D -m644 $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
install: install-lib install-i18n
dist: $(I18Npo) clean
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@mkdir $(TMPDIR)/$(ARCHIVE)
@cp -a * $(TMPDIR)/$(ARCHIVE)
@tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@echo Distribution package created as $(PACKAGE).tgz
clean:
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
@-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~

79
README Normal file
View File

@ -0,0 +1,79 @@
This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Louis Braun <louis.braun@gmx.de>
Project's homepage: http://projects.vdr-developer.org/projects/plg-scraper2vdr
Latest version available at: http://projects.vdr-developer.org/git/vdr-plugin-scraper2vdr.git/
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.
See the file COPYING for more information.
Description
-----------
scraper2vdr acts as client and provides scraped metadata for tvshows and
movies from epgd to other plugins via its service interface. The plugin
cares about caching the images locally and also cleans up the images if
not longer needed.
epgd itself uses the thetvdb.com API for collecting series metadata and
themoviedb.org API for movies. Check the websites of both services for
the terms of use.
Requirements
------------
To run the plugin the following libaries have to be installed:
- VDR 1.7.x
- libmysql >= 5.07
- uuid-dev
- imagemagick or graphicksmagick
Installation and configuration
------------------------------
Just install the plugin depending on your used distribution. During VDR
startup the following options can be set:
-i <IMAGEDIR>, --imagedir=<IMAGEDIR> Set directory where images are stored
-m <MODE>, --mode=<MODE> mode can be client or headless.
Each running scraper2vdr Plugin reports his recordings to the epgd
database, the epgd then checks these entries and tries to find
appropriate scraping information. epgd performs first a lookup for a
event in the database which belongs to the recording. If this fails, epgd
checks if another client has already reported this recording to the database.
After that, the scrapinfo file in the recording directory (if existing)
is checked. If nothing is successfull, a new scrap process for the name
of the recording is done. If in this case the length of the recording
is less than 70 minutes, a series recording is assumed, otherwise
the scraper searches for a movie.
In client mode both live epg and recordings metadata is loaded from the
database. In headless mode only recording metadata is loaded. This mode
is useful for headless VDRs so that recordings which are done from this
VDR during no other VDR client with running scraper2vdr Plugin is active
are not missed. The recording information is then written to the database
in time before the related and already reliably scraped event entry is
deleted from the database.
Service Interface
-----------------
Other Plugins can and should request information about meta data from
scraper2vdr via a call to the provided service interface.
First the service "GetEventType" which expects a pointer to a cEvent or
a cRecording object as input variable has to be called. This call provides
the type of the event or recording (tSeries, tMovie, tNone) and the seriesId,
episodeId and movieId. If type is tSeries, movieId is 0 and vice versa.
With that then a second call to GetSeries or GetMovie with the appropriate IDs
provides all stored information for the series or movie in form of a cSeries
or cMovie object.
For further information just check the self explanatory services.h file.

56
config.c Normal file
View File

@ -0,0 +1,56 @@
#include "lib/common.h"
#include "config.h"
cScraper2VdrConfig::cScraper2VdrConfig() {
mainMenuEntry = 1;
headless = false;
uuid = "";
imgDirSet = false;
mysqlHost = "localhost";
mysqlPort = 3306;
mysqlDBName = "epg2vdr";
mysqlDBUser = "epg2vdr";
mysqlDBPass = "epg";
recScrapInfoName = "scrapinfo";
}
cScraper2VdrConfig::~cScraper2VdrConfig() {
}
void cScraper2VdrConfig::SetUuid(cPlugin *plug) {
if (uuid.size() == 0) {
uuid = getUniqueId();
plug->SetupStore("uuid", uuid.c_str());
}
tell(0, "epgd uuid: %s", uuid.c_str());
}
void cScraper2VdrConfig::SetImageDir(cString dir) {
imageDir = *dir;
imgDirSet = true;
}
void cScraper2VdrConfig::SetDefaultImageDir(void) {
if (!imgDirSet) {
imageDir = cPlugin::CacheDirectory(PLUGIN_NAME_I18N);
}
tell (0, "using image directory %s", imageDir.c_str());
}
void cScraper2VdrConfig::SetMode(string mode) {
if (!mode.compare("headless"))
headless = true;
}
bool cScraper2VdrConfig::SetupParse(const char *Name, const char *Value) {
if (strcmp(Name, "uuid") == 0) uuid = Value;
else if (strcmp(Name, "mainMenuEntry") == 0) mainMenuEntry = atoi(Value);
else if (strcmp(Name, "mysqlHost") == 0) mysqlHost = Value;
else if (strcmp(Name, "mysqlPort") == 0) mysqlPort = atoi(Value);
else if (strcmp(Name, "mysqlDBName") == 0) mysqlDBName = Value;
else if (strcmp(Name, "mysqlDBUser") == 0) mysqlDBUser = Value;
else if (strcmp(Name, "mysqlDBPass") == 0) mysqlDBPass = Value;
else
return false;
return true;
}

31
config.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef __SCRAPER2VDR_CONFIG_H
#define __SCRAPER2VDR_CONFIG_H
#include <string>
#include <vdr/plugin.h>
using namespace std;
class cScraper2VdrConfig {
private:
public:
cScraper2VdrConfig();
~cScraper2VdrConfig();
bool SetupParse(const char *Name, const char *Value);
void SetUuid(cPlugin *plug);
void SetImageDir(cString dir);
void SetDefaultImageDir(void);
void SetMode(string mode);
int mainMenuEntry;
bool headless;
string uuid;
bool imgDirSet;
string imageDir;
string mysqlHost;
int mysqlPort;
string mysqlDBName;
string mysqlDBUser;
string mysqlDBPass;
string recScrapInfoName;
};
#endif //__SCRAPER2VDR_CONFIG_H

7
lib/Makefile Normal file
View File

@ -0,0 +1,7 @@
all:
g++ -ggdb -DPLGDIR='"."' test.c common.c config.c db.c tabledef.c -lrt -lz -lmysqlclient -o t
clean:
rm -f *.o *.a *~ core

812
lib/common.c Normal file
View File

@ -0,0 +1,812 @@
/*
* common.c:
*
* See the README file for copyright information and how to reach the author.
*
*/
#include <sys/stat.h>
#ifdef USEUUID
# include <uuid/uuid.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <zlib.h>
#ifdef USELIBARCHIVE
# include <archive.h>
# include <archive_entry.h>
#endif
#ifdef VDR_PLUGIN
# include <vdr/thread.h>
#endif
#include "common.h"
#include "config.h"
#ifdef VDR_PLUGIN
cMutex logMutex;
#endif
//***************************************************************************
// Debug
//***************************************************************************
void tell(int eloquence, const char* format, ...)
{
if (EPG2VDRConfig.loglevel < eloquence)
return ;
const int sizeBuffer = 100000;
char t[sizeBuffer+100]; *t = 0;
va_list ap;
#ifdef VDR_PLUGIN
cMutexLock lock(&logMutex);
#endif
va_start(ap, format);
#ifdef VDR_PLUGIN
snprintf(t, sizeBuffer, "scraper2vdr: ");
#endif
vsnprintf(t+strlen(t), sizeBuffer-strlen(t), format, ap);
if (EPG2VDRConfig.logstdout)
{
char buf[50+TB];
time_t now;
time(&now);
strftime(buf, 50, "%y.%m.%d %H:%M:%S", localtime(&now));
printf("%s %s\n", buf, t);
}
else
syslog(LOG_ERR, "%s", t);
va_end(ap);
}
//***************************************************************************
// Host ID
//***************************************************************************
unsigned int getHostId()
{
static unsigned int id = gethostid() & 0xFFFFFFFF;
return id;
}
//***************************************************************************
// String Operations
//***************************************************************************
void toUpper(std::string& str)
{
const char* s = str.c_str();
int lenSrc = str.length();
char* dest = (char*)malloc(lenSrc+TB); *dest = 0;
char* d = dest;
int csSrc; // size of character
for (int ps = 0; ps < lenSrc; ps += csSrc)
{
csSrc = max(mblen(&s[ps], lenSrc-ps), 1);
if (csSrc == 1)
*d++ = toupper(s[ps]);
else if (csSrc == 2 && s[ps] == (char)0xc3 && s[ps+1] >= (char)0xa0)
{
*d++ = s[ps];
*d++ = s[ps+1] - 32;
}
else
{
for (int i = 0; i < csSrc; i++)
*d++ = s[ps+i];
}
}
*d = 0;
str = dest;
free(dest);
}
void removeChars(std::string& str, const char* ignore)
{
const char* s = str.c_str();
int lenSrc = str.length();
int lenIgn = strlen(ignore);
char* dest = (char*)malloc(lenSrc+TB); *dest = 0;
char* d = dest;
int csSrc; // size of character
int csIgn; //
for (int ps = 0; ps < lenSrc; ps += csSrc)
{
int skip = no;
csSrc = max(mblen(&s[ps], lenSrc-ps), 1);
for (int pi = 0; pi < lenIgn; pi += csIgn)
{
csIgn = max(mblen(&ignore[pi], lenIgn-pi), 1);
if (csSrc == csIgn && strncmp(&s[ps], &ignore[pi], csSrc) == 0)
{
skip = yes;
break;
}
}
if (!skip)
{
for (int i = 0; i < csSrc; i++)
*d++ = s[ps+i];
}
}
*d = 0;
str = dest;
free(dest);
}
void removeCharsExcept(std::string& str, const char* except)
{
const char* s = str.c_str();
int lenSrc = str.length();
int lenIgn = strlen(except);
char* dest = (char*)malloc(lenSrc+TB); *dest = 0;
char* d = dest;
int csSrc; // size of character
int csIgn; //
for (int ps = 0; ps < lenSrc; ps += csSrc)
{
int skip = yes;
csSrc = max(mblen(&s[ps], lenSrc-ps), 1);
for (int pi = 0; pi < lenIgn; pi += csIgn)
{
csIgn = max(mblen(&except[pi], lenIgn-pi), 1);
if (csSrc == csIgn && strncmp(&s[ps], &except[pi], csSrc) == 0)
{
skip = no;
break;
}
}
if (!skip)
{
for (int i = 0; i < csSrc; i++)
*d++ = s[ps+i];
}
}
*d = 0;
str = dest;
free(dest);
}
void removeWord(std::string& pattern, std::string word)
{
size_t pos;
if ((pos = pattern.find(word)) != std::string::npos)
pattern.swap(pattern.erase(pos, word.length()));
}
//***************************************************************************
// String Manipulation
//***************************************************************************
void prepareCompressed(std::string& pattern)
{
// const char* ignore = " (),.;:-_+*!#?=&%$<>§/'`´@~\"[]{}";
const char* notignore = "ABCDEFGHIJKLMNOPQRSTUVWXYZßÖÄÜöäü0123456789";
toUpper(pattern);
removeWord(pattern, " TEIL ");
removeWord(pattern, " FOLGE ");
removeCharsExcept(pattern, notignore);
}
//***************************************************************************
// Left Trim
//***************************************************************************
char* lTrim(char* buf)
{
if (buf)
{
char *tp = buf;
while (*tp && strchr("\n\r\t ",*tp))
tp++;
memmove(buf, tp, strlen(tp) +1);
}
return buf;
}
//*************************************************************************
// Right Trim
//*************************************************************************
char* rTrim(char* buf)
{
if (buf)
{
char *tp = buf + strlen(buf);
while (tp >= buf && strchr("\n\r\t ",*tp))
tp--;
*(tp+1) = 0;
}
return buf;
}
//*************************************************************************
// All Trim
//*************************************************************************
char* allTrim(char* buf)
{
return lTrim(rTrim(buf));
}
//***************************************************************************
// Number to String
//***************************************************************************
std::string num2Str(int num)
{
char txt[16];
snprintf(txt, sizeof(txt), "%d", num);
return std::string(txt);
}
//***************************************************************************
// Long to Pretty Time
//***************************************************************************
std::string l2pTime(time_t t)
{
char txt[30];
tm* tmp = localtime(&t);
strftime(txt, sizeof(txt), "%d.%m.%Y %T", tmp);
return std::string(txt);
}
//***************************************************************************
// MS to Duration
//***************************************************************************
std::string ms2Dur(uint64_t t)
{
char txt[30];
int s = t / 1000;
int ms = t % 1000;
snprintf(txt, sizeof(txt), "%d.%03d seconds", s, ms);
return std::string(txt);
}
//***************************************************************************
// Char to Char-String
//***************************************************************************
const char* c2s(char c, char* buf)
{
sprintf(buf, "%c", c);
return buf;
}
//***************************************************************************
// TOOLS
//***************************************************************************
int isEmpty(const char* str)
{
return !str || !*str;
}
char* sstrcpy(char* dest, const char* src, int max)
{
if (!dest || !src)
return 0;
strncpy(dest, src, max);
dest[max-1] = 0;
return dest;
}
int isLink(const char* path)
{
struct stat sb;
if (lstat(path, &sb) == 0)
return S_ISLNK(sb.st_mode);
tell(0, "Error: Detecting state for '%s' failed, error was '%m'", path);
return false;
}
int fileSize(const char* path)
{
struct stat sb;
if (lstat(path, &sb) == 0)
return sb.st_size;
tell(0, "Error: Detecting state for '%s' failed, error was '%m'", path);
return false;
}
int fileExists(const char* path)
{
return access(path, F_OK) == 0;
}
int createLink(const char* link, const char* dest, int force)
{
if (!fileExists(link) || force)
{
// may be the link exists and point to a wrong or already deleted destination ...
// .. therefore we delete the link at first
unlink(link);
if (symlink(dest, link) != 0)
{
tell(0, "Failed to create symlink '%s', error was '%m'", link);
return fail;
}
}
return success;
}
//***************************************************************************
// Remove File
//***************************************************************************
int removeFile(const char* filename)
{
int lnk = isLink(filename);
if (unlink(filename) != 0)
{
tell(0, "Can't remove file '%s', '%m'", filename);
return 1;
}
tell(3, "Removed %s '%s'", lnk ? "link" : "file", filename);
return 0;
}
//***************************************************************************
// Check Dir
//***************************************************************************
int chkDir(const char* path)
{
struct stat fs;
if (stat(path, &fs) != 0 || !S_ISDIR(fs.st_mode))
{
tell(0, "Creating directory '%s'", path);
if (mkdir(path, ACCESSPERMS) == -1)
{
tell(0, "Can't create directory '%m'");
return fail;
}
}
return success;
}
#ifdef USELIBXML
//***************************************************************************
// Load XSLT
//***************************************************************************
xsltStylesheetPtr loadXSLT(const char* name, const char* path, int utf8)
{
xsltStylesheetPtr stylesheet;
char* xsltfile;
asprintf(&xsltfile, "%s/%s-%s.xsl", path, name, utf8 ? "utf-8" : "iso-8859-1");
if ((stylesheet = xsltParseStylesheetFile((const xmlChar*)xsltfile)) == 0)
tell(0, "Error: Can't load xsltfile %s", xsltfile);
else
tell(0, "Info: Stylesheet '%s' loaded", xsltfile);
free(xsltfile);
return stylesheet;
}
#endif
//***************************************************************************
// Gnu Unzip
//***************************************************************************
int gunzip(MemoryStruct* zippedData, MemoryStruct* unzippedData)
{
const int growthStep = 1024;
z_stream stream = {0,0,0,0,0,0,0,0,0,0,0,Z_NULL,Z_NULL,Z_NULL};
unsigned int resultSize = 0;
int res = 0;
unzippedData->clear();
// determining the size in this way is taken from the sources of the gzip utility.
memcpy(&unzippedData->size, zippedData->memory + zippedData->size -4, 4);
unzippedData->memory = (char*)malloc(unzippedData->size);
// zlib initialisation
stream.avail_in = zippedData->size;
stream.next_in = (Bytef*)zippedData->memory;
stream.avail_out = unzippedData->size;
stream.next_out = (Bytef*)unzippedData->memory;
// The '+ 32' tells zlib to process zlib&gzlib headers
res = inflateInit2(&stream, MAX_WBITS + 32);
if (res != Z_OK)
{
tellZipError(res, " during zlib initialisation", stream.msg);
inflateEnd(&stream);
return fail;
}
// skip the header
res = inflate(&stream, Z_BLOCK);
if (res != Z_OK)
{
tellZipError(res, " while skipping the header", stream.msg);
inflateEnd(&stream);
return fail;
}
while (res == Z_OK)
{
if (stream.avail_out == 0)
{
unzippedData->size += growthStep;
unzippedData->memory = (char*)realloc(unzippedData->memory, unzippedData->size);
// Set the stream pointers to the potentially changed buffer!
stream.avail_out = resultSize - stream.total_out;
stream.next_out = (Bytef*)(unzippedData + stream.total_out);
}
res = inflate(&stream, Z_SYNC_FLUSH);
resultSize = stream.total_out;
}
if (res != Z_STREAM_END)
{
tellZipError(res, " during inflating", stream.msg);
inflateEnd(&stream);
return fail;
}
unzippedData->size = resultSize;
inflateEnd(&stream);
return success;
}
//*************************************************************************
// tellZipError
//*************************************************************************
void tellZipError(int errorCode, const char* op, const char* msg)
{
if (!op) op = "";
if (!msg) msg = "None";
switch (errorCode)
{
case Z_OK: return;
case Z_STREAM_END: return;
case Z_MEM_ERROR: tell(0, "Error: Not enough memory to unzip file%s!\n", op); return;
case Z_BUF_ERROR: tell(0, "Error: Couldn't unzip data due to output buffer size problem%s!\n", op); return;
case Z_DATA_ERROR: tell(0, "Error: Zipped input data corrupted%s! Details: %s\n", op, msg); return;
case Z_STREAM_ERROR: tell(0, "Error: Invalid stream structure%s. Details: %s\n", op, msg); return;
default: tell(0, "Error: Couldn't unzip data for unknown reason (%6d)%s!\n", errorCode, op); return;
}
}
//*************************************************************************
// Host Data
//*************************************************************************
#include <sys/utsname.h>
#include <netdb.h>
#include <ifaddrs.h>
static struct utsname info;
const char* getHostName()
{
// get info from kernel
if (uname(&info) == -1)
return "";
return info.nodename;
}
const char* getFirstIp()
{
struct ifaddrs *ifaddr, *ifa;
static char host[NI_MAXHOST] = "";
if (getifaddrs(&ifaddr) == -1)
{
tell(0, "getifaddrs() failed");
return "";
}
// walk through linked interface list
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr)
continue;
// For an AF_INET interfaces
if (ifa->ifa_addr->sa_family == AF_INET) // || ifa->ifa_addr->sa_family == AF_INET6)
{
int res = getnameinfo(ifa->ifa_addr,
(ifa->ifa_addr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, NI_MAXHOST, 0, 0, NI_NUMERICHOST);
if (res)
{
tell(0, "getnameinfo() failed: %s", gai_strerror(res));
return "";
}
// skip loopback interface
if (strcmp(host, "127.0.0.1") == 0)
continue;
tell(5, "%-8s %-15s %s", ifa->ifa_name, host,
ifa->ifa_addr->sa_family == AF_INET ? " (AF_INET)" :
ifa->ifa_addr->sa_family == AF_INET6 ? " (AF_INET6)" : "");
}
}
freeifaddrs(ifaddr);
return host;
}
#ifdef USELIBARCHIVE
//***************************************************************************
// unzip <file> and get data of first content which name matches <filter>
//***************************************************************************
int unzip(const char* file, const char* filter, char*& buffer, int& size, char* entryName)
{
const int step = 1024*10;
int bufSize = 0;
int r;
int res;
struct archive_entry* entry;
struct archive* a = archive_read_new();
*entryName = 0;
buffer = 0;
size = 0;
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
r = archive_read_open_filename(a, file, 10204);
if (r != ARCHIVE_OK)
{
tell(0, "Error: Open '%s' failed - %m", file);
return 1;
}
while (archive_read_next_header(a, &entry) == ARCHIVE_OK)
{
strcpy(entryName, archive_entry_pathname(entry));
if (strstr(entryName, filter))
{
bufSize = step;
buffer = (char*)malloc(bufSize+1);
while ((res = archive_read_data(a, buffer+size, step)) > 0)
{
size += res;
bufSize += step;
buffer = (char*)realloc(buffer, bufSize+1);
}
buffer[size] = 0;
break;
}
}
r = archive_read_free(a);
if (r != ARCHIVE_OK)
{
size = 0;
free(buffer);
return fail;
}
return size > 0 ? success : fail;
}
#endif
//***************************************************************************
// Class LogDuration
//***************************************************************************
#ifdef VDR_PLUGIN
# include <vdr/plugin.h>
LogDuration::LogDuration(const char* aMessage, int aLogLevel)
{
logLevel = aLogLevel;
strcpy(message, aMessage);
// at last !
durationStart = cTimeMs::Now();
}
LogDuration::~LogDuration()
{
tell(logLevel, "duration '%s' was (%dms)",
message, cTimeMs::Now() - durationStart);
}
void LogDuration::show(const char* label)
{
tell(logLevel, "elapsed '%s' at '%s' was (%dms)",
message, label, cTimeMs::Now() - durationStart);
}
#endif
//***************************************************************************
// Get Unique ID
//***************************************************************************
#ifdef USEUUID
const char* getUniqueId()
{
static char uuid[sizeUuid+TB] = "";
uuid_t id;
uuid_generate(id);
uuid_unparse_upper(id, uuid);
return uuid;
}
#endif // USEUUID
//***************************************************************************
// Create MD5
//***************************************************************************
#ifdef USEMD5
int createMd5(const char* buf, md5* md5)
{
MD5_CTX c;
unsigned char out[MD5_DIGEST_LENGTH];
MD5_Init(&c);
MD5_Update(&c, buf, strlen(buf));
MD5_Final(out, &c);
for (int n = 0; n < MD5_DIGEST_LENGTH; n++)
sprintf(md5+2*n, "%02x", out[n]);
md5[sizeMd5] = 0;
return done;
}
int createMd5OfFile(const char* path, const char* name, md5* md5)
{
FILE* f;
char buffer[1000];
int nread = 0;
MD5_CTX c;
unsigned char out[MD5_DIGEST_LENGTH];
char* file = 0;
asprintf(&file, "%s/%s", path, name);
if (!(f = fopen(file, "r")))
{
tell(0, "Fatal: Can't access '%s'; %m", file);
free(file);
return fail;
}
free(file);
MD5_Init(&c);
while ((nread = fread(buffer, 1, 1000, f)) > 0)
MD5_Update(&c, buffer, nread);
fclose(f);
MD5_Final(out, &c);
for (int n = 0; n < MD5_DIGEST_LENGTH; n++)
sprintf(md5+2*n, "%02x", out[n]);
md5[sizeMd5] = 0;
return success;
}
#endif // USEMD5

179
lib/common.h Normal file
View File

@ -0,0 +1,179 @@
/*
* common.h: EPG2VDR plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __COMMON_H
#define __COMMON_H
#include <stdint.h> // uint_64_t
#include <stdlib.h>
#include <string>
#include <openssl/md5.h> // MD5_*
#ifdef VDR_PLUGIN
# include <vdr/tools.h>
#endif
#ifdef USELIBXML
# include <libxslt/transform.h>
# include <libxslt/xsltutils.h>
# include <libexslt/exslt.h>
#endif
//***************************************************************************
//
//***************************************************************************
#ifndef VDR_PLUGIN
inline long min(long a, long b) { return a < b ? a : b; }
inline long max(long a, long b) { return a > b ? a : b; }
#endif
enum Misc
{
success = 0,
done = success,
fail = -1,
na = -1,
ignore = -2,
all = -3,
abrt = -4,
yes = 1,
on = 1,
off = 0,
no = 0,
TB = 1,
sizeMd5 = 2 * MD5_DIGEST_LENGTH,
sizeUuid = 36,
tmeSecondsPerMinute = 60,
tmeSecondsPerHour = tmeSecondsPerMinute * 60,
tmeSecondsPerDay = 24 * tmeSecondsPerHour
};
//***************************************************************************
// Tell
//***************************************************************************
void tell(int eloquence, const char* format, ...);
//***************************************************************************
// MemoryStruct for curl callbacks
//***************************************************************************
struct MemoryStruct
{
MemoryStruct() { memory = 0; clear(); }
~MemoryStruct() { clear(); }
// data
char* memory;
size_t size;
// tag attribute
char tag[100]; // the tag to be compared
char name[100]; // content name (filename)
int headerOnly;
int isEmpty() { return memory == 0; }
void clear()
{
free(memory);
memory = 0;
size = 0;
*tag = 0;
*name = 0;
headerOnly = no;
}
};
//***************************************************************************
// Tools
//***************************************************************************
unsigned int getHostId();
const char* getHostName();
const char* getFirstIp();
#ifdef USEUUID
const char* getUniqueId();
#endif
void removeChars(std::string& str, const char* ignore);
void removeCharsExcept(std::string& str, const char* except);
void removeWord(std::string& pattern, std::string word);
void prepareCompressed(std::string& pattern);
char* rTrim(char* buf);
char* lTrim(char* buf);
char* allTrim(char* buf);
char* sstrcpy(char* dest, const char* src, int max);
std::string num2Str(int num);
std::string l2pTime(time_t t);
std::string ms2Dur(uint64_t t);
const char* c2s(char c, char* buf);
int fileExists(const char* path);
int fileSize(const char* path);
int createLink(const char* link, const char* dest, int force);
int isLink(const char* path);
int isEmpty(const char* str);
int removeFile(const char* filename);
int chkDir(const char* path);
#ifdef USELIBXML
xsltStylesheetPtr loadXSLT(const char* name, const char* path, int utf8);
#endif
#ifdef USEMD5
typedef char md5Buf[sizeMd5+TB];
typedef char md5;
int createMd5(const char* buf, md5* md5);
int createMd5OfFile(const char* path, const char* name, md5* md5);
#endif
//***************************************************************************
// Zip
//***************************************************************************
int gunzip(MemoryStruct* zippedData, MemoryStruct* unzippedData);
void tellZipError(int errorCode, const char* op, const char* msg);
#ifdef USELIBARCHIVE
int unzip(const char* file, const char* filter, char*& buffer,
int& size, char* entryName);
#endif
#ifdef VDR_PLUGIN
//***************************************************************************
// Log Duration
//***************************************************************************
class LogDuration
{
public:
LogDuration(const char* aMessage, int aLogLevel = 2);
~LogDuration();
void show(const char* label = "");
protected:
char message[1000];
uint64_t durationStart;
int logLevel;
};
#endif
//***************************************************************************
#endif //___COMMON_H

63
lib/config.c Normal file
View File

@ -0,0 +1,63 @@
/*
* config.c:
*
* See the README file for copyright information and how to reach the author.
*
*/
#include <string.h>
#include "common.h"
#include "config.h"
cEPG2VDRConfig EPG2VDRConfig;
cEPG2VDRConfig::cEPG2VDRConfig(void)
{
mainmenuVisible = yes;
mainmenuFullupdate = 0;
useproxy = no;
sstrcpy(httpproxy, "127.0.0.1:8000", sizeof(httpproxy));
sstrcpy(username, "", sizeof(username));
sstrcpy(password, "", sizeof(password));
checkInitial = yes;
updatetime = 6; // hours
days = 8;
upddays = 2;
storeXmlToFs = no;
blacklist = no;
masterMode = 0;
getepgimages = yes;
maximagesperevent = 1;
epgImageSize = 2;
seriesEnabled = yes;
sstrcpy(seriesUrl, "eplists.constabel.net", sizeof(seriesUrl));
seriesPort = 2006;
storeSeriesToFs = no;
#ifdef VDR_PLUGIN
activeOnEpgd = no;
scheduleBoot = no;
#else
sstrcpy(cachePath, "/var/cache/epgd", sizeof(cachePath));
sstrcpy(pluginPath, PLGDIR, sizeof(pluginPath));
sstrcpy(epgView, "eventsview.sql", sizeof(epgView));
updateThreshold = 200;
maintanance = no;
#endif
sstrcpy(dbHost, "localhost", sizeof(dbHost));
dbPort = 3306;
sstrcpy(dbName, "epg2vdr", sizeof(dbName));
sstrcpy(dbUser, "epg2vdr", sizeof(dbUser));
sstrcpy(dbPass, "epg", sizeof(dbPass));
logstdout = no;
loglevel = 1;
uuid[0] = 0;
}

73
lib/config.h Normal file
View File

@ -0,0 +1,73 @@
/*
* config.h:
*
* See the README file for copyright information and how to reach the author.
*
* $Id: config.h,v 1.2 2012/10/26 08:44:13 wendel Exp $
*/
#ifndef __EPG2VDR_CONFIG_H
#define __EPG2VDR_CONFIG_H
#include "common.h"
//***************************************************************************
// Config
//***************************************************************************
struct cEPG2VDRConfig
{
public:
cEPG2VDRConfig(void);
int useproxy;
char httpproxy[256+TB];
char username[100+TB];
char password[100+TB];
int checkInitial;
int updatetime;
int days;
int upddays;
int storeXmlToFs;
int blacklist; // to enable noepg feature
int getepgimages;
int maximagesperevent;
int epgImageSize;
int seriesEnabled;
char seriesUrl[500+TB];
int seriesPort;
int storeSeriesToFs;
#ifdef VDR_PLUGIN
int activeOnEpgd;
int scheduleBoot;
#else
char cachePath[256+TB];
char pluginPath[256+TB];
char epgView[100+TB];
int updateThreshold;
int maintanance;
#endif
char dbHost[100+TB];
int dbPort;
char dbName[100+TB];
char dbUser[100+TB];
char dbPass[100+TB];
int logstdout;
int loglevel;
int mainmenuVisible;
int mainmenuFullupdate;
int masterMode;
char uuid[sizeUuid+TB];
};
extern cEPG2VDRConfig EPG2VDRConfig;
#endif // __EPG2VDR_CONFIG_H

1086
lib/db.c Normal file

File diff suppressed because it is too large Load Diff

1030
lib/db.h Normal file

File diff suppressed because it is too large Load Diff

856
lib/tabledef.c Normal file
View File

@ -0,0 +1,856 @@
/*
* tabledef.c
*
* See the README file for copyright information and how to reach the author.
*
*/
#include "tabledef.h"
//***************************************************************************
// cEpgdState
//***************************************************************************
const char* cEpgdState::states[] =
{
"init",
"standby",
"stopped",
"busy (events)",
"busy (match)",
"busy (images)",
"busy (scraping)",
0
};
const char* cEpgdState::toName(cEpgdState::State s)
{
if (!isValid(s))
return "unknown";
return states[s];
}
cEpgdState::State cEpgdState::toState(const char* name)
{
for (int i = 0; i < esCount; i++)
if (strcmp(states[i], name) == 0)
return (State)i;
return esUnknown;
}
//***************************************************************************
// Event Fields
//***************************************************************************
//***************************************************************************
// Fields
//***************************************************************************
cDbService::FieldDef cTableEvents::fields[] =
{
// name format size index type
// primary key
{ "eventid", ffUInt, 0, fiEventId, ftPrimary },
{ "channelid", ffAscii, 50, fiChannelId, ftPrimary },
{ "masterid", ffUInt, 0, fiMasterId, ftAutoinc },
{ "useid", ffUInt, 0, fiUseId, ftData },
// meta
{ "source", ffAscii, 10, fiSource, ftMeta },
{ "fileref", ffAscii, 50, fiFileRef, ftMeta },
{ "inssp", ffInt, 10, fiInsSp, ftMeta },
{ "updsp", ffInt, 10, fiUpdSp, ftMeta },
{ "updflg", ffAscii, 1, fiUpdFlg, ftMeta },
{ "delflg", ffAscii, 1, fiDelFlg, ftMeta },
// vdr event data
{ "tableid", ffInt, 2, fiTableId, ftData },
{ "version", ffInt, 3, fiVersion, ftData },
{ "title", ffAscii, 200, fiTitle, ftData },
{ "comptitle", ffAscii, 200, fiCompTitle, ftData },
{ "shorttext", ffAscii, 300, fiShortText, ftData },
{ "compshorttext", ffAscii, 300, fiCompShortText, ftData },
{ "longdescription", ffText, 25000, fiLongDescription, ftData },
{ "starttime", ffInt, 10, fiStartTime, ftData },
{ "duration", ffInt, 5, fiDuration, ftData },
{ "parentalrating", ffInt, 2, fiParentalRating, ftData },
{ "vps", ffInt, 10, fiVps, ftData },
{ "description", ffText, 50000, fiDescription, ftCalc },
// additional external data
{ "shortdescription", ffAscii, 3000, fiShortDescription, ftData },
{ "actor", ffAscii, 3000, fiActor, ftData },
{ "audio", ffAscii, 50, fiAudio, ftData },
{ "category", ffAscii, 50, fiCategory, ftData },
{ "country", ffAscii, 50, fiCountry, ftData },
{ "director", ffAscii, 250, fiDirector, ftData },
{ "flags", ffAscii, 100, fiFlags, ftData },
{ "genre", ffAscii, 100, fiGenre, ftData },
{ "info", ffText, 10000, fiInfo, ftData },
{ "music", ffAscii, 250, fiMusic, ftData },
{ "producer", ffText, 1000, fiProducer, ftData },
{ "screenplay", ffAscii, 500, fiScreenplay, ftData },
{ "shortreview", ffAscii, 500, fiShortreview, ftData },
{ "tipp", ffAscii, 250, fiTipp, ftData },
{ "topic", ffAscii, 500, fiTopic, ftData },
{ "year", ffAscii, 10, fiYear, ftData },
{ "rating", ffAscii, 250, fiRating, ftData },
{ "fsk", ffAscii, 2, fiFsk, ftData },
{ "movieid", ffAscii, 20, fiMovieid, ftData },
{ "moderator", ffAscii, 250, fiModerator, ftData },
{ "other", ffText, 2000, fiOther, ftData },
{ "guest", ffText, 1000, fiGuest, ftData },
{ "camera", ffText, 1000, fiCamera, ftData },
{ "extepnum", ffInt, 4, fiExtEpNum, ftData },
{ "imagecount", ffInt, 2, fiImageCount, ftData },
// episodes (constable)
{ "episode", ffAscii, 250, fiEpisode, ftData },
{ "episodepart", ffAscii, 250, fiEpisodePart, ftData },
{ "episodelang", ffAscii, 3, fiEpisodeLang, ftData },
// tv scraper
{ "scrseriesid", ffInt, 11, fiScrSeriesId, ftData },
{ "scrseriesepisode", ffInt, 11, fiScrSeriesEpisode, ftData },
{ "scrmovieid", ffInt, 11, fiScrMovieId, ftData },
{ "scrsp", ffInt, 11, fiScrSp, ftData },
{ 0 }
};
cDbService::FieldDef* cTableEvents::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableEvents::indices[] =
{
// index fields
{ "comptitle", { fiCompTitle, na }, 0 },
{ "source", { fiSource, na }, 0 },
{ "FilerefSource", { fiFileRef, fiSource, na }, 0 },
{ "channelid", { fiChannelId, na }, 0 },
{ "useid", { fiUseId, na }, 0 },
{ "useidchannelid", { fiUseId, fiChannelId, na }, 0 },
{ "updflgupdsp", { fiUpdFlg, fiUpdSp, na }, 0 },
{ "idxsourcechannelid", { fiSource, fiChannelId, na }, 0 },
{ 0 }
};
//***************************************************************************
// Components
//***************************************************************************
cDbService::FieldDef cTableComponents::fields[] =
{
// name format size index type
{ "eventid", ffUInt, 0, fiEventId, ftPrimary },
{ "channelid", ffAscii, 50, fiChannelId, ftPrimary },
{ "stream", ffInt, 3, fiStream, ftPrimary },
{ "type", ffInt, 3, fiType, ftPrimary },
{ "lang", ffAscii, 8, fiLang, ftPrimary },
{ "description", ffAscii, 100, fiDescription, ftPrimary },
{ "inssp", ffInt, 0, fiInsSp, ftMeta },
{ "updsp", ffInt, 0, fiUpdSp, ftMeta },
{ 0 }
};
//***************************************************************************
// File References
//***************************************************************************
cDbService::FieldDef cTableFileRefs::fields[] =
{
// name format size index type
{ "name", ffAscii, 100, fiName, ftPrimary },
{ "source", ffAscii, 10, fiSource, ftPrimary },
{ "inssp", ffInt, 0, fiInsSp, ftMeta },
{ "updsp", ffInt, 0, fiUpdSp, ftMeta },
{ "extid", ffAscii, 10, fiExternalId, ftData },
{ "fileref", ffAscii, 100, fiFileRef, ftData }, // name + '-' + tag
{ "tag", ffAscii, 100, fiTag, ftData },
{ 0 }
};
cDbService::IndexDef cTableFileRefs::indices[] =
{
// index fields
{ "SourceFileref", { fiSource, fiFileRef, na }, 0 },
{ "Fileref", { fiFileRef, na }, 0 },
{ 0 }
};
//***************************************************************************
// Image Ref Fields
//***************************************************************************
cDbService::FieldDef cTableImageRefs::fields[] =
{
// name format size index type
{ "eventid", ffUInt, 0, fiEventId, ftPrimary },
{ "lfn", ffInt, 0, fiLfn, ftPrimary },
{ "inssp", ffInt, 0, fiInsSp, ftMeta },
{ "updsp", ffInt, 0, fiUpdSp, ftMeta },
{ "source", ffAscii, 10, fiSource, ftMeta },
{ "fileref", ffAscii, 100, fiFileRef, ftData },
{ "imagename", ffAscii, 100, fiImgName, ftData },
{ 0 }
};
cDbService::IndexDef cTableImageRefs::indices[] =
{
// index fields
{ "lfn", { fiLfn, na }, 0 },
{ "name", { fiImgName, na }, 0 },
{ 0 }
};
//***************************************************************************
// Image Fields
//***************************************************************************
cDbService::FieldDef cTableImages::fields[] =
{
// name format size index type
{ "imagename", ffAscii, 100, fiImgName, ftPrimary },
{ "inssp", ffInt, 0, fiInsSp, ftMeta },
{ "updsp", ffInt, 0, fiUpdSp, ftMeta },
{ "image", ffMlob, 200000, fiImage, ftData },
{ 0 }
};
//***************************************************************************
// Series Episode Fields
//***************************************************************************
cDbService::FieldDef cTableEpisodes::fields[] =
{
// name format size index type
// primary key
{ "compname", ffAscii, 100, fiCompName, ftPrimary }, // episode name compressed
{ "comppartname", ffAscii, 200, fiCompPartName, ftPrimary }, // part name compressed
{ "lang", ffAscii, 10, fiLang, ftPrimary },
{ "inssp", ffInt, 0, fiInsSp, ftMeta },
{ "updsp", ffInt, 0, fiUpdSp, ftMeta },
{ "link", ffInt, 0, fiLink, ftData },
// episode data
{ "shortname", ffAscii, 100, fiShortName, ftData },
{ "episodename", ffAscii, 100, fiEpisodeName, ftData }, // episode name
// part data
{ "partname", ffAscii, 300, fiPartName, ftData }, // part name
{ "season", ffInt, 0, fiSeason, ftData },
{ "part", ffInt, 0, fiPart, ftData },
{ "parts", ffInt, 0, fiParts, ftData },
{ "number", ffInt, 0, fiNumber, ftData },
{ "extracol1", ffAscii, 250, fiExtraCol1, ftData },
{ "extracol2", ffAscii, 250, fiExtraCol2, ftData },
{ "extracol3", ffAscii, 250, fiExtraCol3, ftData },
{ "comment", ffAscii, 250, fiComment, ftData },
{ 0 }
};
cDbService::IndexDef cTableEpisodes::indices[] =
{
// index fields
{ "updsp", { fiUpdSp, na }, 0 },
{ 0 }
};
//***************************************************************************
// Channel Map Fields
//***************************************************************************
cDbService::FieldDef cTableChannelMap::fields[] =
{
// name format size index type
{ "extid", ffAscii, 10, fiExternalId, ftPrimary },
{ "channelid", ffAscii, 50, fiChannelId, ftPrimary },
{ "source", ffAscii, 20, fiSource, ftPrimary },
{ "channelname", ffAscii, 100, fiChannelName, ftData },
{ "vps", ffInt, 0, fiVps, ftData },
{ "merge", ffInt, 0, fiMerge, ftData },
{ "mergesp", ffInt, 0, fiMergeSp, ftData },
{ "inssp", ffInt, 0, fiInsSp, ftMeta },
{ "updsp", ffInt, 0, fiUpdSp, ftMeta },
{ "updflg", ffAscii, 1, fiUpdFlg, ftMeta },
{ 0 }
};
cDbService::IndexDef cTableChannelMap::indices[] =
{
// index fields
{ "sourceExtid", { fiSource, fiExternalId, na }, 0 },
{ "source", { fiSource, na }, 0 },
{ "updflg", { fiUpdFlg, na }, 0 },
{ "idxsourcechannelid", { fiSource, fiChannelId, na }, 0 },
{ 0 }
};
//***************************************************************************
// VDRs Fields
//***************************************************************************
cDbService::FieldDef cTableVdrs::fields[] =
{
// name format size index type
{ "uuid", ffAscii, 40, fiUuid, ftPrimary },
{ "inssp", ffInt, 0, fiInsSp, ftMeta },
{ "updsp", ffInt, 0, fiUpdSp, ftMeta },
{ "name", ffAscii, 100, fiName, ftData },
{ "version", ffAscii, 100, fiVersion, ftData },
{ "dbapi", ffUInt, 0, fiDbApi, ftData },
{ "lastupd", ffInt, 0, fiLastUpdate, ftData },
{ "nextupd", ffInt, 0, fiNextUpdate, ftData },
{ "state", ffAscii, 20, fiState, ftData },
{ "master", ffAscii, 1, fiMaster, ftData },
{ "ip", ffAscii, 20, fiIp, ftData },
{ 0 }
};
//***************************************************************************
// Parameter Fields
//***************************************************************************
cDbService::FieldDef cTableParameters::fields[] =
{
// name format size index type
{ "owner", ffAscii, 40, fiOwner, ftPrimary },
{ "name", ffAscii, 40, fiName, ftPrimary },
{ "inssp", ffInt, 0, fiInsSp, ftMeta },
{ "updsp", ffInt, 0, fiUpdSp, ftMeta },
{ "value", ffAscii, 100, fiValue, ftData },
{ 0 }
};
//***************************************************************************
// Analyse
//***************************************************************************
cDbService::FieldDef cTableAnalyse::fields[] =
{
// name format size index type
{ "channelid", ffAscii, 50, fiChannelId, ftPrimary },
{ "vdr_masterid", ffUInt, 0, fiVdrMasterId, ftData },
{ "vdr_eventid", ffUInt, 0, fiVdrEventId, ftPrimary },
{ "vdr_starttime", ffInt, 10, fiVdrStartTime, ftData },
{ "vdr_duration", ffInt, 5, fiVdrDuration, ftData },
{ "vdr_title", ffAscii, 200, fiVdrTitle, ftData },
{ "vdr_shorttext", ffAscii, 300, fiVdrShortText, ftData },
{ "ext_masterid", ffUInt, 0, fiExtMasterId, ftData },
{ "ext_eventid", ffUInt, 0, fiExtEventId, ftData },
{ "ext_starttime", ffInt, 10, fiExtStartTime, ftData },
{ "ext_duration", ffInt, 5, fiExtDuration, ftData },
{ "ext_title", ffAscii, 200, fiExtTitle, ftData },
{ "ext_shorttext", ffAscii, 300, fiExtShortText, ftData },
{ "ext_episode", ffAscii, 1, fiExtEpisode, ftData },
{ "ext_merge", ffInt, 11, fiExtMerge, ftData },
{ "ext_images", ffAscii, 1, fiExiImages, ftData },
{ "lvmin", ffInt, 3, fiLvMin, ftData },
{ "rank", ffInt, 5, fiRank, ftData },
{ 0 }
};
cDbService::IndexDef cTableAnalyse::indices[] =
{
// index fields
{ "vdr_masterid", { fiVdrMasterId, na }, 0 },
{ 0 }
};
//***************************************************************************
// Snapshot
//***************************************************************************
cDbService::FieldDef cTableSnapshot::fields[] =
{
// name format size index type
{ "channelid", ffAscii, 50, fiChannelId, ftData },
{ "source", ffAscii, 10, fiSource, ftData },
{ "masterid", ffUInt, 0, fiVdrMasterId, ftData },
{ "eventid", ffUInt, 0, fiEventId, ftData },
{ "useid", ffUInt, 0, fiUseId, ftData },
{ "starttime", ffInt, 10, fiStartTime, ftData },
{ "duration", ffInt, 5, fiDuration, ftData },
{ "title", ffAscii, 200, fiTitle, ftData },
{ "comptitle", ffAscii, 200, fiCompTitle, ftData },
{ "shorttext", ffAscii, 300, fiShortText, ftData },
{ "compshorttext", ffAscii, 300, fiCompShortText, ftData },
{ "updsp", ffInt, 10, fiUpdsp, ftData },
{ "episode", ffAscii, 1, fiEpisode, ftData },
{ "merge", ffInt, 0, fiMerge, ftData },
{ "images", ffAscii, 1, fiImages, ftData },
{ 0 }
};
cDbService::IndexDef cTableSnapshot::indices[] =
{
// index fields
{ "channelid", { fiChannelId, na }, 0 },
{ "starttimeSource", { fiStartTime, fiSource, na }, 0 },
{ 0 }
};
//***************************************************************************
// Series Fields
//***************************************************************************
cDbService::FieldDef cTableSeries::fields[] =
{
// name format size index type
// primary key
{ "series_id", ffUInt, 0, fiSeriesId, ftPrimary },
//Data
{ "series_name", ffAscii, 200, fiSeriesName, ftData },
{ "series_last_scraped", ffUInt, 0, fiSeriesLastScraped, ftData },
{ "series_last_updated", ffUInt, 0, fiSeriesLastUpdated, ftData },
{ "series_overview", ffText, 10000, fiSeriesOverview, ftData },
{ "series_firstaired", ffAscii, 50, fiSeriesFirstAired, ftData },
{ "series_network", ffAscii, 100, fiSeriesNetwork, ftData },
{ "series_imdb_id", ffAscii, 20, fiSeriesIMDBId, ftData },
{ "series_genre", ffAscii, 100, fiSeriesGenre, ftData },
{ "series_rating", ffFloat, 31, fiSeriesRating, ftData },
{ "series_status", ffAscii, 50, fiSeriesStatus, ftData },
{ 0 }
};
cDbService::FieldDef* cTableSeries::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableSeries::indices[] =
{
// index fields
{ 0 }
};
//***************************************************************************
// SeriesEpisode Fields
//***************************************************************************
cDbService::FieldDef cTableSeriesEpisode::fields[] =
{
// name format size index type
// primary key
{ "episode_id", ffUInt, 0, fiEpisodeId, ftPrimary },
//Data
{ "episode_number", ffUInt, 0, fiEpisodeNumber, ftData },
{ "season_number", ffUInt, 0, fiSeasonNumber, ftData },
{ "episode_name", ffAscii, 300, fiEpisodeName, ftData },
{ "episode_overview", ffText, 10000, fiEpisodeOverview, ftData },
{ "episode_firstaired", ffAscii, 20, fiEpisodeFirstAired, ftData },
{ "episode_gueststars", ffAscii, 1000, fiEpisodeGuestStars, ftData },
{ "episode_rating", ffFloat, 31, fiEpisodeRating, ftData },
{ "episode_last_updated", ffUInt, 0, fiEpisodeLastUpdated, ftData },
{ "series_id", ffUInt, 0, fiSeriesId, ftData },
{ 0 }
};
cDbService::FieldDef* cTableSeriesEpisode::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableSeriesEpisode::indices[] =
{
// index fields
{ "series_id", { fiSeriesId, na }, 0 },
{ 0 }
};
//***************************************************************************
// SeriesMedia Fields
//***************************************************************************
cDbService::FieldDef cTableSeriesMedia::fields[] =
{
// name format size index type
// primary key
{ "series_id", ffUInt, 0, fiSeriesId, ftPrimary },
{ "season_number", ffUInt, 0, fiSeasonNumber, ftPrimary },
{ "episode_id", ffUInt, 0, fiEpisodeId, ftPrimary },
{ "actor_id", ffUInt, 0, fiActorId, ftPrimary },
{ "media_type", ffUInt, 0, fiMediaType, ftPrimary },
//Data
{ "media_url", ffAscii, 100, fiMediaUrl, ftData },
{ "media_width", ffUInt, 0, fiMediaWidth, ftData },
{ "media_height", ffUInt, 0, fiMediaHeight, ftData },
{ "media_rating", ffFloat, 31, fiMediaRating, ftData },
{ "media_content", ffMlob, 1000000, fiMediaContent, ftData },
{ 0 }
};
cDbService::FieldDef* cTableSeriesMedia::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableSeriesMedia::indices[] =
{
// index fields
{ "series_id", { fiSeriesId, na }, 0 },
{ "season_number", { fiSeasonNumber, na }, 0 },
{ "episode_id", { fiEpisodeId, na }, 0 },
{ "actor_id", { fiActorId, na }, 0 },
{ 0 }
};
//***************************************************************************
// SeriesActor Fields
//***************************************************************************
cDbService::FieldDef cTableSeriesActor::fields[] =
{
// name format size index type
// primary key
{ "actor_id", ffUInt, 0, fiActorId, ftPrimary },
//Data
{ "actor_name", ffAscii, 100, fiActorName, ftData },
{ "actor_role", ffAscii, 500, fiActorRole, ftData },
{ "actor_sortorder", ffUInt, 0, fiSortOrder, ftData },
{ 0 }
};
cDbService::FieldDef* cTableSeriesActor::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableSeriesActor::indices[] =
{
// index fields
{ 0 }
};
//***************************************************************************
// Movie Fields
//***************************************************************************
cDbService::FieldDef cTableMovies::fields[] =
{
// name format size index type
// primary key
{ "movie_id", ffUInt, 0, fiMovieId, ftPrimary },
//Data
{ "movie_title", ffAscii, 300, fiTitle, ftData },
{ "movie_original_title", ffAscii, 300, fiOriginalTitle, ftData },
{ "movie_tagline", ffAscii, 1000, fiTagline, ftData },
{ "movie_overview", ffText, 5000, fiOverview, ftData },
{ "movie_adult", ffUInt, 0, fiIsAdult, ftData },
{ "movie_collection_id", ffUInt, 0, fiCollectionId, ftData },
{ "movie_collection_name", ffAscii, 300, fiCollectionName, ftData },
{ "movie_budget", ffUInt, 0, fiBudget, ftData },
{ "movie_revenue", ffUInt, 0, fiRevenue, ftData },
{ "movie_genres", ffAscii, 500, fiGenres, ftData },
{ "movie_homepage", ffAscii, 300, fiHomepage, ftData },
{ "movie_release_date", ffAscii, 20, fiReleaaseDate, ftData },
{ "movie_runtime", ffUInt, 0, fiRuntime, ftData },
{ "movie_popularity", ffFloat, 31, fiPopularity, ftData },
{ "movie_vote_average", ffFloat, 31, fiVoteAverage, ftData },
{ 0 }
};
cDbService::FieldDef* cTableMovies::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableMovies::indices[] =
{
// index fields
{ "movie_id", { fiMovieId, na }, 0 },
{ 0 }
};
//***************************************************************************
// MovieActor Fields
//***************************************************************************
cDbService::FieldDef cTableMovieActor::fields[] =
{
// name format size index type
// primary key
{ "actor_id", ffUInt, 0, fiActorId, ftPrimary },
//Data
{ "actor_name", ffAscii, 300, fiActorName, ftData },
{ 0 }
};
cDbService::FieldDef* cTableMovieActor::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableMovieActor::indices[] =
{
// index fields
{ "actor_id", { fiActorId, na }, 0 },
{ 0 }
};
//***************************************************************************
// MovieActors Fields
//***************************************************************************
cDbService::FieldDef cTableMovieActors::fields[] =
{
// name format size index type
// primary key
{ "movie_id", ffUInt, 0, fiMovieId, ftPrimary },
{ "actor_id", ffUInt, 0, fiActorId, ftPrimary },
//Data
{ "actor_role", ffAscii, 300, fiRole, ftData },
{ 0 }
};
cDbService::FieldDef* cTableMovieActors::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableMovieActors::indices[] =
{
// index fields
{ "movie_id", { fiMovieId, na }, 0 },
{ "actor_id", { fiActorId, na }, 0 },
{ 0 }
};
//***************************************************************************
// cTableMovieMedia Fields
//***************************************************************************
cDbService::FieldDef cTableMovieMedia::fields[] =
{
// name format size index type
// primary key
{ "movie_id", ffUInt, 0, fiMovieId, ftPrimary },
{ "actor_id", ffUInt, 0, fiActorId, ftPrimary },
{ "media_type", ffUInt, 0, fiMediaType, ftPrimary },
//Data
{ "media_url", ffAscii, 100, fiMediaUrl, ftData },
{ "media_width", ffUInt, 0, fiMediaWidth, ftData },
{ "media_height", ffUInt, 0, fiMediaHeight, ftData },
{ "media_content", ffMlob, 1000000, fiMediaContent, ftData },
{ 0 }
};
cDbService::FieldDef* cTableMovieMedia::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableMovieMedia::indices[] =
{
// index fields
{ "movie_id", { fiMovieId, na }, 0 },
{ "actor_id", { fiActorId, na }, 0 },
{ 0 }
};
//***************************************************************************
// cTableRecordings Fields
//***************************************************************************
cDbService::FieldDef cTableRecordings::fields[] =
{
// name format size index type
// primary key
{ "uuid", ffAscii, 40, fiUuid, ftPrimary },
{ "rec_path", ffAscii, 200, fiRecPath, ftPrimary },
{ "rec_start", ffUInt, 0, fiRecStart, ftPrimary },
//Data
{ "event_id", ffUInt, 0, fiEventId, ftData },
{ "channel_id", ffAscii, 50, fiChannelId, ftData },
{ "scrapinfo_movie_id", ffUInt, 0, fiScrapInfoMovieId, ftData },
{ "scrapinfo_series_id", ffUInt, 0, fiScrapInfoSeriesId, ftData },
{ "scrapinfo_episode_id", ffUInt, 0, fiScrapInfoEpisodeId, ftData },
{ "scrap_new", ffUInt, 0, fiScrapNew, ftData },
{ "rec_title", ffAscii, 200, fiRecTitle, ftData },
{ "rec_subtitle", ffAscii, 500, fiRecSubTitle, ftData },
{ "rec_duration", ffUInt, 0, fiRecDuration, ftData },
{ "movie_id", ffUInt, 0, fiMovieId, ftData },
{ "series_id", ffUInt, 0, fiSeriesId, ftData },
{ "episode_id", ffUInt, 0, fiEpisodeId, ftData },
{ 0 }
};
cDbService::FieldDef* cTableRecordings::toField(const char* name)
{
for (int i = 0; i < fiCount; i++)
if (strcmp(fields[i].name, name) == 0)
return &fields[i];
tell(0, "Request for unexpected field '%s', ignoring", name);
return 0;
}
cDbService::IndexDef cTableRecordings::indices[] =
{
// index fields
{ "uuid", { fiUuid, na }, 0 },
{ "rec_path", { fiRecPath, na }, 0 },
{ "rec_start", { fiRecStart, na }, 0 },
{ 0 }
};

832
lib/tabledef.h Normal file
View File

@ -0,0 +1,832 @@
/*
* tabledef.h
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __TABLEDEF_H
#define __TABLEDEF_H
#include "db.h"
//***************************************************************************
// cEpgdState
//***************************************************************************
class cEpgdState
{
public:
enum State
{
esUnknown = na,
esInit,
esStandby,
esStopped,
esBusy,
esBusyEvents = esBusy,
esBusyMatch,
esBusyImages,
esBusyScraping,
esCount
};
static const char* toName(State s);
static State toState(const char* name);
static int isValid(State s) { return s > esUnknown && s < esCount; }
static const char* states[];
};
typedef cEpgdState Es;
//***************************************************************************
// cUpdateState
//***************************************************************************
class cUpdateState
{
public:
enum State
{
// add to VDRs EPG
usActive = 'A',
usLink = 'L',
usPassthrough = 'P',
// remove from VDRs EPG
usChanged = 'C',
usDelete = 'D',
usRemove = 'R',
// don't care for VDRs EPG
usInactive = 'I',
usTarget = 'T'
};
// get lists for SQL 'in' statements
static const char* getDeletable() { return "'A','L','P','R','I'"; }
static const char* getNeeded() { return "'A','L','P','C','D','R'"; }
// checks fpr c++ code
static int isNeeded(char c) { return strchr("ALPCDR", c) != 0; }
static int isRemove(char c) { return strchr("CDR", c) != 0; }
};
typedef cUpdateState Us;
//***************************************************************************
// class cTableFileRef
//***************************************************************************
class cTableFileRefs : public cDbTable
{
public:
cTableFileRefs(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "fileref"; }
enum FieldIndex
{
fiName,
fiSource,
fiInsSp,
fiUpdSp,
fiExternalId,
fiFileRef,
fiTag,
fiCount
};
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableImageRef
//***************************************************************************
class cTableImageRefs : public cDbTable
{
public:
cTableImageRefs(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "imagerefs"; }
enum FieldIndex
{
fiEventId,
fiLfn,
fiInsSp,
fiUpdSp,
fiSource,
fiFileRef,
fiImgName,
fiCount
};
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableImage
//***************************************************************************
class cTableImages : public cDbTable
{
public:
cTableImages(cDbConnection* aConnection)
: cDbTable(aConnection, fields) { }
virtual const char* TableName() { return "images"; }
enum FieldIndex
{
fiImgName,
fiInsSp,
fiUpdSp,
fiImage,
fiCount
};
static FieldDef fields[];
};
//***************************************************************************
// class cTableEvent
//***************************************************************************
class cTableEvents : public cDbTable
{
public:
cTableEvents(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "events"; }
enum FieldIndex
{
fiEventId,
fiChannelId,
fiMasterId,
fiUseId,
fiSource,
fiFileRef,
fiInsSp,
fiUpdSp,
fiUpdFlg, // update flag
fiDelFlg, // deletion flag
fiTableId,
fiVersion,
fiTitle,
fiCompTitle, // compressed (without whitespace and special characters)
fiShortText,
fiCompShortText, // compressed (without whitespace and special characters)
fiLongDescription,
fiStartTime,
fiDuration,
fiParentalRating,
fiVps,
fiDescription, // view field, not stored!
fiShortDescription,
fiActor,
fiAudio,
fiCategory,
fiCountry,
fiDirector,
fiFlags,
fiGenre,
fiInfo,
fiMusic,
fiProducer,
fiScreenplay,
fiShortreview,
fiTipp,
fiTopic,
fiYear,
fiRating,
fiFsk,
fiMovieid,
fiModerator,
fiOther,
fiGuest,
fiCamera,
fiExtEpNum,
fiImageCount,
fiEpisode,
fiEpisodePart,
fiEpisodeLang,
fiScrSeriesId,
fiScrSeriesEpisode,
fiScrMovieId,
fiScrSp,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableComponent
//***************************************************************************
class cTableComponents : public cDbTable
{
public:
cTableComponents(cDbConnection* aConnection)
: cDbTable(aConnection, fields) { }
virtual const char* TableName() { return "components"; }
enum FieldIndex
{
fiEventId,
fiChannelId,
fiStream,
fiType,
fiLang,
fiDescription,
fiInsSp,
fiUpdSp
};
static FieldDef fields[];
};
//***************************************************************************
// class cTableEpisode
//***************************************************************************
class cTableEpisodes : public cDbTable
{
public:
cTableEpisodes(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "episodes"; }
enum FieldIndex
{
// primary key
fiCompName, // compressed name (without whitespace and special characters)
fiCompPartName, // " " "
fiLang, // "de", "en", ...
fiInsSp,
fiUpdSp,
fiLink,
// episode data
fiShortName,
fiEpisodeName, // episode name (fielname without path and suffix)
// part data
fiPartName, // part name
fiSeason,
fiPart,
fiParts,
fiNumber,
fiExtraCol1,
fiExtraCol2,
fiExtraCol3,
fiComment,
fiCount
};
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableChannelMap
//***************************************************************************
class cTableChannelMap : public cDbTable
{
public:
cTableChannelMap(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "channelmap"; }
enum FieldIndex
{
fiExternalId, //
fiChannelId, //
fiSource,
fiChannelName,
fiVps,
fiMerge,
fiMergeSp,
fiInsSp,
fiUpdSp,
fiUpdFlg,
fiCount
};
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableVdr
//***************************************************************************
class cTableVdrs : public cDbTable
{
public:
cTableVdrs(cDbConnection* aConnection)
: cDbTable(aConnection, fields) { }
virtual const char* TableName() { return "vdrs"; }
enum FieldIndex
{
fiUuid,
fiInsSp,
fiUpdSp,
fiName,
fiVersion,
fiDbApi,
fiLastUpdate,
fiNextUpdate,
fiState,
fiMaster,
fiIp,
fiCount
};
static FieldDef fields[];
};
//***************************************************************************
// class cTableParameters
//***************************************************************************
class cTableParameters : public cDbTable
{
public:
cTableParameters(cDbConnection* aConnection)
: cDbTable(aConnection, fields) { }
virtual const char* TableName() { return "parameters"; }
enum FieldIndex
{
fiOwner,
fiName,
fiInsSp,
fiUpdSp,
fiValue,
fiCount
};
static FieldDef fields[];
};
//***************************************************************************
// cTableAnalyse
//***************************************************************************
class cTableAnalyse : public cDbTable
{
public:
cTableAnalyse(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "analyse"; }
enum FieldIndex
{
fiChannelId,
fiVdrMasterId,
fiVdrEventId,
fiVdrStartTime,
fiVdrDuration,
fiVdrTitle,
fiVdrShortText,
fiExtMasterId,
fiExtEventId,
fiExtStartTime,
fiExtDuration,
fiExtTitle,
fiExtShortText,
fiExtEpisode,
fiExtMerge,
fiExiImages,
fiLvMin,
fiRank,
fiCount
};
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// cTableSnapshot
//***************************************************************************
class cTableSnapshot : public cDbTable
{
public:
cTableSnapshot(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "snapshot"; }
enum FieldIndex
{
fiChannelId,
fiSource,
fiVdrMasterId,
fiEventId,
fiUseId,
fiStartTime,
fiDuration,
fiTitle,
fiCompTitle,
fiShortText,
fiCompShortText,
fiUpdsp,
fiEpisode,
fiMerge,
fiImages,
fiCount
};
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableSeries
//***************************************************************************
class cTableSeries : public cDbTable
{
public:
cTableSeries(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "series"; }
enum FieldIndex
{
fiSeriesId,
fiSeriesName,
fiSeriesLastScraped,
fiSeriesLastUpdated,
fiSeriesOverview,
fiSeriesFirstAired,
fiSeriesNetwork,
fiSeriesIMDBId,
fiSeriesGenre,
fiSeriesRating,
fiSeriesStatus,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableSeriesEpisode
//***************************************************************************
class cTableSeriesEpisode : public cDbTable
{
public:
cTableSeriesEpisode(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "series_episode"; }
enum FieldIndex
{
fiEpisodeId,
fiEpisodeNumber,
fiSeasonNumber,
fiEpisodeName,
fiEpisodeOverview,
fiEpisodeFirstAired,
fiEpisodeGuestStars,
fiEpisodeRating,
fiEpisodeLastUpdated,
fiSeriesId,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableSeriesMedia
//***************************************************************************
class cTableSeriesMedia : public cDbTable
{
public:
cTableSeriesMedia(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "series_media"; }
enum FieldIndex
{
fiSeriesId,
fiSeasonNumber,
fiEpisodeId,
fiActorId,
fiMediaType,
fiMediaUrl,
fiMediaWidth,
fiMediaHeight,
fiMediaRating,
fiMediaContent,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableSeriesActor
//***************************************************************************
class cTableSeriesActor : public cDbTable
{
public:
cTableSeriesActor(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "series_actor"; }
enum FieldIndex
{
fiActorId,
fiActorName,
fiActorRole,
fiSortOrder,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableMovies
//***************************************************************************
class cTableMovies : public cDbTable
{
public:
cTableMovies(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "movie"; }
enum FieldIndex
{
fiMovieId,
fiTitle,
fiOriginalTitle,
fiTagline,
fiOverview,
fiIsAdult,
fiCollectionId,
fiCollectionName,
fiBudget,
fiRevenue,
fiGenres,
fiHomepage,
fiReleaaseDate,
fiRuntime,
fiPopularity,
fiVoteAverage,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableMovieActor
//***************************************************************************
class cTableMovieActor : public cDbTable
{
public:
cTableMovieActor(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "movie_actor"; }
enum FieldIndex
{
fiActorId,
fiActorName,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableMovieActors
//***************************************************************************
class cTableMovieActors : public cDbTable
{
public:
cTableMovieActors(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "movie_actors"; }
enum FieldIndex
{
fiMovieId,
fiActorId,
fiRole,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableMovieMedia
//***************************************************************************
class cTableMovieMedia : public cDbTable
{
public:
cTableMovieMedia(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "movie_media"; }
enum FieldIndex
{
fiMovieId,
fiActorId,
fiMediaType,
fiMediaUrl,
fiMediaWidth,
fiMediaHeight,
fiMediaContent,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
//***************************************************************************
// class cTableRecordings
//***************************************************************************
class cTableRecordings : public cDbTable
{
public:
cTableRecordings(cDbConnection* aConnection)
: cDbTable(aConnection, fields, indices) { }
virtual const char* TableName() { return "recordings"; }
enum FieldIndex
{
fiUuid,
fiRecPath,
fiRecStart,
fiEventId,
fiChannelId,
fiScrapInfoMovieId,
fiScrapInfoSeriesId,
fiScrapInfoEpisodeId,
fiScrapNew,
fiRecTitle,
fiRecSubTitle,
fiRecDuration,
fiMovieId,
fiSeriesId,
fiEpisodeId,
fiCount
};
static FieldDef* toField(const char* name);
static FieldDef fields[];
static IndexDef indices[];
};
#endif //__TABLEDEF_H

130
moviedbmovie.c Normal file
View File

@ -0,0 +1,130 @@
#define __STL_CONFIG_H
#include "lib/common.h"
#include "moviedbmovie.h"
using namespace std;
cMovieDbMovie::cMovieDbMovie(void) {
title = "";
originalTitle = "";
tagline = "";
overview = "";
adult = false;
collectionID = 0;
collectionName = "";
budget = 0;
revenue = 0;
genres = "";
homepage = "";
imdbid = "";
releaseDate = "";
runtime = 0;
popularity = 0.0;
voteAverage = 0.0;
}
cMovieDbMovie::~cMovieDbMovie() {
for (map<int, cMovieActor*>::iterator it = actors.begin(); it != actors.end(); it++) {
cMovieActor *a = (cMovieActor*)it->second;
delete a;
}
for (map<int, cMovieMedia*>::iterator it = medias.begin(); it != medias.end(); it++) {
cMovieMedia *m = (cMovieMedia*)it->second;
delete m;
}
}
void cMovieDbMovie::InsertMedia(cMovieMedia *media) {
medias.insert(pair<int, cMovieMedia*>(media->mediaType, media));
}
void cMovieDbMovie::InsertActor(cMovieActor *actor) {
cMovieMedia *m = new cMovieMedia();
actor->actorThumb = m;
actors.insert(pair<int, cMovieActor*>(actor->id, actor));
}
vector<int> cMovieDbMovie::GetActorIDs(void) {
vector<int> IDs;
for (map<int, cMovieActor*>::iterator it = actors.begin(); it != actors.end(); it++) {
cMovieActor *a = it->second;
IDs.push_back(a->id);
}
return IDs;
}
void cMovieDbMovie::SetActorThumbSize(int actorId, int imgWidth, int imgHeight) {
map<int, cMovieActor*>::iterator hit = actors.find(actorId);
if (hit != actors.end()) {
cMovieActor *a = hit->second;
if (!a->actorThumb)
return;
cMovieMedia *thumb = a->actorThumb;
thumb->width = imgWidth;
thumb->height = imgHeight;
thumb->mediaType = mmActorThumb;
}
}
void cMovieDbMovie::SetActorPath(int actorId, string path) {
map<int, cMovieActor*>::iterator hit = actors.find(actorId);
if (hit != actors.end()) {
cMovieActor *a = hit->second;
if (!a->actorThumb)
return;
a->actorThumb->path = path;
}
}
bool cMovieDbMovie::GetMedia(mediaMovies mediatype, cTvMedia *p) {
map<int, cMovieMedia*>::iterator hit = medias.find(mediatype);
if (hit == medias.end())
return false;
cMovieMedia *pStored = hit->second;
p->path = pStored->path;
p->width = pStored->width;
p->height = pStored->height;
return true;
}
void cMovieDbMovie::GetActors(vector<cActor> *a) {
for (map<int, cMovieActor*>::iterator it = actors.begin(); it != actors.end(); it++) {
cMovieActor *aStored = it->second;
cActor act;
act.name = aStored->name;
act.role = aStored->role;
if (aStored->actorThumb) {
act.actorThumb.width = aStored->actorThumb->width;
act.actorThumb.height = aStored->actorThumb->height;
act.actorThumb.path = aStored->actorThumb->path;
}
a->push_back(act);
}
}
void cMovieDbMovie::Dump(void) {
tell(0, "--------------------------- Movie Info ----------------------------------");
tell(0, "title %s, ID: %d", title.c_str(), id);
tell(0, "Orig. Title: %s", originalTitle.c_str());
tell(0, "Tagline: %s", tagline.c_str());
tell(0, "Overview: %s", overview.c_str());
tell(0, "Collection: %s", collectionName.c_str());
tell(0, "Genre: %s", genres.c_str());
tell(0, "Popularity: %f", popularity);
tell(0, "--------------------------- Actors ----------------------------------");
for (map<int, cMovieActor*>::iterator it = actors.begin(); it != actors.end(); it++) {
cMovieActor *a = it->second;
tell(0, "Actor %d, Name: %s, Role %s", a->id, a->name.c_str(), a->role.c_str());
if (a->actorThumb) {
tell(0, "thmbWidth %d, thmbHeight %d", a->actorThumb->width, a->actorThumb->height);
tell(0, "Path %s", a->actorThumb->path.c_str());
}
}
tell(0, "--------------------------- Media ----------------------------------");
for (map<int, cMovieMedia*>::iterator it = medias.begin(); it != medias.end(); it++) {
cMovieMedia *m = it->second;
tell(0, "Media %d", m->mediaType);
tell(0, "width %d, height %d", m->width, m->height);
tell(0, "Path %s", m->path.c_str());
}
}

98
moviedbmovie.h Normal file
View File

@ -0,0 +1,98 @@
#ifndef __TVSCRAPER_MOVIEDBMOVIE_H
#define __TVSCRAPER_MOVIEDBMOVIE_H
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <algorithm>
#include "services.h"
using namespace std;
enum mediaMovies {
mmPoster,
mmFanart,
mmCollectionPoster,
mmCollectionFanart,
mmActorThumb,
mmPosterThumb,
};
// --- cMovieMedia -------------------------------------------------------------
class cMovieMedia {
public:
cMovieMedia(void) {
path = "";
mediaType = mmPoster;
width = 0;
height = 0;
};
~cMovieMedia(void) {
};
string path;
int mediaType;
int width;
int height;
};
// --- cMovieActor -------------------------------------------------------------
class cMovieActor {
public:
cMovieActor(void) {
id = 0;
name = "";
role = "";
actorThumb = NULL;
};
~cMovieActor(void) {
if (actorThumb)
delete actorThumb;
};
int id;
string name;
string role;
cMovieMedia *actorThumb;
};
// --- cMovieDbMovie -------------------------------------------------------------
class cMovieDbMovie {
private:
map<int, cMovieActor*> actors;
map<int, cMovieMedia*> medias;
public:
cMovieDbMovie(void);
virtual ~cMovieDbMovie(void);
int id;
string title;
string originalTitle;
string tagline;
string overview;
bool adult;
int collectionID;
string collectionName;
int budget;
int revenue;
string genres;
string homepage;
string imdbid;
string releaseDate;
int runtime;
float popularity;
float voteAverage;
void InsertActor(cMovieActor *actor);
void InsertMedia(cMovieMedia *media);
vector<int> GetActorIDs(void);
void SetActorThumbSize(int actorId, int imgWidth, int imgHeight);
void SetActorPath(int actorId, string path);
//Getter for Serivice Calls
bool GetMedia(mediaMovies mediatype, cTvMedia *p);
void GetActors(vector<cActor> *a);
void Dump();
};
#endif //__TVSCRAPER_TVDBSERIES_H

65
po/de_DE.po Normal file
View File

@ -0,0 +1,65 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-scraper2vdr 0.0.1\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2014-03-30 14:37+0200\n"
"PO-Revision-Date: 2014-03-30 18:30+0100\n"
"Last-Translator: 3PO\n"
"Language-Team: German <vdr@linuxtv.org>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Update Scraper Information from Database"
msgstr "Filme und Serien aus Datenbank aktualisieren"
msgid "Update Scraper Recordings Information from Database"
msgstr "Aufnahmen aus Datenbank aktualisieren"
msgid "Scan for new recordings in video directory"
msgstr "Videoverzeichnis nach neuen Aufnahmen scannen"
msgid "Scan for new or updated scrapinfo files"
msgstr "Nach neuen oder geänderten scrapinfo Dateien suchen"
msgid "Cleanup Recordings in Database"
msgstr "Aufnahmen in Datenbank bereinigen"
msgid "Updating Scraper EPG Information from Database"
msgstr "Filme und Serien werden aus Datenbank aktualisieren"
msgid "Updating Scraper Recordings Information from Database"
msgstr "Aufnahmen werden aus Datenbank aktualisieren"
msgid "Scanning for new recordings in video directory"
msgstr "Scanne Videoverzeichnis nach neuen Aufnahmen"
msgid "Scanning for new or updated scrapinfo files"
msgstr "Suche nach neuen oder geänderten scrapinfo Dateien"
msgid "Cleaning up Recordings in Database"
msgstr "Bereinige Aufnahmen in Datenbank"
msgid "Show Main Menu Entry"
msgstr "Hauptmenüeintrag anzeigen"
msgid "MySQL Host"
msgstr "MySQL Host"
msgid "MySQL Port"
msgstr "MySQL Port"
msgid "MySQL Database Name"
msgstr "MySQL Datenbannk Name"
msgid "MySQL User"
msgstr "MySQL Benutzer"
msgid "MySQL Password"
msgstr "MySQL Passwort"

249
scraper2vdr.c Normal file
View File

@ -0,0 +1,249 @@
/*
* scraper2vdr.c: A plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
* $Id$
*/
#include "scraper2vdr.h"
#if defined (APIVERSNUM) && (APIVERSNUM < 10600)
# error VDR API versions < 1.6.0 are not supported !
#endif
//***************************************************************************
// Plugin Main Menu
//***************************************************************************
class cScraper2VdrPluginMenu : public cOsdMenu {
public:
cScraper2VdrPluginMenu(const char* title, cUpdate *update);
virtual ~cScraper2VdrPluginMenu() { };
virtual eOSState ProcessKey(eKeys key);
protected:
cUpdate *update;
};
cScraper2VdrPluginMenu::cScraper2VdrPluginMenu(const char* title, cUpdate *update) : cOsdMenu(title) {
this->update = update;
Clear();
cOsdMenu::Add(new cOsdItem(tr("Update Scraper Information from Database")));
cOsdMenu::Add(new cOsdItem(tr("Update Scraper Recordings Information from Database")));
cOsdMenu::Add(new cOsdItem(tr("Scan for new recordings in video directory")));
cOsdMenu::Add(new cOsdItem(tr("Scan for new or updated scrapinfo files")));
cOsdMenu::Add(new cOsdItem(tr("Cleanup Recordings in Database")));
SetHelp(0, 0, 0,0);
Display();
}
//***************************************************************************
// Process Key
//***************************************************************************
eOSState cScraper2VdrPluginMenu::ProcessKey(eKeys key) {
eOSState state = cOsdMenu::ProcessKey(key);
if (state != osUnknown)
return state;
switch (key) {
case kOk: {
if (Current() == 0) {
Skins.Message(mtInfo, tr("Updating Scraper EPG Information from Database"));
update->ForceUpdate();
} else if (Current() == 1) {
Skins.Message(mtInfo, tr("Updating Scraper Recordings Information from Database"));
update->ForceRecordingUpdate();
} else if (Current() == 2) {
Skins.Message(mtInfo, tr("Scanning for new recordings in video directory"));
update->ForceVideoDirUpdate();
} else if (Current() == 3) {
Skins.Message(mtInfo, tr("Scanning for new or updated scrapinfo files"));
update->ForceScrapInfoUpdate();
} else if (Current() == 4) {
Skins.Message(mtInfo, tr("Cleaning up Recordings in Database"));
update->TriggerCleanRecordingsDB();
}
return osEnd;
}
default:
break;
}
return state;
}
//***************************************************************************
// cPluginScraper2vdr
//***************************************************************************
cPluginScraper2vdr::cPluginScraper2vdr(void) {
cDbConnection::init();
}
cPluginScraper2vdr::~cPluginScraper2vdr() {
delete update;
delete scrapManager;
cDbConnection::exit();
}
const char *cPluginScraper2vdr::CommandLineHelp(void) {
return
" -i <IMAGEDIR>, --imagedir=<IMAGEDIR> Set directory where images are stored\n"
" -m <MODE>, --mode=<MODE> mode can be client or headless, see README\n";
}
bool cPluginScraper2vdr::ProcessArgs(int argc, char *argv[]) {
static const struct option long_options[] = {
{ "imagedir", required_argument, NULL, 'i' },
{ "mode", required_argument, NULL, 'm' },
{ 0, 0, 0, 0 }
};
int c;
while ((c = getopt_long(argc, argv, "i:m:", long_options, NULL)) != -1) {
switch (c) {
case 'i':
config.SetImageDir(optarg);
break;
case 'm':
config.SetMode(optarg);
break;
default:
return false;
}
}
return true;
}
bool cPluginScraper2vdr::Initialize(void) {
config.SetUuid(this);
config.SetDefaultImageDir();
scrapManager = new cScrapManager();
update = new cUpdate(scrapManager);
return true;
}
bool cPluginScraper2vdr::Start(void) {
update->Start();
return true;
}
void cPluginScraper2vdr::Stop(void) {
update->Stop();
}
void cPluginScraper2vdr::Housekeeping(void) {
}
void cPluginScraper2vdr::MainThreadHook(void) {
}
cString cPluginScraper2vdr::Active(void) {
return NULL;
}
time_t cPluginScraper2vdr::WakeupTime(void) {
return 0;
}
cOsdObject *cPluginScraper2vdr::MainMenuAction(void) {
if (config.mainMenuEntry == 1)
return new cScraper2VdrPluginMenu("Scraper2Vdr", update);
return NULL;
}
cMenuSetupPage *cPluginScraper2vdr::SetupMenu(void) {
return new cScraper2VdrSetup(update);
}
bool cPluginScraper2vdr::SetupParse(const char *Name, const char *Value) {
return config.SetupParse(Name, Value);
}
bool cPluginScraper2vdr::Service(const char *Id, void *Data) {
if (Data == NULL)
return false;
if (strcmp(Id, "GetEventType") == 0) {
ScraperGetEventType* call = (ScraperGetEventType*) Data;
if (!call->event && !call->recording)
return false;
return scrapManager->GetEventType(call);
}
if (strcmp(Id, "GetSeries") == 0) {
cSeries* call = (cSeries*) Data;
if (call->seriesId == 0)
return false;
return scrapManager->GetSeries(call);
}
if (strcmp(Id, "GetMovie") == 0) {
cMovie* call = (cMovie*) Data;
if (call->movieId == 0)
return false;
return scrapManager->GetMovie(call);
}
if (strcmp(Id, "GetPosterBanner") == 0) {
ScraperGetPosterBanner* call = (ScraperGetPosterBanner*) Data;
if (!call->event)
return false;
return scrapManager->GetPosterBanner(call);
}
if (strcmp(Id, "GetPoster") == 0) {
ScraperGetPoster* call = (ScraperGetPoster*) Data;
if (!call->event && !call->recording)
return false;
return scrapManager->GetPoster(call);
}
if (strcmp(Id, "GetPosterThumb") == 0) {
ScraperGetPosterThumb* call = (ScraperGetPosterThumb*) Data;
if (!call->event && !call->recording)
return false;
return scrapManager->GetPosterThumb(call);
}
return false;
}
const char **cPluginScraper2vdr::SVDRPHelpPages(void) {
static const char *HelpPages[] = {
"UPDT\n"
" Load all series and movies for events from database.",
"UPDR\n"
" Load recordings from database.",
"SCVD\n"
" Trigger scan fornew recordings in video directory.",
"SCSI\n"
" Trigger scan for scrapinfo files in video directory.",
"CRDB\n"
" Trigger cleanup of recordings database.",
0
};
return HelpPages;
}
cString cPluginScraper2vdr::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) {
if (strcasecmp(Command, "UPDT") == 0) {
update->ForceUpdate();
return "SCRAPER2VDR full update from database forced.";
} else if (strcasecmp(Command, "UPDR") == 0) {
update->ForceRecordingUpdate();
return "SCRAPER2VDR scanning of recordings in database triggered.";
} else if (strcasecmp(Command, "SCVD") == 0) {
update->ForceVideoDirUpdate();
return "SCRAPER2VDR scan for new recordings in video dir triggered.";
} else if (strcasecmp(Command, "SCSI") == 0) {
update->ForceScrapInfoUpdate();
return "SCRAPER2VDR scan for new or updated scrapinfo files triggered.";
} else if (strcasecmp(Command, "CRDB") == 0) {
update->TriggerCleanRecordingsDB();
return "SCRAPER2VDR cleanup of recording DB triggered.";
}
return NULL;
}
VDRPLUGINCREATOR(cPluginScraper2vdr); // Don't touch this!

57
scraper2vdr.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef __SCRAPER2VDR_H
#define __SCRAPER2VDR_H
#include <getopt.h>
#include <vdr/plugin.h>
#include "lib/common.h"
#include "config.h"
#include "setup.h"
#include "scrapmanager.h"
#include "update.h"
#include "services.h"
//***************************************************************************
// Constants
//***************************************************************************
static const char *VERSION = "0.0.1";
static const char *DESCRIPTION = "'scraper2vdr' plugin";
static const char *MAINMENUENTRY = "Scraper2Vdr";
//***************************************************************************
// Globals
//***************************************************************************
cScraper2VdrConfig config;
//***************************************************************************
// cPluginScraper2vdr
//***************************************************************************
class cPluginScraper2vdr : public cPlugin {
private:
cScrapManager *scrapManager;
cUpdate *update;
public:
cPluginScraper2vdr(void);
virtual ~cPluginScraper2vdr();
virtual const char *Version(void) { return VERSION; }
virtual const char *Description(void) { return DESCRIPTION; }
virtual const char *CommandLineHelp(void);
virtual bool ProcessArgs(int argc, char *argv[]);
virtual bool Initialize(void);
virtual bool Start(void);
virtual void Stop(void);
virtual void Housekeeping(void);
virtual void MainThreadHook(void);
virtual cString Active(void);
virtual time_t WakeupTime(void);
virtual const char *MainMenuEntry(void) { return (config.mainMenuEntry)?MAINMENUENTRY:NULL; }
virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
virtual bool Service(const char *Id, void *Data = NULL);
virtual const char **SVDRPHelpPages(void);
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
};
//***************************************************************************
#endif // __SCRAPER2VDR_H

503
scrapmanager.c Normal file
View File

@ -0,0 +1,503 @@
#define __STL_CONFIG_H
#include <vdr/recording.h>
#include "tools.h"
#include "scrapmanager.h"
using namespace std;
bool operator<(const sEventsKey& l, const sEventsKey& r) {
if (l.eventId != r.eventId)
return (l.eventId < r.eventId);
int comp = l.channelId.compare(r.channelId);
if (comp < 0)
return true;
return false;
}
bool operator<(const sRecordingsKey& l, const sRecordingsKey& r) {
if (l.recStart != r.recStart)
return (l.recStart < r.recStart);
int comp = l.recPath.compare(r.recPath);
if (comp < 0)
return true;
return false;
}
cScrapManager::cScrapManager(void) {
}
cScrapManager::~cScrapManager(void) {
for (map<int, cTVDBSeries*>::iterator it = series.begin(); it != series.end(); it++) {
cTVDBSeries *s = (cTVDBSeries*)it->second;
delete s;
}
series.clear();
for (map<int, cMovieDbMovie*>::iterator it = movies.begin(); it != movies.end(); it++) {
cMovieDbMovie *m = (cMovieDbMovie*)it->second;
delete m;
}
movies.clear();
}
void cScrapManager::InitIterator(bool isRec) {
if (!isRec)
eventsIterator = events.begin();
else
recIterator = recordings.begin();
}
sEventsValue cScrapManager::GetEventInformation(int eventId, string channelId) {
sEventsKey k;
k.eventId = eventId;
k.channelId = channelId;
sEventsValue emptyVal;
emptyVal.seriesId = 0;
emptyVal.episodeId = 0;
emptyVal.movieId = 0;
emptyVal.isNew = false;
map<sEventsKey, sEventsValue>::iterator hit = events.find(k);
if (hit != events.end())
return hit->second;
return emptyVal;
}
void cScrapManager::AddEvent(int eventId, string channelId, int seriesId, int episodeId, int movieId) {
sEventsKey k;
k.eventId = eventId;
k.channelId = channelId;
sEventsValue v;
v.seriesId = seriesId;
v.episodeId = episodeId;
v.movieId = movieId;
v.isNew = true;
events.insert(pair<sEventsKey, sEventsValue>(k, v));
}
bool cScrapManager::GetNextSeries(bool isRec, int &seriesId, int &episodeId) {
bool next = false;
if (!isRec) {
while (eventsIterator != events.end()) {
next = true;
sEventsValue ev = eventsIterator->second;
if (ev.isNew && (ev.seriesId > 0)) {
seriesId = ev.seriesId;
episodeId = ev.episodeId;
eventsIterator->second.isNew = false;
eventsIterator++;
break;
}
eventsIterator++;
next = false;
}
} else {
while (recIterator != recordings.end()) {
next = true;
sEventsValue ev = recIterator->second;
if (ev.isNew && (ev.seriesId > 0)) {
seriesId = ev.seriesId;
episodeId = ev.episodeId;
recIterator->second.isNew = false;
recIterator++;
break;
}
recIterator++;
next = false;
}
}
return next;
}
bool cScrapManager::GetNextMovie(bool isRec, int &movieId) {
bool next = false;
if (!isRec) {
while (eventsIterator != events.end()) {
next = true;
sEventsValue ev = eventsIterator->second;
if (ev.isNew && (ev.movieId > 0)) {
movieId = ev.movieId;
eventsIterator->second.isNew = false;
eventsIterator++;
break;
}
eventsIterator++;
next = false;
}
} else {
while (recIterator != recordings.end()) {
next = true;
sEventsValue ev = recIterator->second;
if (ev.isNew && (ev.movieId > 0)) {
movieId = ev.movieId;
recIterator->second.isNew = false;
recIterator++;
break;
}
recIterator++;
next = false;
}
}
return next;
}
cTVDBSeries *cScrapManager::GetSeries(int seriesId) {
map<int, cTVDBSeries*>::iterator hit = series.find(seriesId);
if (hit == series.end())
return NULL;
return hit->second;
}
cMovieDbMovie *cScrapManager::GetMovie(int movieId) {
map<int, cMovieDbMovie*>::iterator hit = movies.find(movieId);
if (hit == movies.end())
return NULL;
return hit->second;
}
cTVDBSeries *cScrapManager::AddSeries(cTableSeries* tSeries) {
cTVDBSeries *s = new cTVDBSeries();
s->id = tSeries->getIntValue(cTableSeries::fiSeriesId);
s->name = tSeries->getStrValue(cTableSeries::fiSeriesName);
s->overview = tSeries->getStrValue(cTableSeries::fiSeriesOverview);
s->firstAired = tSeries->getStrValue(cTableSeries::fiSeriesFirstAired);
s->network = tSeries->getStrValue(cTableSeries::fiSeriesNetwork);
string genre = replaceString(tSeries->getStrValue(cTableSeries::fiSeriesGenre), "|", ", ");
s->genre = genre;
s->rating = tSeries->getFloatValue(cTableSeries::fiSeriesRating);
s->status = tSeries->getStrValue(cTableSeries::fiSeriesStatus);
series.insert(pair<int, cTVDBSeries*>(tSeries->getIntValue(cTableSeries::fiSeriesId), s));
return s;
}
cMovieDbMovie *cScrapManager::AddMovie(cTableMovies* tMovies) {
cMovieDbMovie *m = new cMovieDbMovie();
m->id = tMovies->getIntValue(cTableMovies::fiMovieId);
m->title = tMovies->getStrValue(cTableMovies::fiTitle);
m->originalTitle = tMovies->getStrValue(cTableMovies::fiOriginalTitle);
m->tagline = tMovies->getStrValue(cTableMovies::fiTagline);
m->overview = tMovies->getStrValue(cTableMovies::fiOverview);
m->adult = tMovies->getIntValue(cTableMovies::fiIsAdult);
m->collectionName = tMovies->getStrValue(cTableMovies::fiCollectionName);
m->budget = tMovies->getIntValue(cTableMovies::fiBudget);
m->revenue = tMovies->getIntValue(cTableMovies::fiRevenue);
string genre = replaceString(tMovies->getStrValue(cTableMovies::fiGenres), "|", ",");
m->genres = genre;
m->homepage = tMovies->getStrValue(cTableMovies::fiHomepage);
m->releaseDate = tMovies->getStrValue(cTableMovies::fiReleaaseDate);
m->runtime = tMovies->getIntValue(cTableMovies::fiRuntime);
m->popularity = tMovies->getFloatValue(cTableMovies::fiPopularity);
m->voteAverage = tMovies->getFloatValue(cTableMovies::fiVoteAverage);
movies.insert(pair<int, cMovieDbMovie*>(tMovies->getIntValue(cTableMovies::fiMovieId), m));
return m;
}
void cScrapManager::AddSeriesEpisode(cTVDBSeries *series, cTableSeriesEpisode* tEpisodes) {
cTVDBEpisode *e = new cTVDBEpisode();
e->id = tEpisodes->getIntValue(cTableSeriesEpisode::fiEpisodeId);
e->name = tEpisodes->getStrValue(cTableSeriesEpisode::fiEpisodeName);
e->number = tEpisodes->getIntValue(cTableSeriesEpisode::fiEpisodeNumber);
e->season = tEpisodes->getIntValue(cTableSeriesEpisode::fiSeasonNumber);
e->overview = tEpisodes->getStrValue(cTableSeriesEpisode::fiEpisodeOverview);
e->firstAired = tEpisodes->getStrValue(cTableSeriesEpisode::fiEpisodeFirstAired);
string guestStars = replaceString(tEpisodes->getStrValue(cTableSeriesEpisode::fiEpisodeGuestStars), "|", ", ");
e->guestStars = guestStars;
e->rating = tEpisodes->getFloatValue(cTableSeriesEpisode::fiEpisodeRating);
series->InsertEpisode(e);
}
void cScrapManager::AddSeriesActor(cTVDBSeries *series, cTableSeriesActor* tActors) {
cTVDBActor *a = new cTVDBActor();
a->id = tActors->getIntValue(cTableSeriesActor::fiActorId);
a->name = tActors->getStrValue(cTableSeriesActor::fiActorName);
a->role = tActors->getStrValue(cTableSeriesActor::fiActorRole);
series->InsertActor(a);
}
void cScrapManager::AddMovieMedia(cMovieDbMovie *movie, cTableMovieMedia* tMovieMedia, string path) {
cMovieMedia *m = new cMovieMedia();
m->mediaType = tMovieMedia->getIntValue(cTableMovieMedia::fiMediaType);
m->width = tMovieMedia->getIntValue(cTableMovieMedia::fiMediaWidth);
m->height = tMovieMedia->getIntValue(cTableMovieMedia::fiMediaHeight);
m->path = path;
movie->InsertMedia(m);
}
void cScrapManager::AddMovieActor(cMovieDbMovie *movie, cTableMovieActor* tActor, string role) {
cMovieActor *a = new cMovieActor();
a->id = tActor->getIntValue(cTableMovieActor::fiActorId);
a->name = tActor->getStrValue(cTableMovieActor::fiActorName);
a->role = role;
movie->InsertActor(a);
}
bool cScrapManager::AddRecording(int recStart, string recPath, int seriesId, int episodeId, int movieId) {
sRecordingsKey k;
k.recStart = recStart;
k.recPath = recPath;
//check if recording already exists
map<sRecordingsKey, sEventsValue>::iterator hit = recordings.find(k);
if (hit != recordings.end()) {
sEventsValue v = hit->second;
if ((v.seriesId == seriesId) && (v.episodeId == episodeId) && (v.movieId == movieId))
return false;
else
recordings.erase(hit);
}
sEventsValue v;
v.seriesId = seriesId;
v.episodeId = episodeId;
v.movieId = movieId;
v.isNew = true;
recordings.insert(pair<sRecordingsKey, sEventsValue>(k, v));
return true;
}
bool cScrapManager::RecordingExists(int recStart, string recPath) {
sRecordingsKey k;
k.recStart = recStart;
k.recPath = recPath;
map<sRecordingsKey, sEventsValue>::iterator hit = recordings.find(k);
if (hit != recordings.end())
return true;
return false;
}
bool cScrapManager::SeriesInUse(int seriesId) {
map<int, cTVDBSeries*>::iterator hit = series.find(seriesId);
if (hit != series.end())
return true;
return false;
}
bool cScrapManager::MovieInUse(int movieId) {
map<int, cMovieDbMovie*>::iterator hit = movies.find(movieId);
if (hit != movies.end())
return true;
return false;
}
void cScrapManager::DumpSeries(int num) {
int i=0;
tell(0, "Dumping first %d series", num);
for (map<int, cTVDBSeries*>::iterator it = series.begin(); it != series.end(); it++) {
cTVDBSeries *s = it->second;
s->Dump();
if (i == num)
break;
i++;
}
}
void cScrapManager::DumpMovies(int num) {
int i=0;
tell(0, "Dumping first %d movies", num);
for (map<int, cMovieDbMovie*>::iterator it = movies.begin(); it != movies.end(); it++) {
cMovieDbMovie *m = it->second;
m->Dump();
if (i == num)
break;
i++;
}
}
void cScrapManager::DumpRecordings(int num) {
tell(0, "Dumping first %d recordings", num);
for (map<sRecordingsKey, sEventsValue>::iterator it = recordings.begin(); it != recordings.end(); it++) {
sRecordingsKey key = it->first;
sEventsValue val = it->second;
tell(0, "recStart %d, recPath %s, seriesId %d, episodeId %d, movieId %d", key.recStart, key.recPath.c_str(), val.seriesId, val.episodeId, val.movieId);
}
}
bool cScrapManager::GetEventType(ScraperGetEventType *call) {
sEventsValue v;
if (call->event) {
sEventsKey k;
k.eventId = call->event->EventID();
k.channelId = *(call->event->ChannelID().ToString());
map<sEventsKey, sEventsValue>::iterator hit = events.find(k);
if (hit == events.end())
return false;
v = hit->second;
} else if (call->recording) {
sRecordingsKey k;
k.recStart = call->recording->Start();
k.recPath = call->recording->FileName();
map<sRecordingsKey, sEventsValue>::iterator hit = recordings.find(k);
if (hit == recordings.end())
return false;
v = hit->second;
}
if (v.seriesId > 0) {
call->type = tSeries;
} else if (v.movieId > 0) {
call->type = tMovie;
} else {
call->type = tNone;
return false;
}
call->seriesId = v.seriesId;
call->episodeId = v.episodeId;
call->movieId = v.movieId;
return true;
}
bool cScrapManager::GetSeries(cSeries *s) {
map<int, cTVDBSeries*>::iterator hit = series.find(s->seriesId);
if (hit == series.end())
return false;
cTVDBSeries *sStored = hit->second;
s->name = sStored->name;
s->overview = sStored->overview;
s->firstAired = sStored->firstAired;
s->network = sStored->network;
s->genre = sStored->genre;
s->rating = sStored->rating;
s->status = sStored->status;
//Episode
if (s->episodeId > 0) {
sStored->GetEpisode(s->episodeId, &s->episode);
sStored->GetSeasonPoster(s->episodeId, &s->seasonPoster);
}
//Media
sStored->GetPosters(&s->posters);
sStored->GetBanners(&s->banners);
sStored->GetFanart(&s->fanarts);
//Actors
sStored->GetActors(&s->actors);
return true;
}
bool cScrapManager::GetMovie(cMovie *m) {
map<int, cMovieDbMovie*>::iterator hit = movies.find(m->movieId);
if (hit == movies.end())
return false;
cMovieDbMovie *mStored = hit->second;
m->title = mStored->title;
m->originalTitle = mStored->originalTitle;
m->tagline = mStored->tagline;
m->overview = mStored->overview;
m->adult = mStored->adult;
m->collectionName = mStored->collectionName;
m->budget = mStored->budget;
m->revenue = mStored->revenue;
m->genres = mStored->genres;
m->homepage = mStored->homepage;
m->releaseDate = mStored->releaseDate;
m->runtime = mStored->runtime;
m->popularity = mStored->popularity;
m->voteAverage = mStored->voteAverage;
//Media
mStored->GetMedia(mmPoster, &m->poster);
mStored->GetMedia(mmFanart, &m->fanart);
mStored->GetMedia(mmCollectionPoster, &m->collectionPoster);
mStored->GetMedia(mmCollectionFanart, &m->collectionFanart);
//Actors
mStored->GetActors(&m->actors);
return true;
}
bool cScrapManager::GetPosterBanner(ScraperGetPosterBanner *call) {
sEventsKey k;
k.eventId = call->event->EventID();
k.channelId = *(call->event->ChannelID().ToString());
map<sEventsKey, sEventsValue>::iterator hit = events.find(k);
if (hit == events.end())
return false;
sEventsValue v = hit->second;
if (v.seriesId > 0) {
call->type = tSeries;
map<int, cTVDBSeries*>::iterator hitSeries = series.find(v.seriesId);
if (hitSeries == series.end())
return false;
cTVDBSeries *s = hitSeries->second;
bool found = s->GetRandomBanner(&call->banner);
if (v.episodeId > 0) {
s->GetSeasonPoster(v.episodeId, &call->poster);
}
return found;
} else if (v.movieId > 0) {
call->type = tMovie;
map<int, cMovieDbMovie*>::iterator hitMovies = movies.find(v.movieId);
if (hitMovies == movies.end())
return false;
cMovieDbMovie *m = hitMovies->second;
return m->GetMedia(mmPoster, &call->poster);
} else {
call->type = tNone;
}
return false;
}
bool cScrapManager::GetPoster(ScraperGetPoster *call) {
sEventsValue v;
if (call->event) {
sEventsKey k;
k.eventId = call->event->EventID();
k.channelId = *(call->event->ChannelID().ToString());
map<sEventsKey, sEventsValue>::iterator hit = events.find(k);
if (hit == events.end())
return false;
v = hit->second;
} else if (call->recording) {
sRecordingsKey k;
k.recStart = call->recording->Start();
k.recPath = call->recording->FileName();
map<sRecordingsKey, sEventsValue>::iterator hit = recordings.find(k);
if (hit == recordings.end())
return false;
v = hit->second;
}
if (v.seriesId > 0) {
map<int, cTVDBSeries*>::iterator hitSeries = series.find(v.seriesId);
if (hitSeries == series.end())
return false;
cTVDBSeries *s = hitSeries->second;
return s->GetPoster(&call->poster);
} else if (v.movieId > 0) {
map<int, cMovieDbMovie*>::iterator hitMovies = movies.find(v.movieId);
if (hitMovies == movies.end())
return false;
cMovieDbMovie *m = hitMovies->second;
return m->GetMedia(mmPoster, &call->poster);
}
}
bool cScrapManager::GetPosterThumb(ScraperGetPosterThumb *call) {
sEventsValue v;
if (call->event) {
sEventsKey k;
k.eventId = call->event->EventID();
k.channelId = *(call->event->ChannelID().ToString());
map<sEventsKey, sEventsValue>::iterator hit = events.find(k);
if (hit == events.end())
return false;
v = hit->second;
} else if (call->recording) {
sRecordingsKey k;
k.recStart = call->recording->Start();
k.recPath = call->recording->FileName();
map<sRecordingsKey, sEventsValue>::iterator hit = recordings.find(k);
if (hit == recordings.end())
return false;
v = hit->second;
}
if (v.seriesId > 0) {
map<int, cTVDBSeries*>::iterator hitSeries = series.find(v.seriesId);
if (hitSeries == series.end())
return false;
cTVDBSeries *s = hitSeries->second;
return s->GetPosterThumb(&call->poster);
} else if (v.movieId > 0) {
map<int, cMovieDbMovie*>::iterator hitMovies = movies.find(v.movieId);
if (hitMovies == movies.end())
return false;
cMovieDbMovie *m = hitMovies->second;
return m->GetMedia(mmPosterThumb, &call->poster);
}
return false;
}

81
scrapmanager.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef __SCRAPMANAGER_H
#define __SCRAPMANAGER_H
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <algorithm>
#include "lib/common.h"
#include "lib/db.h"
#include "lib/tabledef.h"
#include "services.h"
#include "tvdbseries.h"
#include "moviedbmovie.h"
using namespace std;
struct sEventsKey {
int eventId;
string channelId;
};
struct sEventsValue {
int seriesId;
int episodeId;
int movieId;
bool isNew;
};
struct sRecordingsKey {
int recStart;
string recPath;
};
class cScrapManager {
private:
map<sEventsKey, sEventsValue> events;
map<sEventsKey, sEventsValue>::iterator eventsIterator;
map<sRecordingsKey, sEventsValue> recordings;
map<sRecordingsKey, sEventsValue>::iterator recIterator;
map<int, cTVDBSeries*> series;
map<int, cMovieDbMovie*> movies;
public:
cScrapManager(void);
virtual ~cScrapManager(void);
//Series and Movies Handling
void AddEvent(int eventId, string channelId, int seriesId, int episodeId, int movieId);
void InitIterator(bool isRec);
int GetNumSeries(void) { return series.size(); };
int GetNumMovies(void) { return movies.size(); };
sEventsValue GetEventInformation(int eventId, string channelId);
bool GetNextSeries(bool isRec, int &seriesId, int &episodeId);
bool GetNextMovie(bool isRec, int &movieId);
cTVDBSeries *GetSeries(int seriesId);
cMovieDbMovie *GetMovie(int movieId);
cTVDBSeries *AddSeries(cTableSeries* tSeries);
cMovieDbMovie *AddMovie(cTableMovies* tMovies);
void AddSeriesEpisode(cTVDBSeries *series, cTableSeriesEpisode* tEpisodes);
void AddSeriesActor(cTVDBSeries *series, cTableSeriesActor* tActors);
void AddMovieActor(cMovieDbMovie *movie, cTableMovieActor* tActor, string role);
void AddMovieMedia(cMovieDbMovie *movie, cTableMovieMedia* tMovieMedia, string path);
//Recording Handling
bool AddRecording(int recStart, string recPath, int seriesId, int episodeId, int movieId);
bool RecordingExists(int recStart, string recPath);
bool SeriesInUse(int seriesId);
bool MovieInUse(int movieId);
//Debug
void DumpSeries(int num);
void DumpMovies(int num);
void DumpRecordings(int num);
//Service Calls
bool GetEventType(ScraperGetEventType *call);
bool GetSeries(cSeries *series);
bool GetMovie(cMovie *movie);
bool GetPosterBanner(ScraperGetPosterBanner *call);
bool GetPoster(ScraperGetPoster *call);
bool GetPosterThumb(ScraperGetPosterThumb *call);
};
#endif //__SCRAPMANAGER_H

194
services.h Normal file
View File

@ -0,0 +1,194 @@
#ifndef __SCRAPER2VDRSERVICES_H
#define __SCRAPER2VDRSERVICES_H
#include <vdr/epg.h>
#include <vdr/recording.h>
enum tvType {
tSeries,
tMovie,
tNone,
};
/*********************************************************************
* Helper Structures
*********************************************************************/
class cTvMedia {
public:
cTvMedia(void) {
path = "";
width = height = 0;
};
std::string path;
int width;
int height;
};
class cEpisode {
public:
cEpisode(void) {
number = 0;
season = 0;
name = "";
firstAired = "";
guestStars = "";
overview = "";
rating = 0.0;
};
int number;
int season;
std::string name;
std::string firstAired;
std::string guestStars;
std::string overview;
float rating;
cTvMedia episodeImage;
};
class cActor {
public:
cActor(void) {
name = "";
role = "";
};
std::string name;
std::string role;
cTvMedia actorThumb;
};
/*********************************************************************
* Data Structures for Service Calls
*********************************************************************/
// Data structure for service "GetEventType"
class ScraperGetEventType {
public:
ScraperGetEventType(void) {
event = NULL;
recording = NULL;
type = tNone;
movieId = 0;
seriesId = 0;
episodeId = 0;
};
// in
const cEvent *event; // check type for this event
const cRecording *recording; // or for this recording
//out
tvType type; //typeSeries or typeMovie
int movieId;
int seriesId;
int episodeId;
};
//Data structure for full series and episode information
class cMovie {
public:
cMovie(void) {
title = "";
originalTitle = "";
tagline = "";
overview = "";
adult = false;
collectionName = "";
budget = 0;
revenue = 0;
genres = "";
homepage = "";
releaseDate = "";
runtime = 0;
popularity = 0.0;
voteAverage = 0.0;
};
//IN
int movieId; // movieId fetched from ScraperGetEventType
//OUT
std::string title;
std::string originalTitle;
std::string tagline;
std::string overview;
bool adult;
std::string collectionName;
int budget;
int revenue;
std::string genres;
std::string homepage;
std::string releaseDate;
int runtime;
float popularity;
float voteAverage;
cTvMedia poster;
cTvMedia fanart;
cTvMedia collectionPoster;
cTvMedia collectionFanart;
std::vector<cActor> actors;
};
//Data structure for full series and episode information
class cSeries {
public:
cSeries(void) {
seriesId = 0;
episodeId = 0;
name = "";
overview = "";
firstAired = "";
network = "";
genre = "";
rating = 0.0;
status = "";
};
//IN
int seriesId; // seriesId fetched from ScraperGetEventType
int episodeId; // episodeId fetched from ScraperGetEventType
//OUT
std::string name;
std::string overview;
std::string firstAired;
std::string network;
std::string genre;
float rating;
std::string status;
cEpisode episode;
std::vector<cActor> actors;
std::vector<cTvMedia> posters;
std::vector<cTvMedia> banners;
std::vector<cTvMedia> fanarts;
cTvMedia seasonPoster;
};
// Data structure for service "GetPosterBanner"
class ScraperGetPosterBanner {
public:
ScraperGetPosterBanner(void) {
type = tNone;
};
// in
const cEvent *event; // check type for this event
//out
tvType type; //typeSeries or typeMovie
cTvMedia poster;
cTvMedia banner;
};
// Data structure for service "GetPoster"
class ScraperGetPoster {
public:
// in
const cEvent *event; // check type for this event
const cRecording *recording; // or for this recording
//out
cTvMedia poster;
};
// Data structure for service "GetPosterThumb"
class ScraperGetPosterThumb {
public:
// in
const cEvent *event; // check type for this event
const cRecording *recording; // or for this recording
//out
cTvMedia poster;
};
#endif //__SCRAPER2VDRSERVICES_H

78
setup.c Normal file
View File

@ -0,0 +1,78 @@
#include "setup.h"
extern cScraper2VdrConfig config;
cScraper2VdrSetup::cScraper2VdrSetup(cUpdate *update) {
this->update = update;
tmpConfig = config;
strn0cpy(host, tmpConfig.mysqlHost.c_str(), sizeof(host));
strn0cpy(dbname, tmpConfig.mysqlDBName.c_str(), sizeof(dbname));
strn0cpy(user, tmpConfig.mysqlDBUser.c_str(), sizeof(user));
strn0cpy(password, tmpConfig.mysqlDBPass.c_str(), sizeof(password));
Setup();
}
cScraper2VdrSetup::~cScraper2VdrSetup() {
}
void cScraper2VdrSetup::Setup(void) {
int currentItem = Current();
Clear();
Add(new cMenuEditBoolItem(tr("Show Main Menu Entry"), &tmpConfig.mainMenuEntry));
Add(new cMenuEditStrItem(tr("MySQL Host"), host, sizeof(host), tr(FileNameChars)));
Add(new cMenuEditIntItem(tr("MySQL Port"), &tmpConfig.mysqlPort, 1, 99999));
Add(new cMenuEditStrItem(tr("MySQL Database Name"), dbname, sizeof(dbname), tr(FileNameChars)));
Add(new cMenuEditStrItem(tr("MySQL User"), user, sizeof(user), tr(FileNameChars)));
Add(new cMenuEditStrItem(tr("MySQL Password"), password, sizeof(password), tr(FileNameChars)));
Add(new cOsdItem(tr("Update Scraper Information from Database")));
Add(new cOsdItem(tr("Update Scraper Recordings Information from Database")));
Add(new cOsdItem(tr("Scan for new recordings in video directory")));
Add(new cOsdItem(tr("Scan for new or updated scrapinfo files")));
Add(new cOsdItem(tr("Cleanup Recordings in Database")));
SetCurrent(Get(currentItem));
Display();
}
eOSState cScraper2VdrSetup::ProcessKey(eKeys Key) {
bool hadSubMenu = HasSubMenu();
eOSState state = cMenuSetupPage::ProcessKey(Key);
if (Key == kOk) {
tmpConfig.mysqlHost = host;
tmpConfig.mysqlDBName = dbname;
tmpConfig.mysqlDBUser = user;
tmpConfig.mysqlDBPass = password;
Store();
if (Current() == 6) {
Skins.Message(mtInfo, tr("Updating Scraper EPG Information from Database"));
update->ForceUpdate();
} else if (Current() == 7) {
Skins.Message(mtInfo, tr("Updating Scraper Recordings Information from Database"));
update->ForceRecordingUpdate();
} else if (Current() == 8) {
Skins.Message(mtInfo, tr("Scanning for new recordings in video directory"));
update->ForceVideoDirUpdate();
} else if (Current() == 9) {
Skins.Message(mtInfo, tr("Scanning for new or updated scrapinfo files"));
update->ForceScrapInfoUpdate();
} else if (Current() == 10) {
Skins.Message(mtInfo, tr("Cleaning up Recordings in Database"));
update->TriggerCleanRecordingsDB();
}
return osEnd;
}
return state;
}
void cScraper2VdrSetup::Store(void) {
config = tmpConfig;
SetupStore("mainMenuEntry", tmpConfig.mainMenuEntry);
SetupStore("mysqlHost", tmpConfig.mysqlHost.c_str());
SetupStore("mysqlPort", tmpConfig.mysqlPort);
SetupStore("mysqlDBName", tmpConfig.mysqlDBName.c_str());
SetupStore("mysqlDBUser", tmpConfig.mysqlDBUser.c_str());
SetupStore("mysqlDBPass", tmpConfig.mysqlDBPass.c_str());
}

25
setup.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef __SCRAPER2VDR_SETUP_H
#define __SCRAPER2VDR_SETUP_H
#include <vdr/menuitems.h>
#include "update.h"
#include "config.h"
class cScraper2VdrSetup : public cMenuSetupPage {
public:
cScraper2VdrSetup(cUpdate *update);
virtual ~cScraper2VdrSetup();
private:
cUpdate *update;
cScraper2VdrConfig tmpConfig;
char host[256];
char dbname[256];
char user[256];
char password[256];
void Setup(void);
protected:
virtual eOSState ProcessKey(eKeys Key);
virtual void Store(void);
};
#endif //__SCRAPER2VDR_SETUP_H

170
tools.c Normal file
View File

@ -0,0 +1,170 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <dirent.h>
#include <fstream>
#include <algorithm>
#include <functional>
#include <cctype>
#include <locale>
#include <Magick++.h>
#include <vdr/plugin.h>
#include "lib/common.h"
#include "tools.h"
using namespace std;
using namespace Magick;
bool CreateDirectory(string dir) {
mkdir(dir.c_str(), 0775);
//check if successfull
DIR *pDir;
bool exists = false;
pDir = opendir(dir.c_str());
if (pDir != NULL) {
exists = true;
closedir(pDir);
}
return exists;
}
bool FileExists(string filename, bool isImage) {
ifstream ifile(filename.c_str());
if (ifile) {
//a valid image should be larger then 500 bytes
ifile.seekg (0, ifile.end);
int length = ifile.tellg();
int minimumLength = isImage ? 500 : 1;
if (length > minimumLength)
return true;
}
return false;
}
bool CheckDirExists(const char* dirName) {
struct statfs statfsbuf;
if (statfs(dirName,&statfsbuf)==-1) return false;
if ((statfsbuf.f_type!=0x01021994) && (statfsbuf.f_type!=0x28cd3d45)) return false;
if (access(dirName,R_OK|W_OK)==-1) return false;
return true;
}
void DeleteFile(string filename) {
remove(filename.c_str());
}
void DeleteDirectory(string dirname) {
DIR *dir;
struct dirent *entry;
if ((dir = opendir (dirname.c_str())) != NULL) {
while ((entry = readdir (dir)) != NULL) {
string file = entry->d_name;
if (!file.compare("."))
continue;
if (!file.compare(".."))
continue;
string delFile = dirname + "/" + file;
DeleteFile(delFile);
}
closedir (dir);
}
rmdir(dirname.c_str());
}
string TwoFoldersHigher(string folder) {
unsigned found = folder.find_last_of("/");
if (found != string::npos) {
string firstDirRemoved = folder.substr(0,found);
unsigned found2 = firstDirRemoved.find_last_of("/");
if (found2 != string::npos) {
return firstDirRemoved.substr(0,found2);
}
}
return "";
}
// trim from start
string &ltrim(string &s) {
s.erase(s.begin(), find_if(s.begin(), s.end(), not1(ptr_fun<int, int>(isspace))));
return s;
}
// trim from end
string &rtrim(string &s) {
s.erase(find_if(s.rbegin(), s.rend(), not1(ptr_fun<int, int>(isspace))).base(), s.end());
return s;
}
// trim from both ends
string &trim(string &s) {
return ltrim(rtrim(s));
}
void toLower(string &s) {
transform(s.begin(), s.end(), s.begin(), ::tolower);
}
bool isNumber(const string& s) {
string::const_iterator it = s.begin();
while (it != s.end() && isdigit(*it)) ++it;
return !s.empty() && it == s.end();
}
string replaceString(string content, string search, string repl) {
size_t pos = 0;
while((pos = content.find(search, pos)) != std::string::npos) {
if (pos > 3 && pos < content.size() - 2) {
content.replace(pos, search.length(), repl);
} else {
content.replace(pos, search.length(), "");
}
pos += repl.length();
}
return content;
}
/****************************************************************************************
* SPLTSTRING
****************************************************************************************/
// split: receives a char delimiter; returns a vector of strings
// By default ignores repeated delimiters, unless argument rep == 1.
vector<string>& splitstring::split(char delim, int rep) {
if (!flds.empty()) flds.clear(); // empty vector if necessary
string work = data();
string buf = "";
int i = 0;
while (i < work.length()) {
if (work[i] != delim)
buf += work[i];
else if (rep == 1) {
flds.push_back(buf);
buf = "";
} else if (buf.length() > 0) {
flds.push_back(buf);
buf = "";
}
i++;
}
if (!buf.empty())
flds.push_back(buf);
return flds;
}
void CreateThumbnail(string sourcePath, string destPath, int origWidth, int origHeight, int shrinkFactor) {
if (sourcePath.size() < 5 || destPath.size() < 5 || shrinkFactor < 2)
return;
int thumbWidth = origWidth / shrinkFactor;
int thumbHeight = origHeight / shrinkFactor;
InitializeMagick(NULL);
Image buffer;
try {
buffer.read(sourcePath.c_str());
buffer.sample(Geometry(thumbWidth, thumbHeight));
buffer.write(destPath.c_str());
} catch( ... ) {}
}

30
tools.h Normal file
View File

@ -0,0 +1,30 @@
#include <vector>
#include <string>
using namespace std;
//Filesystem Functions
bool CreateDirectory(string dir);
bool FileExists(string filename, bool isImage = true);
bool CheckDirExists(const char* dirName);
void DeleteFile(string filename);
void DeleteDirectory(string dirname);
string TwoFoldersHigher(string folder);
//String Functions
string &ltrim(string &s);
string &rtrim(string &s);
string &trim(string &s);
void toLower(string &s);
bool isNumber(const string& s);
string replaceString(string content, string search, string repl);
class splitstring : public string {
vector<string> flds;
public:
splitstring(const char *s) : string(s) { };
vector<string>& split(char delim, int rep=0);
};
//Image Functions
void CreateThumbnail(string sourcePath, string destPath, int origWidth, int origHeight, int shrinkFactor);

289
tvdbseries.c Normal file
View File

@ -0,0 +1,289 @@
#define __STL_CONFIG_H
#include "lib/common.h"
#include "tvdbseries.h"
using namespace std;
cTVDBSeries::cTVDBSeries(void) {
id = 0;
name = "";
overview = "";
firstAired = "";
network = "";
genre = "";
rating = 0.0;
status = "";
posterThumb = NULL;
}
cTVDBSeries::~cTVDBSeries() {
for (map<int, cTVDBActor*>::iterator it = actors.begin(); it != actors.end(); it++) {
cTVDBActor *a = (cTVDBActor*)it->second;
delete a;
}
for (map<int, cTVDBEpisode*>::iterator it = episodes.begin(); it != episodes.end(); it++) {
cTVDBEpisode *e = (cTVDBEpisode*)it->second;
delete e;
}
for (vector<cTVDBMedia*>::iterator it = posters.begin(); it != posters.end(); it++) {
cTVDBMedia *p = *it;
delete p;
}
for (vector<cTVDBMedia*>::iterator it = banners.begin(); it != banners.end(); it++) {
cTVDBMedia *b = *it;
delete b;
}
for (vector<cTVDBMedia*>::iterator it = fanart.begin(); it != fanart.end(); it++) {
cTVDBMedia *f = *it;
delete f;
}
for (map<int, cTVDBMedia*>::iterator it = seasonPosters.begin(); it != seasonPosters.end(); it++) {
cTVDBMedia *s = (cTVDBMedia*)it->second;
delete s;
}
for (map<int, cTVDBMedia*>::iterator it = seasonPosterThumbs.begin(); it != seasonPosterThumbs.end(); it++) {
cTVDBMedia *s = (cTVDBMedia*)it->second;
delete s;
}
if (posterThumb)
delete posterThumb;
}
void cTVDBSeries::InsertEpisode(cTVDBEpisode *episode) {
map<int, cTVDBEpisode*>::iterator hit = episodes.find(episode->id);
if (hit != episodes.end())
delete episode;
else
episodes.insert(pair<int, cTVDBEpisode*>(episode->id, episode));
}
void cTVDBSeries::InsertEpisodeImage(int episodeId, int width, int height, string path) {
map<int, cTVDBEpisode*>::iterator hit = episodes.find(episodeId);
if (hit != episodes.end()) {
cTVDBEpisode *e = hit->second;
cTVDBMedia *m = new cTVDBMedia();
m->width = width;
m->height = height;
m->path = path;
m->mediaType = msEpisodePic;
e->episodeImage = m;
}
}
void cTVDBSeries::InsertActor(cTVDBActor *actor) {
actors.insert(pair<int, cTVDBActor*>(actor->id, actor));
}
void cTVDBSeries::InsertActorThumb(int actorId, int imgWidth, int imgHeight, string path) {
map<int, cTVDBActor*>::iterator hit = actors.find(actorId);
if (hit != actors.end()) {
cTVDBActor *a = hit->second;
cTVDBMedia *m = new cTVDBMedia();
m->width = imgWidth;
m->height = imgHeight;
m->path = path;
m->mediaType = msActorThumb;
a->actorThumb = m;
}
}
void cTVDBSeries::InsertMedia(int mediaType, int imgWidth, int imgHeight, string path, int season) {
cTVDBMedia *media = new cTVDBMedia();
media->width = imgWidth;
media->height = imgHeight;
media->path = path;
media->mediaType = mediaType;
switch (mediaType) {
case msPoster1:
case msPoster2:
case msPoster3:
posters.push_back(media);
break;
case msFanart1:
case msFanart2:
case msFanart3:
fanart.push_back(media);
break;
case msBanner1:
case msBanner2:
case msBanner3:
banners.push_back(media);
case msSeasonPoster:
seasonPosters.insert(pair<int, cTVDBMedia*>(season, media));
break;
case msPosterThumb:
posterThumb = media;
break;
case msSeasonPosterThumb:
seasonPosterThumbs.insert(pair<int, cTVDBMedia*>(season, media));
break;
default:
break;
}
}
void cTVDBSeries::GetEpisode(int episodeId, cEpisode *e) {
map<int, cTVDBEpisode*>::iterator hit = episodes.find(episodeId);
if (hit == episodes.end())
return;
cTVDBEpisode *eStored = hit->second;
e->number = eStored->number;
e->season = eStored->season;
e->name = eStored->name;
e->firstAired = eStored->firstAired;
e->guestStars = eStored->guestStars;
e->overview = eStored->overview;
e->rating = eStored->rating;
if (eStored->episodeImage) {
e->episodeImage.path = eStored->episodeImage->path;
e->episodeImage.width = eStored->episodeImage->width;
e->episodeImage.height = eStored->episodeImage->height;
}
}
void cTVDBSeries::GetPosters(vector<cTvMedia> *p) {
for (vector<cTVDBMedia*>::iterator it = posters.begin(); it != posters.end(); it++) {
cTVDBMedia *mStored = *it;
cTvMedia m;
m.path = mStored->path;
m.width = mStored->width;
m.height = mStored->height;
p->push_back(m);
}
}
bool cTVDBSeries::GetPoster(cTvMedia *p) {
if (posters.size() > 0) {
p->path = posters[0]->path;
p->width = posters[0]->width;
p->height = posters[0]->height;
return true;
}
return false;
}
bool cTVDBSeries::GetPosterThumb(cTvMedia *p) {
if (posterThumb) {
p->path = posterThumb->path;
p->width = posterThumb->width;
p->height = posterThumb->height;
return true;
}
return false;
}
void cTVDBSeries::GetBanners(vector<cTvMedia> *b) {
for (vector<cTVDBMedia*>::iterator it = banners.begin(); it != banners.end(); it++) {
cTVDBMedia *bStored = *it;
cTvMedia m;
m.path = bStored->path;
m.width = bStored->width;
m.height = bStored->height;
b->push_back(m);
}
}
bool cTVDBSeries::GetRandomBanner(cTvMedia *b) {
int numBanners = banners.size();
if (numBanners == 0)
return false;
srand((unsigned)time(NULL));
int banner = rand()%numBanners;
cTVDBMedia *bStored = banners[banner];
b->path = bStored->path;
b->width = bStored->width;
b->height = bStored->height;
return true;
}
void cTVDBSeries::GetFanart(vector<cTvMedia> *f) {
for (vector<cTVDBMedia*>::iterator it = fanart.begin(); it != fanart.end(); it++) {
cTVDBMedia *fStored = *it;
cTvMedia m;
m.path = fStored->path;
m.width = fStored->width;
m.height = fStored->height;
f->push_back(m);
}
}
void cTVDBSeries::GetSeasonPoster(int episodeId, cTvMedia *sp) {
map<int, cTVDBEpisode*>::iterator hit = episodes.find(episodeId);
if (hit == episodes.end())
return;
cTVDBEpisode *e = hit->second;
map<int, cTVDBMedia*>::iterator hit2 = seasonPosters.find(e->season);
if (hit2 == seasonPosters.end())
return;
cTVDBMedia *spStored = hit2->second;
sp->width = spStored->width;
sp->height = spStored->height;
sp->path = spStored->path;
}
void cTVDBSeries::GetActors(vector<cActor> *a) {
for (map<int, cTVDBActor*>::iterator it = actors.begin(); it != actors.end(); it++) {
cTVDBActor *aStored = it->second;
cActor act;
act.name = aStored->name;
act.role = aStored->role;
if (aStored->actorThumb) {
act.actorThumb.width = aStored->actorThumb->width;
act.actorThumb.height = aStored->actorThumb->height;
act.actorThumb.path = aStored->actorThumb->path;
}
a->push_back(act);
}
}
void cTVDBSeries::Dump(void) {
tell(0, "--------------------------- Series Info ----------------------------------");
tell(0, "series %s, ID: %d", name.c_str(), id);
tell(0, "Overview: %s", overview.c_str());
tell(0, "FirstAired: %s", firstAired.c_str());
tell(0, "Network: %s", network.c_str());
tell(0, "Status: %s", status.c_str());
tell(0, "Genre: %s", genre.c_str());
tell(0, "Rating: %f", rating);
tell(0, "--------------------------- Media ----------------------------------");
for (vector<cTVDBMedia*>::iterator it = posters.begin(); it != posters.end(); it++) {
cTVDBMedia *m = *it;
tell(0, "Poster %d, Path: %s", m->mediaType, m->path.c_str());
tell(0, "width %d, height %d", m->width, m->height);
}
for (vector<cTVDBMedia*>::iterator it = banners.begin(); it != banners.end(); it++) {
cTVDBMedia *m = *it;
tell(0, "Banner %d, Path: %s", m->mediaType, m->path.c_str());
tell(0, "width %d, height %d", m->width, m->height);
}
for (vector<cTVDBMedia*>::iterator it = fanart.begin(); it != fanart.end(); it++) {
cTVDBMedia *m = *it;
tell(0, "Fanart %d, Path: %s", m->mediaType, m->path.c_str());
tell(0, "width %d, height %d", m->width, m->height);
}
tell(0, "--------------------------- Episodes ----------------------------------");
for (map<int, cTVDBEpisode*>::iterator it = episodes.begin(); it != episodes.end(); it++) {
cTVDBEpisode *e = it->second;
tell(0, "Episode %d, Name: %s", e->id, e->name.c_str());
if (e->episodeImage) {
tell(0, "Episode Image: %d x %d, Path: %s", e->episodeImage->width, e->episodeImage->height, e->episodeImage->path.c_str());
}
}
tell(0, "--------------------------- Season Posters ----------------------------------");
for (map<int, cTVDBMedia*>::iterator it = seasonPosters.begin(); it != seasonPosters.end(); it++) {
int season = it->first;
cTVDBMedia *m = it->second;
tell(0, "Season %d, %d x %d, Path: %s", season, m->width, m->height, m->path.c_str());
}
tell(0, "--------------------------- Actors ----------------------------------");
for (map<int, cTVDBActor*>::iterator it = actors.begin(); it != actors.end(); it++) {
cTVDBActor *a = it->second;
tell(0, "Actor %d, Name: %s, Role %s", a->id, a->name.c_str(), a->role.c_str());
if (a->actorThumb) {
tell(0, "Thumb: %d x %d, Path: %s", a->actorThumb->width, a->actorThumb->height, a->actorThumb->path.c_str());
}
}
if (posterThumb) {
tell(0, "posterThumb path %s, width %d, height %d", posterThumb->path.c_str(), posterThumb->width, posterThumb->height);
}
}

143
tvdbseries.h Normal file
View File

@ -0,0 +1,143 @@
#ifndef __TVSCRAPER_TVDBSERIES_H
#define __TVSCRAPER_TVDBSERIES_H
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <algorithm>
#include "services.h"
using namespace std;
enum mediaSeries {
msBanner1,
msBanner2,
msBanner3,
msPoster1,
msPoster2,
msPoster3,
msSeasonPoster,
msFanart1,
msFanart2,
msFanart3,
msEpisodePic,
msActorThumb,
msPosterThumb,
msSeasonPosterThumb,
};
// --- cTVDBMedia -------------------------------------------------------------
class cTVDBMedia {
public:
cTVDBMedia(void) {
path = "";
mediaType = msBanner1;
width = 0;
height = 0;
};
~cTVDBMedia(void) {
};
string path;
int mediaType;
int width;
int height;
};
// --- cTVDBEpisode -------------------------------------------------------------
class cTVDBEpisode {
public:
cTVDBEpisode(void) {
id = 0;
number = 0;
season = 0;
name = "";
firstAired = "";
guestStars = "";
overview = "";
rating = 0.0;
episodeImage = NULL;
};
~cTVDBEpisode(void) {
if (episodeImage)
delete episodeImage;
};
int id;
int number;
int season;
string name;
string firstAired;
string guestStars;
string overview;
float rating;
cTVDBMedia *episodeImage;
};
// --- cTVDBActor -------------------------------------------------------------
class cTVDBActor {
public:
cTVDBActor(void) {
id = 0;
name = "";
role = "";
thumbWidth = 0;
thumbHeight = 0;
actorThumb = NULL;
};
~cTVDBActor(void) {
if (actorThumb)
delete actorThumb;
};
int id;
string name;
string role;
int thumbWidth;
int thumbHeight;
cTVDBMedia *actorThumb;
};
// --- cTVDBSeries -------------------------------------------------------------
class cTVDBSeries {
private:
map<int, cTVDBEpisode*> episodes;
map<int, cTVDBActor*> actors;
vector<cTVDBMedia*> posters;
vector<cTVDBMedia*> banners;
vector<cTVDBMedia*> fanart;
map<int, cTVDBMedia*> seasonPosters;
map<int, cTVDBMedia*> seasonPosterThumbs;
cTVDBMedia *posterThumb;
public:
cTVDBSeries(void);
virtual ~cTVDBSeries(void);
int id;
string name;
string overview;
string firstAired;
string network;
string genre;
float rating;
string status;
void InsertEpisode(cTVDBEpisode *episode);
void InsertEpisodeImage(int episodeId, int width, int height, string path);
void InsertActor(cTVDBActor *actor);
void InsertActorThumb(int actorId, int imgWidth, int imgHeight, string path);
void InsertMedia(int mediaType, int imgWidth, int imgHeight, string path, int season = 0);
//Getter for Serivice Calls
void GetEpisode(int episodeId, cEpisode *e);
void GetPosters(vector<cTvMedia> *p);
bool GetPoster(cTvMedia *p);
bool GetPosterThumb(cTvMedia *p);
void GetBanners(vector<cTvMedia> *b);
bool GetRandomBanner(cTvMedia *b);
void GetFanart(vector<cTvMedia> *f);
void GetSeasonPoster(int episodeId, cTvMedia *sp);
void GetActors(vector<cActor> *a);
void Dump(void);
};
#endif //__TVSCRAPER_TVDBSERIES_H

1323
update.c Normal file

File diff suppressed because it is too large Load Diff

87
update.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef __UPDATE_H
#define __UPDATE_H
#include <mysql/mysql.h>
#include <map>
#include <vdr/thread.h>
#include "lib/common.h"
#include "lib/db.h"
#include "lib/tabledef.h"
#include "scrapmanager.h"
#define EPGDNAME "epgd"
class cUpdate : public cThread {
private:
cScrapManager *scrapManager;
string imgPathSeries;
string imgPathMovies;
bool withutf8;
bool loopActive;
cDbConnection* connection;
cTableVdrs* vdrDb;
cTableEvents* tEvents;
cTableSeries* tSeries;
cTableSeriesEpisode* tEpisodes;
cTableSeriesMedia* tSeriesMedia;
cTableSeriesActor* tSeriesActors;
cTableMovies* tMovies;
cTableMovieActor* tMovieActor;
cTableMovieActors* tMovieActors;
cTableMovieMedia* tMovieMedia;
cTableRecordings* tRecordings;
int lastScrap;
cCondVar waitCondition;
cMutex mutex;
bool forceUpdate;
bool forceRecordingUpdate;
bool forceVideoDirUpdate;
bool forceScrapInfoUpdate;
bool forceCleanupRecordingDb;
int exitDb();
int dbConnected(int force = no) { return connection && (!force || connection->check() == success); };
int CheckConnection(int& timeout);
bool CheckEpgdBusy(void);
void Action(void);
int ReadScrapedEvents(void);
//SERIES
int ReadSeries(bool isRec);
void ReadEpisode(int episodeId, cTVDBSeries *series, string path);
void LoadEpisodeImage(cTVDBSeries *series, int episodeId, string path);
void LoadSeasonPoster(cTVDBSeries *series, int season, string path);
void ReadSeriesActors(cTVDBSeries *series, string path);
void LoadSeriesMedia(cTVDBSeries *series, string path);
string LoadMediaSeries(int seriesId, int mediaType, string path, int width, int height);
void LoadSeriesActorThumb(cTVDBSeries *series, int actorId, string path);
//MOVIES
int ReadMovies(bool isRec);
void ReadMovieActors(cMovieDbMovie *movie);
void LoadMovieActorThumbs(cMovieDbMovie *movie);
void LoadMovieMedia(cMovieDbMovie *movie, string moviePath);
string LoadMediaMovie(int movieId, int mediaType, string path, int width, int height);
//RECORDINGS
int ReadRecordings(void);
int ScanVideoDir(void);
int ScanVideoDirScrapInfo(void);
bool LoadRecording(int eventId, string recName);
bool ScrapInfoChanged(int scrapInfoMovieID, int scrapInfoSeriesID, int scrapInfoEpisodeID);
void ReadScrapInfo(string recDir, int &scrapInfoMovieID, int &scrapInfoSeriesID, int &scrapInfoEpisodeID);
//CLEANUP
int CleanupSeries(void);
int CleanupMovies(void);
int CleanupRecordings(void);
public:
cUpdate(cScrapManager *manager);
virtual ~cUpdate(void);
int initDb();
void Stop(void);
void ForceUpdate(void);
void ForceRecordingUpdate(void);
void ForceVideoDirUpdate(void);
void ForceScrapInfoUpdate(void);
void TriggerCleanRecordingsDB(void);
};
//***************************************************************************
#endif //__UPDATE_H