Version 0.8.0 beta

This commit is contained in:
louis
2016-01-26 18:32:38 +01:00
parent 196dd7eb99
commit 809fbda03c
210 changed files with 24710 additions and 20849 deletions

100
extensions/cairoimage.c Normal file
View File

@@ -0,0 +1,100 @@
#include "cairoimage.h"
cCairoImage::cCairoImage(void) {
surface = NULL;
cr = NULL;
}
cCairoImage::~cCairoImage() {
if (cr)
cairo_destroy (cr);
if (surface)
cairo_surface_destroy (surface);
}
void cCairoImage::InitCairoImage(int width, int height) {
this->width = width;
this->height = height;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cr = cairo_create(surface);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_SUBPIXEL);
}
void cCairoImage::DrawTextVertical(string text, tColor color, string font, int size, int direction) {
int imgHeight = GetTextWidth(text, font, size);
InitCairoImage(size * 1.2, imgHeight);
SetColor(color);
cairo_font_weight_t fontWeight = CAIRO_FONT_WEIGHT_NORMAL;
cairo_font_slant_t fontSlant = CAIRO_FONT_SLANT_NORMAL;
cairo_select_font_face (cr, font.c_str(), fontSlant, fontWeight);
cairo_set_font_size (cr, size);
int x = size;
int y = imgHeight;
double rotate = 3*M_PI/2;
if (direction == (int)eDirection::topdown) {
rotate = M_PI/2;
x = size*0.3;
y = 0;
}
cairo_move_to (cr, x, y);
cairo_rotate(cr, rotate);
cairo_show_text (cr, text.c_str());
}
cImage *cCairoImage::GetImage(void) {
if (!cr || !surface)
return NULL;
unsigned char *data = cairo_image_surface_get_data(surface);
cImage *image = new cImage(cSize(width, height), (tColor*)data);
return image;
}
/**********************************************************************************
* Private Functions
**********************************************************************************/
int cCairoImage::GetTextWidth(string text, string font, int size) {
cairo_surface_t *tmpSurface;
cairo_t *tmpCr;
double width = 300;
double height = (double)size *1.3;
cairo_font_weight_t fontWeight = CAIRO_FONT_WEIGHT_NORMAL;
cairo_font_slant_t fontSlant = CAIRO_FONT_SLANT_NORMAL;
tmpSurface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
tmpCr = cairo_create (tmpSurface);
cairo_select_font_face (tmpCr, font.c_str(), fontSlant, fontWeight);
cairo_set_font_size (tmpCr, size);
cairo_text_extents_t te;
cairo_text_extents (tmpCr, text.c_str(), &te);
int textWidth = te.width;
cairo_destroy (tmpCr);
cairo_surface_destroy (tmpSurface);
return (double)textWidth * 1.1;
}
void cCairoImage::SetColor(tColor color) {
if (!cr || !surface)
return;
tIndex tAlpha = (color & 0xFF000000) >> 24;
tIndex tRed = (color & 0x00FF0000) >> 16;
tIndex tGreen = (color & 0x0000FF00) >> 8;
tIndex tBlue = (color & 0x000000FF);
double a = (int)tAlpha / (double)255;
double r = (int)tRed / (double)255;
double g = (int)tGreen / (double)255;
double b = (int)tBlue / (double)255;
cairo_set_source_rgba(cr, r, g, b, a);
}

27
extensions/cairoimage.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef __CAIROIMAGE_H
#define __CAIROIMAGE_H
#include <cairo.h>
#include <vdr/osd.h>
#include <string>
#include <sstream>
#include "../coreengine/definitions.h"
using namespace std;
class cCairoImage {
private:
int width;
int height;
cairo_surface_t *surface;
cairo_t *cr;
void SetColor(tColor color);
int GetTextWidth(string text, string font, int size);
public:
cCairoImage(void);
virtual ~cCairoImage();
void InitCairoImage(int width, int height);
void DrawTextVertical(string text, tColor color, string font, int size, int direction);
cImage *GetImage(void);
};
#endif //__CAIROIMAGE_H

237
extensions/curlfuncs.c Normal file
View File

@@ -0,0 +1,237 @@
/*
Copyright (c) 2002, Mayukh Bose
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Mayukh Bose nor the names of other
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Change History:
11/23/2004 - Removed the #include <unistd.h> line because I didn't
need it. Wonder why I had it there in the first place :).
10/20/2004 - Publicly released this code.
*/
#include <string>
#include <cstdio>
#include <curl/curl.h>
#include <curl/easy.h>
#include <vdr/tools.h>
#include "curlfuncs.h"
#ifndef TRUE
#define TRUE 1
#endif
using namespace std;
// Local function prototypes
int CurlDoPost(const char *url, string *sOutput, const string &sReferer,
struct curl_httppost *formpost, struct curl_slist *headerlist);
namespace curlfuncs {
string sBuf;
bool bInitialized = false;
CURL *curl = NULL;
}
size_t collect_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
string sTmp;
register size_t actualsize = size * nmemb;
if ((FILE *)stream == NULL) {
sTmp.assign((char *)ptr, actualsize);
curlfuncs::sBuf += sTmp;
}
else {
fwrite(ptr, size, nmemb, (FILE *)stream);
}
return actualsize;
}
inline void InitCurlLibraryIfNeeded()
{
if (!curlfuncs::bInitialized) {
curl_global_init(CURL_GLOBAL_ALL);
curlfuncs::curl = curl_easy_init();
if (!curlfuncs::curl)
throw string("Could not create new curl instance");
curl_easy_setopt(curlfuncs::curl, CURLOPT_NOPROGRESS, 1); // Do not show progress
curl_easy_setopt(curlfuncs::curl, CURLOPT_WRITEFUNCTION, collect_data);
curl_easy_setopt(curlfuncs::curl, CURLOPT_WRITEDATA, 0); // Set option to write to string
curl_easy_setopt(curlfuncs::curl, CURLOPT_FOLLOWLOCATION, TRUE);
curl_easy_setopt(curlfuncs::curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Mayukh's libcurl wrapper http://www.mayukhbose.com/)");
curl_easy_setopt(curlfuncs::curl, CURLOPT_TIMEOUT, 5L);
curlfuncs::bInitialized = true;
}
}
int CurlGetUrl(const char *url, string *sOutput, const string &sReferer)
{
InitCurlLibraryIfNeeded();
curl_easy_setopt(curlfuncs::curl, CURLOPT_URL, url); // Set the URL to get
if (sReferer != "")
curl_easy_setopt(curlfuncs::curl, CURLOPT_REFERER, sReferer.c_str());
curl_easy_setopt(curlfuncs::curl, CURLOPT_HTTPGET, TRUE);
curl_easy_setopt(curlfuncs::curl, CURLOPT_WRITEDATA, 0); // Set option to write to string
curlfuncs::sBuf = "";
if (curl_easy_perform(curlfuncs::curl) == 0)
*sOutput = curlfuncs::sBuf;
else {
// We have an error here mate!
*sOutput = "";
return 0;
}
return 1;
}
int CurlGetUrlFile(const char *url, const char *filename, const string &sReferer)
{
int nRet = 0;
InitCurlLibraryIfNeeded();
// Point the output to a file
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
return 0;
curl_easy_setopt(curlfuncs::curl, CURLOPT_WRITEDATA, fp); // Set option to write to file
curl_easy_setopt(curlfuncs::curl, CURLOPT_URL, url); // Set the URL to get
if (sReferer != "")
curl_easy_setopt(curlfuncs::curl, CURLOPT_REFERER, sReferer.c_str());
curl_easy_setopt(curlfuncs::curl, CURLOPT_HTTPGET, TRUE);
if (curl_easy_perform(curlfuncs::curl) == 0)
nRet = 1;
else
nRet = 0;
curl_easy_setopt(curlfuncs::curl, CURLOPT_WRITEDATA, NULL); // Set option back to default (string)
fclose(fp);
return nRet;
}
int CurlPostUrl(const char *url, const string &sPost, string *sOutput, const string &sReferer)
{
InitCurlLibraryIfNeeded();
int retval = 1;
string::size_type nStart = 0, nEnd, nPos;
string sTmp, sName, sValue;
struct curl_httppost *formpost=NULL;
struct curl_httppost *lastptr=NULL;
struct curl_slist *headerlist=NULL;
// Add the POST variables here
while ((nEnd = sPost.find("##", nStart)) != string::npos) {
sTmp = sPost.substr(nStart, nEnd - nStart);
if ((nPos = sTmp.find("=")) == string::npos)
return 0;
sName = sTmp.substr(0, nPos);
sValue = sTmp.substr(nPos+1);
curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, sName.c_str(), CURLFORM_COPYCONTENTS, sValue.c_str(), CURLFORM_END);
nStart = nEnd + 2;
}
sTmp = sPost.substr(nStart);
if ((nPos = sTmp.find("=")) == string::npos)
return 0;
sName = sTmp.substr(0, nPos);
sValue = sTmp.substr(nPos+1);
curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, sName.c_str(), CURLFORM_COPYCONTENTS, sValue.c_str(), CURLFORM_END);
retval = CurlDoPost(url, sOutput, sReferer, formpost, headerlist);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
return retval;
}
int CurlPostRaw(const char *url, const string &sPost, string *sOutput, const string &sReferer)
{
InitCurlLibraryIfNeeded();
int retval;
struct curl_httppost *formpost=NULL;
struct curl_slist *headerlist=NULL;
curl_easy_setopt(curlfuncs::curl, CURLOPT_POSTFIELDS, sPost.c_str());
curl_easy_setopt(curlfuncs::curl, CURLOPT_POSTFIELDSIZE, 0); //FIXME: Should this be the size instead, in case this is binary string?
retval = CurlDoPost(url, sOutput, sReferer, formpost, headerlist);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
return retval;
}
int CurlDoPost(const char *url, string *sOutput, const string &sReferer,
struct curl_httppost *formpost, struct curl_slist *headerlist)
{
headerlist = curl_slist_append(headerlist, "Expect:");
// Now do the form post
curl_easy_setopt(curlfuncs::curl, CURLOPT_URL, url);
if (sReferer != "")
curl_easy_setopt(curlfuncs::curl, CURLOPT_REFERER, sReferer.c_str());
curl_easy_setopt(curlfuncs::curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curlfuncs::curl, CURLOPT_WRITEDATA, 0); // Set option to write to string
curlfuncs::sBuf = "";
if (curl_easy_perform(curlfuncs::curl) == 0) {
*sOutput = curlfuncs::sBuf;
return 1;
}
else {
// We have an error here mate!
*sOutput = "";
return 0;
}
}
void FreeCurlLibrary(void)
{
if (curlfuncs::curl)
curl_easy_cleanup(curlfuncs::curl);
curl_global_cleanup();
curlfuncs::bInitialized = false;
}
int CurlSetCookieFile(char *filename)
{
InitCurlLibraryIfNeeded();
if (curl_easy_setopt(curlfuncs::curl, CURLOPT_COOKIEFILE, filename) != 0)
return 0;
if (curl_easy_setopt(curlfuncs::curl, CURLOPT_COOKIEJAR, filename) != 0)
return 0;
return 1;
}
char *CurlEscape(const char *url) {
InitCurlLibraryIfNeeded();
return curl_escape(url , strlen(url));
}

45
extensions/curlfuncs.h Normal file
View File

@@ -0,0 +1,45 @@
/*
Copyright (c) 2002, Mayukh Bose
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Mayukh Bose nor the names of other
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __CURLFUNCS_H_20020513__
#define __CURLFUNCS_H_20020513__
#include <string>
using namespace std;
int CurlGetUrl(const char *url, string *sOutput, const string &sReferer="");
int CurlGetUrlFile(const char *url, const char *filename, const string &sReferer="");
void FreeCurlLibrary(void);
int CurlSetCookieFile(char *filename);
int CurlPostUrl(const char *url, const string &sPost, string *sOutput, const string &sReferer = "");
int CurlPostRaw(const char *url, const string &sPost, string *sOutput, const string &sReferer = "");
char *CurlEscape(const char *url);
#endif

96
extensions/extrecinfo.c Normal file
View File

@@ -0,0 +1,96 @@
#include "../config.h"
#include "helpers.h"
#include "extrecinfo.h"
cExtRecInfo::cExtRecInfo(const char *xml) {
this->xml = xml;
}
cExtRecInfo::~cExtRecInfo(void) {
}
bool cExtRecInfo::Parse(void) {
//read media info
string mediaInfoXml;
StripXmlTag(xml, mediaInfoXml, "mediainfo");
if (mediaInfoXml.size() == 0) {
return false;
}
StripXmlTag(mediaInfoXml, resWidth, "res_width");
StripXmlTag(mediaInfoXml, resHeight, "res_height");
resString = GetScreenResolutionString(resWidth, resHeight, &isHD);
StripXmlTag(mediaInfoXml, aspectratio, "aspectratio");
isWideScreen = !aspectratio.compare("16:9");
StripXmlTag(mediaInfoXml, codec, "codec");
StripXmlTag(mediaInfoXml, format, "format");
StripXmlTag(mediaInfoXml, framerate, "framerate");
StripXmlTag(mediaInfoXml, interlace, "interlace");
size_t found = 0;
isDolby = false;
do {
string track;
found = StripXmlTag(mediaInfoXml, track, "track", found);
if (found == string::npos)
break;
tAudioTrack sTrack;
StripXmlTag(track, sTrack.codec, "codec");
StripXmlTag(track, sTrack.bitrate, "bitrate");
StripXmlTag(track, sTrack.language, "language");
if (!sTrack.codec.compare("AC-3"))
isDolby = true;
tracks.push_back(sTrack);
} while (found != string::npos);
return true;
}
//get content of <tag> ... </tag> inside xml
size_t cExtRecInfo::StripXmlTag(string &xmlstring, string &content, const char *tag, int start) {
// set the search strings
stringstream strStart, strStop;
strStart << "<" << tag << ">";
strStop << "</" << tag << ">";
// find the strings
size_t locStart = xmlstring.find(strStart.str(), start);
size_t locStop = xmlstring.find(strStop.str(), start);
if (locStart == string::npos || locStop == string::npos)
return string::npos;
// extract relevant text
int pos = locStart + strStart.str().size();
int len = locStop - pos;
content = (len < 0) ? "" : xmlstring.substr(pos, len);
return locStop + strStop.str().size();
}
size_t cExtRecInfo::StripXmlTag(string &xmlstring, int &content, const char *tag, int start) {
// set the search strings
stringstream strStart, strStop;
strStart << "<" << tag << ">";
strStop << "</" << tag << ">";
// find the strings
size_t locStart = xmlstring.find(strStart.str(), start);
size_t locStop = xmlstring.find(strStop.str(), start);
if (locStart == string::npos || locStop == string::npos)
return string::npos;
// extract relevant text
int pos = locStart + strStart.str().size();
int len = locStop - pos;
string value = (len < 0) ? "" : xmlstring.substr(pos, len);
content = atoi(value.c_str());
return locStop + strStop.str().size();
}
void cExtRecInfo::Debug(void) {
dsyslog("skindesigner: extRecInfo xml: %s", xml.c_str());
dsyslog("skindesigner: : res_width %d, res_height %d, res %s, aspectratio %s, codec %s, format %s, framerate %s, interlace %s, hd %s, widescreen %s",
resWidth, resHeight, resString.c_str(), aspectratio.c_str(), codec.c_str(), format.c_str(), framerate.c_str(), interlace.c_str(),
isHD ? "true": "false", isWideScreen ? "true" : "false");
int numTrack = 1;
for (vector<tAudioTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) {
dsyslog("skindesigner: audio track %d, codec %s, bitrate %s, language: %s", numTrack++, (*it).codec.c_str(), (*it).bitrate.c_str(), (*it).language.c_str());
}
}

36
extensions/extrecinfo.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef __EXTRECINFO_H
#define __EXTRECINFO_H
#include <vdr/recording.h>
struct tAudioTrack {
string codec;
string bitrate;
string language;
};
class cExtRecInfo {
private:
string xml;
size_t StripXmlTag(string &xmlstring, string &content, const char *tag, int start = 0);
size_t StripXmlTag(string &xmlstring, int &content, const char *tag, int start = 0);
public:
cExtRecInfo(const char *xml);
~cExtRecInfo(void);
bool Parse(void);
void Debug(void);
int resWidth;
int resHeight;
string resString;
bool isHD;
string aspectratio;
bool isWideScreen;
string codec;
string format;
string framerate;
string interlace;
bool isDolby;
vector< tAudioTrack > tracks;
};
#endif // __EXTRECINFO_H

181
extensions/fontmanager.c Normal file
View File

@@ -0,0 +1,181 @@
#include "fontmanager.h"
#include "../config.h"
#include <ft2build.h>
#include FT_FREETYPE_H
using namespace std;
cMutex cFontManager::mutex;
cFontManager::cFontManager(void) {
}
cFontManager::~cFontManager(void) {
DeleteFonts();
}
void cFontManager::Debug(void) {
dsyslog("skindesigner: fontmanager fonts available:");
for (map < string, map< int, cFont* > >::iterator fts = fonts.begin(); fts != fonts.end(); fts++) {
dsyslog("skindesigner: FontName %s", fts->first.c_str());
for (map<int, cFont*>::iterator ftSizes = (fts->second).begin(); ftSizes != (fts->second).end(); ftSizes++) {
int confHeight = ftSizes->first;
int realHeight = (ftSizes->second)->Height();
dsyslog("skindesigner: fontSize %d, fontHeight %d, ratio %f", confHeight, realHeight, (double)confHeight / (double)realHeight);
}
}
}
void cFontManager::ListAvailableFonts(void) {
cStringList availableFonts;
cFont::GetAvailableFontNames(&availableFonts);
int numFonts = availableFonts.Size();
esyslog("skindesigner: %d Fonts available:", numFonts);
for (int i=0; i<numFonts; i++) {
esyslog("skindesigner: font %d: %s", i, availableFonts[i]);
}
}
void cFontManager::DeleteFonts() {
cMutexLock MutexLock(&mutex);
for(map<string, map<int,cFont*> >::iterator it = fonts.begin(); it != fonts.end(); it++) {
for(map<int,cFont*>::iterator it2 = (it->second).begin(); it2 != (it->second).end(); it2++) {
delete it2->second;
}
}
fonts.clear();
}
int cFontManager::Width(string fontName, int fontSize, const char *text) {
cMutexLock MutexLock(&mutex);
if (!text)
return 0;
cFont *font = GetFont(fontName, fontSize);
//if not already cached, load it new
if (!font)
InsertFont(fontName, fontSize);
font = GetFont(fontName, fontSize);
if (!font)
return 0;
int width = font->Width(text);
return width;
}
int cFontManager::Height(string fontName, int fontSize) {
cMutexLock MutexLock(&mutex);
cFont *font = GetFont(fontName, fontSize);
//if not already cached, load it new
if (!font)
InsertFont(fontName, fontSize);
font = GetFont(fontName, fontSize);
if (!font)
return 0;
return font->Height();
}
cFont *cFontManager::Font(string fontName, int fontSize) {
cMutexLock MutexLock(&mutex);
cFont *font = GetFont(fontName, fontSize);
//if not already cached, load it new
if (!font)
InsertFont(fontName, fontSize);
font = GetFont(fontName, fontSize);
return font;
}
cFont *cFontManager::FontUncached(string fontName, int fontSize) {
cMutexLock MutexLock(&mutex);
cFont *font = CreateFont(fontName, fontSize);
return font;
}
/********************************************************************************
* Private Functions
********************************************************************************/
cFont *cFontManager::CreateFont(string name, int size) {
cMutexLock MutexLock(&mutex);
cFont *fontTmp = cFont::CreateFont(name.c_str(), size);
if (!fontTmp)
fontTmp = cFont::CreateFont(Setup.FontOsd, size);
int realHeight = fontTmp->Height();
delete fontTmp;
cFont *font = cFont::CreateFont(name.c_str(), (double)size / (double)realHeight * (double)size);
if (!font)
font = cFont::CreateFont(Setup.FontOsd, (double)size / (double)realHeight * (double)size);
return font;
}
void cFontManager::InsertFont(string name, int size) {
if (FontExists(name, size))
return;
cFont *newFont = CreateFont(name, size);
if (!newFont)
return;
map < string, map< int, cFont* > >::iterator hit = fonts.find(name);
if (hit != fonts.end()) {
(hit->second).insert(pair<int, cFont*>(size, newFont));
} else {
map<int, cFont*> fontsizes;
fontsizes.insert(pair<int, cFont*>(size, newFont));
fonts.insert(pair<string, map<int, cFont*> >(name, fontsizes));
}
}
bool cFontManager::FontExists(string name, int size) {
map < string, map< int, cFont* > >::iterator hit = fonts.find(name);
if (hit == fonts.end())
return false;
map< int, cFont* >::iterator hit2 = (hit->second).find(size);
if (hit2 == (hit->second).end())
return false;
return true;
}
cFont *cFontManager::GetFont(string name, int size) {
map< string, map<int,cFont*> >::iterator hitName = fonts.find(name);
if (hitName == fonts.end())
return NULL;
map<int,cFont*>::iterator hitSize = (hitName->second).find(size);
if (hitSize == (hitName->second).end())
return NULL;
return hitSize->second;
}
int cFontManager::GetFontHeight(const char *name, int height, int charWidth) {
FT_Library library;
FT_Face face;
cString fontFileName = cFont::GetFontFileName(name);
int descender = 0;
int y_ppem = 0;
int error = FT_Init_FreeType(&library);
if (error) return 0;
error = FT_New_Face(library, fontFileName, 0, &face);
if (error) return 0;
error = FT_Set_Char_Size(face, charWidth * 64, height * 64, 0, 0);
if (error) return 0;
descender = face->size->metrics.descender/64;
y_ppem = face->size->metrics.y_ppem;
int realHeight = y_ppem + descender;
FT_Done_Face(face);
FT_Done_FreeType(library);
return realHeight;
}
bool cFontManager::FontInstalled(string fontName) {
cStringList availableFonts;
cFont::GetAvailableFontNames(&availableFonts);
int numFonts = availableFonts.Size();
string compare = fontName + ":";
for (int i=0; i<numFonts; i++) {
string currentFont = availableFonts[i];
if (currentFont.find(compare) == 0) {
return true;
}
}
return false;
}

35
extensions/fontmanager.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef __FONTMANAGER_H
#define __FONTMANAGER_H
#include <string>
#include <map>
#include <vector>
#include <vdr/skins.h>
using namespace std;
class cFontManager {
private:
static cMutex mutex;
map < string, map< int, cFont* > > fonts;
cFont *CreateFont(string name, int size);
void InsertFont(string name, int size);
bool FontExists(string name, int size);
cFont *GetFont(string name, int size);
int GetFontHeight(const char *name, int height, int charWidth = 0);
public:
cFontManager(void);
~cFontManager(void);
void Lock(void) { mutex.Lock(); };
void Unlock(void) { mutex.Unlock(); };
void DeleteFonts(void);
int Width(string fontName, int fontSize, const char *text);
int Height(string fontName, int fontSize);
cFont *Font(string fontName, int fontSize);
cFont *FontUncached(string fontName, int fontSize);
void Debug(void);
void ListAvailableFonts(void);
bool FontInstalled(string fontName);
};
#endif //__FONTMANAGER_H

240
extensions/helpers.c Normal file
View File

@@ -0,0 +1,240 @@
#include <string>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include "helpers.h"
#include <vdr/skins.h>
cPlugin *GetScraperPlugin(void) {
static cPlugin *pScraper = cPluginManager::GetPlugin("scraper2vdr");
if( !pScraper ) // if it doesn't exit, try tvscraper
pScraper = cPluginManager::GetPlugin("tvscraper");
return pScraper;
}
cSize ScaleToFit(int widthMax, int heightMax, int widthOriginal, int heightOriginal) {
int width = 1;
int height = 1;
if ((widthMax == 0)||(heightMax==0)||(widthOriginal==0)||(heightOriginal==0))
return cSize(width, height);
if ((widthOriginal <= widthMax) && (heightOriginal <= heightMax)) {
width = widthOriginal;
height = heightOriginal;
} else if ((widthOriginal > widthMax) && (heightOriginal <= heightMax)) {
width = widthMax;
height = (double)width/(double)widthOriginal * heightOriginal;
} else if ((widthOriginal <= widthMax) && (heightOriginal > heightMax)) {
height = heightMax;
width = (double)height/(double)heightOriginal * widthOriginal;
} else {
width = widthMax;
height = (double)width/(double)widthOriginal * heightOriginal;
if (height > heightMax) {
height = heightMax;
width = (double)height/(double)heightOriginal * widthOriginal;
}
}
return cSize(width, height);
}
int Minimum(int a, int b, int c, int d, int e, int f) {
int min = a;
if (b < min) min = b;
if (c < min) min = c;
if (d < min) min = d;
if (e < min) min = e;
if (f < min) min = f;
return min;
}
string StrToLowerCase(string str) {
string lowerCase = str;
const int length = lowerCase.length();
for(int i=0; i < length; ++i) {
lowerCase[i] = std::tolower(lowerCase[i]);
}
return lowerCase;
}
bool isNumber(const string& s) {
string::const_iterator it = s.begin();
while (it != s.end() && std::isdigit(*it)) ++it;
return !s.empty() && it == s.end();
}
bool IsToken(const string& token) {
if ((token.find("{") == 0) && (token.find("}") == (token.size()-1)))
return true;
return false;
}
bool FileExists(const string &fullpath) {
struct stat buffer;
if (stat (fullpath.c_str(), &buffer) == 0) {
return true;
}
if (config.debugImageLoading) {
dsyslog("skindesigner: did not find %s", fullpath.c_str());
}
return false;
}
bool FileExists(const string &path, const string &name, const string &ext) {
stringstream fileName;
fileName << path << name << "." << ext;
struct stat buffer;
if (stat (fileName.str().c_str(), &buffer) == 0) {
return true;
}
if (config.debugImageLoading) {
dsyslog("skindesigner: did not find %s", fileName.str().c_str());
}
return false;
}
bool FolderExists(const string &path) {
struct stat buffer;
return stat(path.c_str(), &buffer) == 0 && S_ISDIR(buffer.st_mode);
}
bool FirstFileInFolder(string &path, string &extension, string &fileName) {
DIR *folder = NULL;
struct dirent *file;
folder = opendir(path.c_str());
if (!folder)
return false;
while (file = readdir(folder)) {
if (endswith(file->d_name, extension.c_str())) {
string currentFileName = file->d_name;
int strlength = currentFileName.size();
if (strlength < 8)
continue;
fileName = currentFileName;
return true;
}
}
return false;
}
void CreateFolder(string &path) {
cString command = cString::sprintf("mkdir -p %s", path.c_str());
int ok = system(*command);
if (!ok) {}
}
// 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));
}
// 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 < (int)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;
}
cStopWatch::cStopWatch(const char* message) {
start = cTimeMs::Now();
last = start;
if (message) {
dsyslog("skindesigner: Starting StopWatch %s", message);
}
}
void cStopWatch::Report(const char* message) {
dsyslog("skindesigner: %s - needed %d ms", message, (int)(cTimeMs::Now() - last));
last = cTimeMs::Now();
}
void cStopWatch::Stop(const char* message) {
dsyslog("skindesigner: %s - needed %d ms", message, (int)(cTimeMs::Now() - start));
}
string GetTimeString(int seconds) {
time_t sec(seconds);
tm *p = gmtime(&sec);
int hours = p->tm_hour;
int mins = p->tm_min;
int secs = p->tm_sec;
if (hours > 0) {
return *cString::sprintf("%d:%02d:%02d", hours, mins, secs);
}
return *cString::sprintf("%02d:%02d", mins, secs);;
}
//View Helpers
string GetScreenResolutionString(int width, int height, bool *isHD) {
string name = "";
switch (width) {
case 1920:
case 1440:
name = "hd1080i";
*isHD = true;
break;
case 1280:
if (height == 720)
name = "hd720p";
else
name = "hd1080i";
*isHD = true;
break;
case 720:
name = "sd576i";
break;
default:
name = "sd576i";
break;
}
return name;
}
string GetScreenAspectString(double aspect, bool *isWideScreen) {
string name = "";
*isWideScreen = false;
if (aspect == 4.0/3.0) {
name = "4:3";
*isWideScreen = false;
} else if (aspect == 16.0/9.0) {
name = "16:9";
*isWideScreen = true;
} else if (aspect == 2.21) {
name = "21:9";
*isWideScreen = true;
}
return name;
}

50
extensions/helpers.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef __HELPERS_H
#define __HELPERS_H
#include <string>
#include <algorithm>
#include <vdr/osd.h>
#include <vdr/plugin.h>
#include "../config.h"
cPlugin *GetScraperPlugin(void);
cSize ScaleToFit(int widthMax, int heightMax, int widthOriginal, int heightOriginal);
int Minimum(int a, int b, int c, int d, int e, int f);
std::string StrToLowerCase(string str);
bool isNumber(const string& s);
bool IsToken(const string& token);
bool FileExists(const string &fullpath);
bool FileExists(const string &path, const string &name, const string &ext);
bool FolderExists(const string &path);
bool FirstFileInFolder(string &path, string &extension, string &fileName);
void CreateFolder(string &path);
string &ltrim(string &s);
string &rtrim(string &s);
string &trim(string &s);
class splitstring : public std::string {
std::vector<std::string> flds;
public:
splitstring(const char *s) : std::string(s) { };
std::vector<std::string>& split(char delim, int rep=0);
};
class cStopWatch {
private:
uint64_t start;
uint64_t last;
public:
cStopWatch(const char* message = NULL);
~cStopWatch(void) {};
void Report(const char* message);
void Stop(const char* message);
};
string GetTimeString(int seconds);
string GetScreenResolutionString(int width, int height, bool *isHD);
string GetScreenAspectString(double aspect, bool *isWideScreen);
#endif // __HELPERS_H

579
extensions/imagecache.c Normal file
View File

@@ -0,0 +1,579 @@
#include <string>
#include <sstream>
#include <map>
#include <fstream>
#include <iostream>
#include <sys/stat.h>
#include "imagecache.h"
#include "cairoimage.h"
#include "../config.h"
#include "helpers.h"
cMutex cImageCache::mutex;
string cImageCache::items[16] = { "Schedule", "Channels", "Timers", "Recordings", "Setup", "Commands",
"OSD", "EPG", "DVB", "LNB", "CAM", "Recording", "Replay", "Miscellaneous", "Plugins", "Restart"};
cImageCache::cImageCache() {
tempStaticLogo = NULL;
}
cImageCache::~cImageCache() {
Clear();
if (tempStaticLogo) {
delete tempStaticLogo;
tempStaticLogo = NULL;
}
}
void cImageCache::SetPathes(void) {
cString skinPath = config.GetSkinPath(Setup.OSDSkin);
string logoPathSkin = *cString::sprintf("%s%s/themes/%s/logos/", *skinPath, Setup.OSDSkin, Setup.OSDTheme);
if (FolderExists(logoPathSkin)) {
logoPath = logoPathSkin;
} else {
logoPath = *config.logoPath;
}
iconPathSkin = *cString::sprintf("%s%s/", *skinPath, Setup.OSDSkin);
skinPartsPathSkin = *cString::sprintf("%s%s/skinparts/", *skinPath, Setup.OSDSkin);
iconPathTheme = *cString::sprintf("%s%s/themes/%s/", *skinPath, Setup.OSDSkin, Setup.OSDTheme);
skinPartsPathTheme = *cString::sprintf("%s%s/themes/%s/skinparts/", *skinPath, Setup.OSDSkin, Setup.OSDTheme);
svgTemplatePath = *cString::sprintf("%s%s/svgtemplates/", *skinPath, Setup.OSDSkin);
dsyslog("skindesigner: using channel logo path %s", logoPath.c_str());
dsyslog("skindesigner: using icon path %s", iconPathTheme.c_str());
dsyslog("skindesigner: using skinparts path %s", skinPartsPathTheme.c_str());
dsyslog("skindesigner: using svgtemplate path %s", svgTemplatePath.c_str());
}
void cImageCache::CacheLogo(int width, int height) {
if (config.numLogosPerSizeInitial == 0)
return;
if (width == 0 || height == 0)
return;
int logosCached = 0;
if (config.numLogosMax && config.numLogosMax < (int)channelLogoCache.size())
return;
for (const cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
if (logosCached >= config.numLogosPerSizeInitial)
break;
if (channel->GroupSep()) {
continue;
}
stringstream logoName;
logoName << *channel->GetChannelID().ToString() << "_" << width << "x" << height;
map<string, cImage*>::iterator hit = channelLogoCache.find(logoName.str());
if (hit != channelLogoCache.end()) {
continue;
}
bool success = LoadLogo(channel);
if (success) {
logosCached++;
cImage *image = CreateImage(width, height);
channelLogoCache.insert(pair<string, cImage*>(logoName.str(), image));
}
if (config.numLogosMax && config.numLogosMax < (int)channelLogoCache.size())
return;
}
}
cImage *cImageCache::GetLogo(string channelID, int width, int height) {
cMutexLock MutexLock(&mutex);
stringstream logoName;
logoName << channelID << "_" << width << "x" << height;
std::map<std::string, cImage*>::iterator hit = channelLogoCache.find(logoName.str());
if (hit != channelLogoCache.end()) {
return (cImage*)hit->second;
} else {
tChannelID chanID = tChannelID::FromString(channelID.c_str());
const cChannel *channel = Channels.GetByChannelID(chanID);
if (!channel)
return NULL;
bool success = LoadLogo(channel);
if (success) {
if (config.limitLogoCache && ((int)channelLogoCache.size() >= config.numLogosMax)) {
//logo cache is full, don't cache anymore
if (tempStaticLogo) {
delete tempStaticLogo;
tempStaticLogo = NULL;
}
tempStaticLogo = CreateImage(width, height);
return tempStaticLogo;
} else {
//add requested logo to cache
cImage *image = CreateImage(width, height);
channelLogoCache.insert(pair<string, cImage*>(logoName.str(), image));
hit = channelLogoCache.find(logoName.str());
if (hit != channelLogoCache.end()) {
return (cImage*)hit->second;
}
}
}
}
return NULL;
}
cImage *cImageCache::GetSeparatorLogo(string name, int width, int height) {
cMutexLock MutexLock(&mutex);
stringstream logoName;
logoName << name << "_" << width << "x" << height;
std::map<std::string, cImage*>::iterator hit = channelLogoCache.find(logoName.str());
if (hit != channelLogoCache.end()) {
return (cImage*)hit->second;
} else {
bool success = LoadSeparatorLogo(name);
if (success) {
//add requested logo to cache
cImage *image = CreateImage(width, height);
channelLogoCache.insert(pair<string, cImage*>(logoName.str(), image));
hit = channelLogoCache.find(logoName.str());
if (hit != channelLogoCache.end()) {
return (cImage*)hit->second;
}
}
}
return NULL;
}
bool cImageCache::LogoExists(string channelID) {
tChannelID chanID = tChannelID::FromString(channelID.c_str());
const cChannel *channel = Channels.GetByChannelID(chanID);
if (!channel)
return false;
string logoLower = StrToLowerCase(channel->Name());
string channelIDLower = StrToLowerCase(channelID.c_str());
return (FileExists(logoPath.c_str(), logoLower, "svg") ||
FileExists(logoPath.c_str(), logoLower, "png") ||
FileExists(logoPath.c_str(), channelIDLower, "svg") ||
FileExists(logoPath.c_str(), channelIDLower, "png"));
}
bool cImageCache::SeparatorLogoExists(string name) {
string separatorPath = *cString::sprintf("%sseparatorlogos/", logoPath.c_str());
string nameLower = StrToLowerCase(name.c_str());
return (FileExists(separatorPath, nameLower, "svg") ||
FileExists(separatorPath, nameLower, "png"));
}
void cImageCache::CacheIcon(eImageType type, string name, int width, int height) {
if (width < 1 || width > 1920 || height < 1 || height > 1080)
return;
GetIcon(type, name, width, height);
}
cCachedImage *cImageCache::GetIcon(eImageType type, string name, int width, int height) {
if (width < 1 || width > 1920 || height < 1 || height > 1080)
return NULL;
cMutexLock MutexLock(&mutex);
stringstream iconName;
iconName << name << "_" << width << "x" << height;
map<string, cCachedImage*>::iterator hit = iconCache.find(iconName.str());
if (hit != iconCache.end()) {
return (cCachedImage*)hit->second;
} else {
bool success = LoadIcon(type, name);
if (!success)
return NULL;
cImage *image = CreateImage(width, height, true);
cCachedImage *cachedImg = new cCachedImage();
cachedImg->size = image->Width() * image->Height() * sizeof(tColor);
int handle = cOsdProvider::StoreImage(*image);
if (handle) {
cachedImg->handle = handle;
delete image;
} else {
cachedImg->image = image;
}
iconCache.insert(pair<string, cCachedImage*>(iconName.str(), cachedImg));
hit = iconCache.find(iconName.str());
if (hit != iconCache.end()) {
return (cCachedImage*)hit->second;
}
}
return NULL;
}
string cImageCache::GetIconName(string label, eMenuCategory cat, string plugName) {
//if cat is set, use standard menu entries
switch (cat) {
case mcSchedule:
case mcScheduleNow:
case mcScheduleNext:
case mcEvent:
return "standardicons/Schedule";
case mcChannel:
case mcChannelEdit:
return "standardicons/Channels";
case mcTimer:
case mcTimerEdit:
return "standardicons/Timers";
case mcRecording:
case mcRecordingInfo:
case mcSetupRecord:
case mcSetupReplay:
return "standardicons/Recordings";
case mcPlugin: {
//check for Plugins
for (int i = 0; ; i++) {
cPlugin *p = cPluginManager::GetPlugin(i);
if (p) {
const char *mainMenuEntry = p->MainMenuEntry();
if (mainMenuEntry) {
string plugMainEntry = mainMenuEntry;
try {
if (label.substr(0, plugMainEntry.size()) == plugMainEntry) {
return *cString::sprintf("pluginicons/%s", p->Name());
}
} catch (...) {}
}
} else
break;
}
return "standardicons/Plugins";
}
case mcPluginSetup:
case mcSetupPlugins:
return "standardicons/Plugins";
case mcSetup:
return "standardicons/Setup";
case mcSetupOsd:
return "standardicons/OSD";
case mcSetupEpg:
return "standardicons/EPG";
case mcSetupDvb:
return "standardicons/DVB";
case mcSetupLnb:
return "standardicons/LNB";
case mcSetupCam:
return "standardicons/CAM";
case mcSetupMisc:
return "standardicons/Miscellaneous";
case mcCommand:
return "standardicons/Commands";
default:
break;
}
//check for standard menu entries
for (int i=0; i<16; i++) {
string s = trVDR(items[i].c_str());
if (s == label) {
return *cString::sprintf("standardicons/%s", items[i].c_str());
}
}
//check for special main menu entries "stop recording", "stop replay"
string stopRecording = skipspace(trVDR(" Stop recording "));
string stopReplay = skipspace(trVDR(" Stop replaying"));
try {
if (label.substr(0, stopRecording.size()) == stopRecording) {
return "standardicons/StopRecording";
}
if (label.substr(0, stopReplay.size()) == stopReplay) {
return "standardicons/StopReplay";
}
} catch (...) {}
//check for Plugins
if (plugName.size() > 0) {
return *cString::sprintf("pluginicons/%s", plugName.c_str());
}
for (int i = 0; ; i++) {
cPlugin *p = cPluginManager::GetPlugin(i);
if (p) {
const char *mainMenuEntry = p->MainMenuEntry();
if (mainMenuEntry) {
string plugMainEntry = mainMenuEntry;
try {
if (label.substr(0, plugMainEntry.size()) == plugMainEntry) {
return *cString::sprintf("pluginicons/%s", p->Name());
}
} catch (...) {}
}
} else
break;
}
return *cString::sprintf("customicons/%s", label.c_str());
}
bool cImageCache::MenuIconExists(string name) {
//first check in theme specific icon folder
cString iconThemePath = cString::sprintf("%smenuicons/", iconPathTheme.c_str());
if (FileExists(*iconThemePath, name, "svg")) {
return true;
}
if (FileExists(*iconThemePath, name, "png")) {
return true;
}
//then check skin icon folder
cString iconSkinPath = cString::sprintf("%smenuicons/", iconPathSkin.c_str());
if (FileExists(*iconSkinPath, name, "svg")) {
return true;
}
if (FileExists(*iconSkinPath, name, "png")) {
return true;
}
return false;
}
void cImageCache::CacheSkinpart(string name, int width, int height) {
if (width < 1 || width > 1920 || height < 1 || height > 1080)
return;
GetSkinpart(name, width, height);
}
cCachedImage *cImageCache::GetSkinpart(string name, int width, int height) {
if (width < 1 || width > 1920 || height < 1 || height > 1080)
return NULL;
cMutexLock MutexLock(&mutex);
stringstream iconName;
iconName << name << "_" << width << "x" << height;
map<string, cCachedImage*>::iterator hit = skinPartsCache.find(iconName.str());
if (hit != skinPartsCache.end()) {
return (cCachedImage*)hit->second;
} else {
bool success = LoadSkinpart(name);
if (!success)
return NULL;
cImage *image = CreateImage(width, height, false);
cCachedImage *cachedImg = new cCachedImage();
cachedImg->size = image->Width() * image->Height() * sizeof(tColor);
int handle = cOsdProvider::StoreImage(*image);
if (handle) {
cachedImg->handle = handle;
delete image;
} else {
cachedImg->image = image;
}
skinPartsCache.insert(pair<string, cCachedImage*>(iconName.str(), cachedImg));
hit = skinPartsCache.find(iconName.str());
if (hit != skinPartsCache.end()) {
return (cCachedImage*)hit->second;
}
}
return NULL;
}
cImage *cImageCache::GetVerticalText(string text, tColor color, string font, int size, int direction) {
cMutexLock MutexLock(&mutex);
stringstream buf;
buf << text << "_" << size << "_" << direction;
string imgName = buf.str();
map<string, cImage*>::iterator hit = cairoImageCache.find(imgName);
if (hit != cairoImageCache.end()) {
return (cImage*)hit->second;
} else {
cCairoImage c;
c.DrawTextVertical(text, color, font, size, direction);
cImage *image = c.GetImage();
cairoImageCache.insert(pair<string, cImage*>(imgName, image));
hit = cairoImageCache.find(imgName);
if (hit != cairoImageCache.end()) {
return (cImage*)hit->second;
}
}
return NULL;
}
bool cImageCache::LoadIcon(eImageType type, string name) {
cString subdir("");
if (type == eImageType::menuicon)
subdir = "menuicons";
else if (type == eImageType::icon)
subdir = "icons";
//first check in theme specific icon path
cString subIconThemePath = cString::sprintf("%s%s/", iconPathTheme.c_str(), *subdir);
if (FileExists(*subIconThemePath, name, "svg"))
return LoadImage(*subIconThemePath, name, "svg");
else if (FileExists(*subIconThemePath, name, "png"))
return LoadImage(*subIconThemePath, name, "png");
//then check in skin icon path
cString subIconSkinPath = cString::sprintf("%s%s/", iconPathSkin.c_str(), *subdir);
if (FileExists(*subIconSkinPath, name, "svg"))
return LoadImage(*subIconSkinPath, name, "svg");
else if (FileExists(*subIconSkinPath, name, "png"))
return LoadImage(*subIconSkinPath, name, "png");
//and finally check if a svg template exists
cSVGTemplate svgTemplate(name, svgTemplatePath);
if (!svgTemplate.Exists())
return false;
svgTemplate.ReadTemplate();
if (!svgTemplate.ParseTemplate())
return false;
string tmpImageName = svgTemplate.WriteImage();
return LoadImage(tmpImageName.c_str());
}
bool cImageCache::LoadLogo(const cChannel *channel) {
if (!channel)
return false;
string channelID = StrToLowerCase(*(channel->GetChannelID().ToString()));
string logoLower = StrToLowerCase(channel->Name());
if (FileExists(logoPath.c_str(), channelID.c_str(), "svg"))
return LoadImage(logoPath.c_str(), channelID.c_str(), "svg");
if (FileExists(logoPath.c_str(), channelID.c_str(), "png"))
return LoadImage(logoPath.c_str(), channelID.c_str(), "png");
if (FileExists(logoPath.c_str(), logoLower.c_str(), "svg"))
return LoadImage(logoPath.c_str(), logoLower.c_str(), "svg");
if (FileExists(logoPath.c_str(), logoLower.c_str(), "png"))
return LoadImage(logoPath.c_str(), logoLower.c_str(), "png");
return false;
}
bool cImageCache::LoadSeparatorLogo(string name) {
string separatorPath = *cString::sprintf("%sseparatorlogos/", logoPath.c_str());
string nameLower = StrToLowerCase(name.c_str());
if (FileExists(separatorPath, nameLower.c_str(), "svg"))
return LoadImage(separatorPath, nameLower.c_str(), "svg");
else
return LoadImage(separatorPath, nameLower.c_str(), "png");
}
bool cImageCache::LoadSkinpart(string name) {
if (FileExists(skinPartsPathTheme.c_str(), name, "svg"))
return LoadImage(skinPartsPathTheme.c_str(), name, "svg");
else if (FileExists(skinPartsPathTheme.c_str(), name, "png"))
return LoadImage(skinPartsPathTheme.c_str(), name, "png");
else if (FileExists(skinPartsPathSkin.c_str(), name, "svg"))
return LoadImage(skinPartsPathSkin.c_str(), name, "svg");
else if (FileExists(skinPartsPathSkin.c_str(), name, "png"))
return LoadImage(skinPartsPathSkin.c_str(), name, "png");
//check if a svg template exists
cSVGTemplate svgTemplate(name, svgTemplatePath);
if (!svgTemplate.Exists())
return false;
svgTemplate.ReadTemplate();
if (!svgTemplate.ParseTemplate())
return false;
string tmpImageName = svgTemplate.WriteImage();
return LoadImage(tmpImageName.c_str());
}
void cImageCache::Clear(void) {
for(map<string, cCachedImage*>::const_iterator it = iconCache.begin(); it != iconCache.end(); it++) {
cCachedImage *img = (cCachedImage*)it->second;
delete img;
}
iconCache.clear();
for(map<string, cImage*>::const_iterator it = channelLogoCache.begin(); it != channelLogoCache.end(); it++) {
cImage *img = (cImage*)it->second;
delete img;
}
channelLogoCache.clear();
for(map<string, cCachedImage*>::const_iterator it = skinPartsCache.begin(); it != skinPartsCache.end(); it++) {
cCachedImage *img = (cCachedImage*)it->second;
delete img;
}
skinPartsCache.clear();
for(map<string, cImage*>::const_iterator it = cairoImageCache.begin(); it != cairoImageCache.end(); it++) {
cImage *img = (cImage*)it->second;
delete img;
}
cairoImageCache.clear();
}
void cImageCache::Debug(bool full) {
float sizeIconCacheInternal = 0;
float sizeIconCacheExternal = 0;
int numIcons = 0;
GetIconCacheSize(numIcons, sizeIconCacheInternal, sizeIconCacheExternal);
dsyslog("skindesigner: cached %d icons - size internal mem %.2fMB, high level mem %.2fMB", numIcons, sizeIconCacheInternal, sizeIconCacheExternal);
if (full) {
for(std::map<std::string, cCachedImage*>::const_iterator it = iconCache.begin(); it != iconCache.end(); it++) {
string name = it->first;
cCachedImage *img = (cCachedImage*)it->second;
dsyslog("skindesigner: cached icon %s, handle %d", name.c_str(), img->handle);
}
}
float sizeLogoCache = 0;
int numLogos = 0;
GetLogoCacheSize(numLogos, sizeLogoCache);
dsyslog("skindesigner: cached %d logos - size %.2fMB internal mem", numLogos, sizeLogoCache);
if (full) {
for(std::map<std::string, cImage*>::const_iterator it = channelLogoCache.begin(); it != channelLogoCache.end(); it++) {
string name = it->first;
dsyslog("skindesigner: cached logo %s", name.c_str());
}
}
float sizeSkinpartCacheInternal = 0;
float sizeSkinpartCacheExternal = 0;
int numSkinparts = 0;
GetSkinpartsCacheSize(numSkinparts, sizeSkinpartCacheInternal, sizeSkinpartCacheExternal);
dsyslog("skindesigner: cached %d skinparts - size internal mem %.2fMB, high level mem %.2fMB", numSkinparts, sizeSkinpartCacheInternal, sizeSkinpartCacheExternal);
if (full) {
for(std::map<std::string, cCachedImage*>::const_iterator it = skinPartsCache.begin(); it != skinPartsCache.end(); it++) {
string name = it->first;
dsyslog("skindesigner: cached skinpart %s", name.c_str());
}
}
}
void cImageCache::GetIconCacheSize(int &num, float &sizeInternal, float &sizeExternal) {
num = iconCache.size();
int sizeByteInternal = 0;
int sizeByteExternal = 0;
for (map<string, cCachedImage*>::iterator icon = iconCache.begin(); icon != iconCache.end(); icon++) {
cCachedImage* img = icon->second;
if (img->image)
sizeByteInternal += img->size;
else
sizeByteExternal += img->size;
}
sizeInternal = sizeByteInternal / 1024.0f / 1024.0f;
sizeExternal = sizeByteExternal / 1024.0f / 1024.0f;
}
void cImageCache::GetLogoCacheSize(int &num, float &size) {
num = channelLogoCache.size();
int sizeByte = 0;
for (map<string, cImage*>::iterator logo = channelLogoCache.begin(); logo != channelLogoCache.end(); logo++) {
cImage* img = logo->second;
sizeByte += img->Width() * img->Height() * sizeof(tColor);
}
size = sizeByte / 1024.0f;
}
void cImageCache::GetSkinpartsCacheSize(int &num, float &sizeInternal, float &sizeExternal) {
num = skinPartsCache.size();
int sizeByteInternal = 0;
int sizeByteExternal = 0;
for (map<string, cCachedImage*>::iterator skinpart = skinPartsCache.begin(); skinpart != skinPartsCache.end(); skinpart++) {
cCachedImage* img = skinpart->second;
if (img->image)
sizeByteInternal += img->size;
else
sizeByteExternal += img->size;
}
sizeInternal = sizeByteInternal / 1024.0f / 1024.0f;
sizeExternal = sizeByteExternal / 1024.0f / 1024.0f;
}

79
extensions/imagecache.h Normal file
View File

@@ -0,0 +1,79 @@
#ifndef __NOPACITY_IMAGECACHE_H
#define __NOPACITY_IMAGECACHE_H
#define X_DISPLAY_MISSING
#include <vdr/osd.h>
#include <vdr/skins.h>
#include <vector>
#include "imageloader.h"
#include "../coreengine/definitions.h"
class cCachedImage {
public:
int handle;
cImage *image;
int size;
cCachedImage(void) {
handle = 0;
image = NULL;
size = 0;
};
~cCachedImage(void) {
if (handle)
cOsdProvider::DropImage(handle);
if (image)
delete image;
};
};
class cImageCache : public cImageLoader {
public:
cImageCache();
~cImageCache();
void Lock(void) { mutex.Lock(); }
void Unlock(void) { mutex.Unlock(); }
void SetPathes(void);
//channel logos
void CacheLogo(int width, int height);
cImage *GetLogo(string channelID, int width, int height);
bool LogoExists(string channelID);
cImage *GetSeparatorLogo(string name, int width, int height);
bool SeparatorLogoExists(string name);
//icons
void CacheIcon(eImageType type, string path, int width, int height);
cCachedImage *GetIcon(eImageType type, string name, int width, int height);
string GetIconName(string label, eMenuCategory cat = mcUndefined, string plugName = "");
bool MenuIconExists(string name);
//skinparts
void CacheSkinpart(string path, int width, int height);
cCachedImage *GetSkinpart(string name, int width, int height);
//cairo special images
cImage *GetVerticalText(string text, tColor color, string font, int size, int direction);
//helpers
void Clear(void);
void Debug(bool full);
void GetIconCacheSize(int &num, float &sizeInternal, float &sizeExternal);
void GetLogoCacheSize(int &num, float &size);
void GetSkinpartsCacheSize(int &num, float &sizeInternal, float &sizeExternal);
private:
static cMutex mutex;
static string items[16];
cImage *tempStaticLogo;
string logoPath;
string iconPathSkin;
string skinPartsPathSkin;
string iconPathTheme;
string skinPartsPathTheme;
string svgTemplatePath;
map<string, cCachedImage*> iconCache;
map<string, cImage*> channelLogoCache;
map<string, cCachedImage*> skinPartsCache;
map<string, cImage*> cairoImageCache;
bool LoadIcon(eImageType type, string name);
bool LoadLogo(const cChannel *channel);
bool LoadSeparatorLogo(string name);
bool LoadSkinpart(string name);
};
#endif //__NOPACITY_IMAGECACHE_H

507
extensions/imageloader.c Normal file
View File

@@ -0,0 +1,507 @@
#include "../config.h"
#include "helpers.h"
#include "imageloader.h"
#include <string>
#include <dirent.h>
#include <iostream>
#include <fstream>
cImageLoader::cImageLoader() {
importer = NULL;
}
cImageLoader::~cImageLoader() {
delete(importer);
}
cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) {
if (!importer)
return NULL;
int w, h;
importer->GetImageSize(w, h);
if (width == 0)
width = w;
if (height == 0)
height = h;
cairo_surface_t *surface;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_t *cr;
cr = cairo_create(surface);
double sx = width / (double)w;
double sy = height / (double)h;
if (preserveAspect) {
double tx = 0;
double ty = 0;
if (sx < sy) {
sy = sx;
ty = (height - h * sy) / 2;
}
if (sy < sx) {
sx = sy;
tx = (width - w * sx) / 2;
}
cairo_translate(cr, tx, ty);
}
cairo_scale(cr, sx, sy);
importer->DrawToCairo(cr);
cairo_status_t status = cairo_status(cr);
if (status && config.debugImageLoading)
dsyslog("skindesigner: Cairo CreateImage Error %s", cairo_status_to_string(status));
unsigned char *data = cairo_image_surface_get_data(surface);
cImage *image = new cImage(cSize(width, height), (tColor*)data);
cairo_destroy(cr);
cairo_surface_destroy(surface);
return image;
}
bool cImageLoader::LoadImage(const char *fullpath) {
if ((fullpath == NULL) || (strlen(fullpath) < 5))
return false;
if (config.debugImageLoading)
dsyslog("skindesigner: trying to load: %s", fullpath);
delete(importer);
importer = NULL;
if (endswith(fullpath, ".png"))
importer = new cImageImporterPNG;
else if (endswith(fullpath, ".svg"))
importer = new cImageImporterSVG;
else if (endswith(fullpath, ".jpg"))
importer = new cImageImporterJPG;
else {
importer = cImageImporter::CreateImageImporter(fullpath);
if (!importer)
return false;
}
return importer->LoadImage(fullpath);
}
// Just a different way to call LoadImage. Calls the above one.
bool cImageLoader::LoadImage(std::string Path, std::string FileName, std::string Extension) {
std::stringstream sstrImgFile;
sstrImgFile << Path << FileName << "." << Extension;
std::string imgFile = sstrImgFile.str();
return LoadImage(imgFile.c_str());
}
cImageImporter* cImageImporter::CreateImageImporter(const char* path) {
char pngSig[] = { char(0x89), char(0x50), char(0x4E), char(0x47), char(0x0D), char(0x0A), char(0x1A), char(0x0A) };
char jpgSig[] = { char(0xFF), char(0xD8), char(0xFF), char(0xD9) };
char buffer[8] = { 0 };
ifstream f(path, ios::in | ios::binary);
f.read(buffer, 8);
if (!f)
return NULL;
if(buffer[0] == jpgSig[0] && buffer[1] == jpgSig[1]) {
f.seekg(-2, f.end);
f.read(buffer, 2);
if(buffer[0] == jpgSig[2] && buffer[1] == jpgSig[3]) {
f.close();
return new cImageImporterJPG;
}
} else if(buffer[0] == pngSig[0]
&& buffer[1] == pngSig[1]
&& buffer[2] == pngSig[2]
&& buffer[3] == pngSig[3]
&& buffer[4] == pngSig[4]
&& buffer[5] == pngSig[5]
&& buffer[6] == pngSig[6]
&& buffer[7] == pngSig[7]) {
f.close();
return new cImageImporterPNG;
}
f.close();
return NULL;
}
//
// Image importer for PNG
//
cImageImporterPNG::cImageImporterPNG() {
surface = NULL;
}
cImageImporterPNG::~cImageImporterPNG() {
if (surface)
cairo_surface_destroy(surface);
}
bool cImageImporterPNG::LoadImage(const char *path) {
if (surface)
cairo_surface_destroy(surface);
surface = cairo_image_surface_create_from_png(path);
if (cairo_surface_status(surface)) {
if (config.debugImageLoading)
dsyslog("skindesigner: Cairo LoadImage Error: %s", cairo_status_to_string(cairo_surface_status(surface)));
surface = NULL;
return false;
}
return true;
}
void cImageImporterPNG::DrawToCairo(cairo_t *cr) {
if (surface) {
cairo_set_source_surface(cr, surface, 0, 0);
cairo_paint(cr);
}
}
void cImageImporterPNG::GetImageSize(int &width, int &height) {
if (surface) {
width = cairo_image_surface_get_width(surface);
height = cairo_image_surface_get_height(surface);
}
}
//
// Image importer for SVG
//
cImageImporterSVG::cImageImporterSVG() {
handle = NULL;
}
cImageImporterSVG::~cImageImporterSVG() {
if (handle) {
rsvg_handle_close(handle, NULL);
g_object_unref(handle);
}
}
bool cImageImporterSVG::LoadImage(const char *path) {
if (handle) {
rsvg_handle_close(handle, NULL);
g_object_unref(handle);
}
GError *error = NULL;
handle = rsvg_handle_new_from_file(path, &error);
if (!handle) {
if (config.debugImageLoading && error) {
dsyslog("skindesigner: RSVG Error: %s", error->message);
}
return false;
}
// 90 dpi is the hardcoded default setting of the Inkscape SVG editor
rsvg_handle_set_dpi(handle, 90);
return true;
}
void cImageImporterSVG::DrawToCairo(cairo_t *cr) {
if (handle)
rsvg_handle_render_cairo(handle, cr);
}
void cImageImporterSVG::GetImageSize(int &width, int &height) {
if (handle) {
RsvgDimensionData dim;
rsvg_handle_get_dimensions(handle, &dim);
width = dim.width;
height = dim.height;
}
}
void cImageImporterSVG::InitLibRSVG() {
#if !GLIB_CHECK_VERSION(2, 35, 0)
g_type_init();
#endif
}
//
// Image importer for JPG
//
struct my_error_mgr {
struct jpeg_error_mgr pub; // "public" fields
jmp_buf setjmp_buffer; // for return to caller
};
METHODDEF(void)
my_error_exit(j_common_ptr cinfo) {
// cinfo->err really points to a my_error_mgr struct, so coerce pointer
my_error_mgr *myerr = (my_error_mgr*) cinfo->err;
// Always display the message.
(*cinfo->err->output_message) (cinfo);
// Return control to the setjmp point
longjmp(myerr->setjmp_buffer, 1);
}
METHODDEF(void)
my_output_message(j_common_ptr cinfo) {
char buf[JMSG_LENGTH_MAX];
cinfo->err->format_message(cinfo, buf);
if (config.debugImageLoading)
dsyslog("skindesigner: libjpeg error: %s", buf);
}
cImageImporterJPG::cImageImporterJPG() {
cinfo = NULL;
}
cImageImporterJPG::~cImageImporterJPG() {
if (cinfo) {
jpeg_destroy_decompress(cinfo);
free(cinfo);
fclose(infile);
}
}
bool cImageImporterJPG::LoadImage(const char *path) {
if (cinfo) {
jpeg_destroy_decompress(cinfo);
free(cinfo);
fclose(infile);
cinfo = NULL;
}
// Open input file
if ((infile = fopen(path, "rb")) == NULL) {
if (config.debugImageLoading)
dsyslog("skindesigner: Can't open %s", path);
return false;
}
// Allocate space for our decompress struct
cinfo = (j_decompress_ptr)malloc(sizeof(struct jpeg_decompress_struct));
// We set up the normal JPEG error routines, then override error_exit
// and output_message.
struct my_error_mgr jerr;
cinfo->err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
// Establish the setjmp return context for my_error_exit to use.
if (setjmp(jerr.setjmp_buffer)) {
// If we get here, the JPEG code has signaled an error.
jpeg_destroy_decompress(cinfo);
free(cinfo);
fclose(infile);
cinfo = NULL;
return false;
}
// Now we can initialize the JPEG decompression object.
jpeg_create_decompress(cinfo);
// Step 2: specify data source (eg, a file)
jpeg_stdio_src(cinfo, infile);
// Step 3: read file parameters with jpeg_read_header()
(void) jpeg_read_header(cinfo, TRUE);
return true;
}
void cImageImporterJPG::DrawToCairo(cairo_t *cr) {
if (!cinfo)
return;
unsigned char *bmp_buffer = NULL;
// Re-establish error handling. We have to do this again as the saved
// calling environment of "LoadImage" is invalid if we reach here!
struct my_error_mgr jerr;
cinfo->err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(cinfo);
free(cinfo);
fclose(infile);
free(bmp_buffer);
cinfo = NULL;
return;
}
// Step 4: set parameters for decompression
cinfo->out_color_space = JCS_RGB;
// Step 5: Start decompressor
(void) jpeg_start_decompress(cinfo);
// Allocate buffer. Directly allocate the space needed for ARGB
unsigned int width = cinfo->output_width;
unsigned int height = cinfo->output_height;
bmp_buffer = (unsigned char*)malloc(width * height * 4);
// Step 6: while (scan lines remain to be read)
int jpg_stride = width * cinfo->output_components;
while (cinfo->output_scanline < height) {
unsigned char *buffer_array[1];
buffer_array[0] = bmp_buffer + (cinfo->output_scanline) * jpg_stride;
jpeg_read_scanlines(cinfo, buffer_array, 1);
}
// Step 7: Finish decompression.
(void)jpeg_finish_decompress(cinfo);
// Cleanup. In this "ImageImporter" we clean up everything in "DrawToCairo"
// as I'm not really sure whether we are able to draw a second time.
fclose(infile);
jpeg_destroy_decompress(cinfo);
free(cinfo);
cinfo = NULL;
// --> At this point we have raw RGB data in bmp_buffer
// Do some ugly byte shifting.
// Byte order in libjpeg: RGB
// Byte order in cairo and VDR: BGRA
unsigned char temp[3];
for (int index = (width * height) - 1; index >= 0; index--) {
unsigned char *target = bmp_buffer + (index * 4);
unsigned char *source = bmp_buffer + (index * 3);
memcpy(&temp[0], source + 2, 1);
memcpy(&temp[1], source + 1, 1);
memcpy(&temp[2], source, 1);
memcpy(target, &temp, 3);
}
// Create new Cairo surface from our raw image data
cairo_surface_t *surface;
surface = cairo_image_surface_create_for_data(bmp_buffer,
CAIRO_FORMAT_RGB24,
width,
height,
width * 4);
// Draw surface to Cairo
if (surface) {
cairo_set_source_surface(cr, surface, 0, 0);
cairo_paint(cr);
cairo_surface_destroy(surface);
}
// Free our memory
free(bmp_buffer);
}
void cImageImporterJPG::GetImageSize(int &width, int &height) {
if (cinfo) {
width = cinfo->image_width;
height = cinfo->image_height;
}
}
//
// SVG Template class
//
cSVGTemplate::cSVGTemplate(string imageName, string templatePath) {
this->imageName = imageName;
this->templatePath = templatePath;
startTokenColor = "{sdcol(";
startTokenOpac = "{sdopac(";
endToken = ")}";
filePath = templatePath;
filePath += imageName + ".svg";
}
cSVGTemplate::~cSVGTemplate(void) {
}
bool cSVGTemplate::Exists(void) {
return FileExists(templatePath, imageName, "svg");
}
void cSVGTemplate::ReadTemplate(void) {
string line;
ifstream templatefile(filePath.c_str());
if (templatefile.is_open()) {
while ( getline (templatefile, line) ) {
svgTemplate.push_back(line);
}
templatefile.close();
}
}
bool cSVGTemplate::ParseTemplate(void) {
int i = 0;
for (vector<string>::iterator it = svgTemplate.begin(); it != svgTemplate.end(); it++) {
string line = *it;
size_t hit = line.find(startTokenColor);
if (hit == string::npos) {
i++;
continue;
}
while (hit != string::npos) {
size_t hitEnd = line.find(endToken, hit);
if (hitEnd == string::npos) {
esyslog("skindesigner: error in SVG Template %s: invalid tag", imageName.c_str());
return false;
}
string colorName = GetColorName(line, hit, hitEnd);
tColor replaceColor = 0x0;
if (!config.GetThemeColor(colorName, replaceColor)) {
esyslog("skindesigner: error in SVG Template %s: invalid color %x", imageName.c_str(), replaceColor);
return false;
}
ReplaceTokens(line, hit, hitEnd, replaceColor);
hit = line.find(startTokenColor);
}
svgTemplate[i] = line;
i++;
}
return true;
}
string cSVGTemplate::WriteImage(void) {
string tempPath = *cString::sprintf("/tmp/skindesigner/svg/%s/%s/", Setup.OSDSkin, Setup.OSDTheme);
CreateFolder(tempPath);
string fileName = tempPath + imageName + ".svg";
ofstream tmpimg;
tmpimg.open (fileName.c_str(), ios::out | ios::trunc);
if (!tmpimg.is_open()) {
return "";
}
for (vector<string>::iterator it = svgTemplate.begin(); it != svgTemplate.end(); it++) {
tmpimg << (*it) << "\n";
}
tmpimg.close();
return fileName;
}
string cSVGTemplate::GetColorName(string line, size_t tokenStart, size_t tokenEnd) {
string colorName = line.substr(tokenStart + startTokenColor.size(), tokenEnd - tokenStart - startTokenColor.size());
if (colorName.size() > 0) {
stringstream name;
name << "{" << colorName << "}";
return name.str();
}
return "";
}
void cSVGTemplate::ReplaceTokens(string &line, size_t tokenStart, size_t tokenEnd, tColor color) {
string rgbColor = *cString::sprintf("%06x", color & 0x00FFFFFF);
line.replace(tokenStart, tokenEnd - tokenStart + 2, rgbColor);
size_t hitAlpha = line.find(startTokenOpac);
if (hitAlpha == string::npos) {
return;
}
size_t hitAlphaEnd = line.find(endToken, hitAlpha);
if (hitAlphaEnd == string::npos) {
return;
}
tIndex alpha = (color & 0xFF000000) >> 24;
string svgAlpha = *cString::sprintf("%f", (float)(alpha / (float)255));
std::replace( svgAlpha.begin(), svgAlpha.end(), ',', '.');
line.replace(hitAlpha, hitAlphaEnd - hitAlpha + 2, svgAlpha);
}

115
extensions/imageloader.h Normal file
View File

@@ -0,0 +1,115 @@
#ifndef __SKINDESIGNER_IMAGELOADER_H
#define __SKINDESIGNER_IMAGELOADER_H
#include <string>
#include <cairo.h>
#include <librsvg/rsvg.h>
#ifndef LIBRSVG_CHECK_VERSION // Workaround for librsvg < 2.36.2
#include <librsvg/rsvg-cairo.h>
#include <librsvg/librsvg-features.h>
#endif
#include <jpeglib.h>
#include <setjmp.h>
#include <vdr/osd.h>
#include <vdr/tools.h>
using namespace std;
//
// Image importers
//
class cImageImporter {
public:
cImageImporter() {};
virtual ~cImageImporter() {};
virtual bool LoadImage(const char *path) { return false; };
virtual void DrawToCairo(cairo_t *cr) {};
virtual void GetImageSize(int &width, int &height) {};
static cImageImporter* CreateImageImporter(const char* path);
};
// Image importer for PNG
class cImageImporterPNG : public cImageImporter {
public:
cImageImporterPNG();
~cImageImporterPNG();
bool LoadImage(const char *path);
void DrawToCairo(cairo_t *cr);
void GetImageSize(int &width, int &height);
private:
cairo_surface_t *surface;
};
// Image importer for SVG
#if !LIBRSVG_CHECK_VERSION(2, 36, 0)
#error librsvg version 2.36.0 or above required!
#endif
class cImageImporterSVG : public cImageImporter {
public:
cImageImporterSVG();
~cImageImporterSVG();
bool LoadImage(const char *path);
void DrawToCairo(cairo_t *cr);
void GetImageSize(int &width, int &height);
static void InitLibRSVG();
private:
RsvgHandle *handle;
};
// Image importer for JPG
#if BITS_IN_JSAMPLE != 8
#error libjpeg has to be compiled with 8-bit samples!
#endif
class cImageImporterJPG : public cImageImporter {
public:
cImageImporterJPG();
~cImageImporterJPG();
bool LoadImage(const char *path);
void DrawToCairo(cairo_t *cr);
void GetImageSize(int &width, int &height);
private:
j_decompress_ptr cinfo;
FILE *infile;
};
//
// Image loader class
//
class cImageLoader {
private:
cImageImporter *importer;
public:
cImageLoader();
virtual ~cImageLoader();
cImage *CreateImage(int width, int height, bool preserveAspect = true);
bool LoadImage(std::string Path, std::string FileName, std::string Extension);
bool LoadImage(const char *fullpath);
};
//
// SVG Template class
//
class cSVGTemplate {
private:
string imageName;
string templatePath;
string filePath;
string startTokenColor;
string startTokenOpac;
string endToken;
vector<string> svgTemplate;
string GetColorName(string line, size_t tokenStart, size_t tokenEnd);
void ReplaceTokens(string &line, size_t tokenStart, size_t tokenEnd, tColor color);
public:
cSVGTemplate(string imageName, string templatePath);
virtual ~cSVGTemplate(void);
bool Exists(void);
void ReadTemplate(void);
bool ParseTemplate(void);
string WriteImage(void);
};
#endif //__SKINDESIGNER_IMAGELOADER_H

189
extensions/libxmlwrapper.c Normal file
View File

@@ -0,0 +1,189 @@
#include "helpers.h"
#include "libxmlwrapper.h"
void SkinDesignerXMLErrorHandler (void * userData, xmlErrorPtr error) {
esyslog("skindesigner: Error in XML: %s", error->message);
}
cLibXMLWrapper::cLibXMLWrapper(void) {
ctxt = NULL;
doc = NULL;
root = NULL;
initGenericErrorDefaultFunc(NULL);
xmlSetStructuredErrorFunc(NULL, SkinDesignerXMLErrorHandler);
ctxt = xmlNewParserCtxt();
}
cLibXMLWrapper::~cLibXMLWrapper() {
DeleteDocument();
xmlFreeParserCtxt(ctxt);
}
void cLibXMLWrapper::InitLibXML() {
xmlInitParser();
}
void cLibXMLWrapper::CleanupLibXML() {
xmlCleanupParser();
}
void cLibXMLWrapper::DeleteDocument(void) {
if (doc) {
xmlFreeDoc(doc);
doc = NULL;
}
while (!nodeStack.empty())
nodeStack.pop();
}
bool cLibXMLWrapper::ReadXMLFile(const char *path, bool validate) {
if (!ctxt) {
esyslog("skindesigner: Failed to allocate parser context");
return false;
}
if (!FileExists(path)) {
dsyslog("skindesigner: reading XML Template %s failed", path);
return false;
}
if (validate)
doc = xmlCtxtReadFile(ctxt, path, NULL, XML_PARSE_NOENT | XML_PARSE_DTDVALID);
else
doc = xmlCtxtReadFile(ctxt, path, NULL, XML_PARSE_NOENT);
if (!doc) {
dsyslog("skindesigner: reading XML Template %s failed", path);
return false;
}
return true;
}
bool cLibXMLWrapper::SetDocument(void) {
if (!doc)
return false;
root = xmlDocGetRootElement(doc);
nodeStack.push(root);
if (root == NULL) {
esyslog("skindesigner: ERROR: XML File is empty");
return false;
}
return true;
}
bool cLibXMLWrapper::Validate(void) {
if (!ctxt)
return false;
if (ctxt->valid == 0) {
esyslog("skindesigner: Failed to validate XML File");
return false;
}
return true;
}
bool cLibXMLWrapper::CheckNodeName(const char *name) {
if (nodeStack.empty())
return false;
xmlNodePtr current = nodeStack.top();
if (xmlStrcmp(current->name, (const xmlChar *) name)) {
return false;
}
return true;
}
const char *cLibXMLWrapper::NodeName(void) {
xmlNodePtr current = nodeStack.top();
return (const char*)current->name;
}
vector<stringpair> cLibXMLWrapper::ParseAttributes(void) {
vector<stringpair> attributes;
if (nodeStack.empty())
return attributes;
xmlNodePtr current = nodeStack.top();
xmlAttrPtr attrPtr = current->properties;
if (attrPtr == NULL) {
return attributes;
}
while (NULL != attrPtr) {
string name = (const char*)attrPtr->name;
xmlChar *value = NULL;
value = xmlGetProp(current, attrPtr->name);
if (value)
attributes.push_back(stringpair((const char*)attrPtr->name, (const char*)value));
attrPtr = attrPtr->next;
if (value)
xmlFree(value);
}
return attributes;
}
bool cLibXMLWrapper::LevelDown(void) {
if (nodeStack.empty())
return false;
xmlNodePtr current = nodeStack.top();
xmlNodePtr child = current->xmlChildrenNode;
while (child && child->type != XML_ELEMENT_NODE) {
child = child->next;
}
if (!child)
return false;
nodeStack.push(child);
return true;
}
bool cLibXMLWrapper::LevelUp(void) {
if (nodeStack.size() == 1)
return false;
nodeStack.pop();
return true;
}
bool cLibXMLWrapper::NextNode(void) {
xmlNodePtr current = nodeStack.top();
current = current->next;
while (current && current->type != XML_ELEMENT_NODE) {
current = current->next;
}
if (!current)
return false;
nodeStack.pop();
nodeStack.push(current);
return true;
}
bool cLibXMLWrapper::GetAttribute(string &name, string &value) {
bool ok = false;
xmlNodePtr current = nodeStack.top();
xmlAttrPtr attr = current->properties;
if (attr == NULL) {
return ok;
}
xmlChar *xmlValue = NULL;
while (NULL != attr) {
if (xmlStrcmp(attr->name, (const xmlChar *) name.c_str())) {
attr = attr->next;
continue;
}
ok = true;
xmlValue = xmlGetProp(current, attr->name);
if (xmlValue) {
value = (const char*)xmlValue;
xmlFree(xmlValue);
}
break;
}
return ok;
}
bool cLibXMLWrapper::GetNodeValue(string &value) {
xmlChar *val = NULL;
xmlNodePtr current = nodeStack.top();
val = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
if (val) {
value = (const char*)val;
xmlFree(val);
return true;
}
value = "";
return false;
}

View File

@@ -0,0 +1,46 @@
#ifndef __LIBXMLWRAPPER_H
#define __LIBXMLWRAPPER_H
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <stack>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlerror.h>
#include <vdr/plugin.h>
using namespace std;
typedef pair<string,string> stringpair;
typedef map<string,string> stringmap;
class cLibXMLWrapper {
private:
xmlParserCtxtPtr ctxt;
xmlDocPtr doc;
xmlNodePtr root;
xmlNodePtr current;
stack<xmlNodePtr> nodeStack;
protected:
void DeleteDocument(void);
bool ReadXMLFile(const char *path, bool validate = true);
bool SetDocument(void);
bool Validate(void);
bool CheckNodeName(const char *name);
const char *NodeName(void);
vector<stringpair> ParseAttributes(void);
bool LevelDown(void);
bool LevelUp(void);
bool NextNode(void);
bool GetAttribute(string &name, string &value);
bool GetNodeValue(string &value);
public:
cLibXMLWrapper(void);
virtual ~cLibXMLWrapper(void);
static void InitLibXML();
static void CleanupLibXML();
};
#endif //__LIBXMLWRAPPER_H

301
extensions/pluginmanager.c Normal file
View File

@@ -0,0 +1,301 @@
#include "pluginmanager.h"
cSDPluginManager::cSDPluginManager(void) {
lastId = 0;
subviewsfound = false;
}
cSDPluginManager::~cSDPluginManager(void) {
}
void cSDPluginManager::Reset(void) {
subViewMapping.clear();
}
void cSDPluginManager::RegisterBasicPlugin(skindesignerapi::cPluginStructure *plugStructure) {
dsyslog("skindesigner: plugin %s uses libskindesigner API Version %s",
plugStructure->name.c_str(),
plugStructure->libskindesignerAPIVersion.c_str());
plugStructure->id = lastId;
registeredPlugins.insert(pair<int,string>(lastId, plugStructure->name));
lastId++;
map< int, skindesignerapi::sPlugMenu > menusNew;
for (map< int, skindesignerapi::sPlugMenu >::iterator it = plugStructure->menus.begin(); it != plugStructure->menus.end(); it++) {
int key = it->first;
skindesignerapi::sPlugMenu menu = it->second;
skindesignerapi::sPlugMenu menuNew;
menuNew.type = menu.type;
menuNew.tplname = menu.tplname;
menuNew.tokenContainer = new skindesignerapi::cTokenContainer(*menu.tokenContainer);
menusNew.insert(pair<int, skindesignerapi::sPlugMenu>(key, menuNew));
}
pluginMenus.insert(pair< int, map < int, skindesignerapi::sPlugMenu > >(plugStructure->id, menusNew));
if (plugStructure->menus.size() > 0)
dsyslog("skindesigner: plugin %s has registered %ld menus", plugStructure->name.c_str(), plugStructure->menus.size());
}
int cSDPluginManager::GetNumPluginMenus(void) {
int numMenusTotal = 0;
for (map < int, map < int, skindesignerapi::sPlugMenu > >::iterator it = pluginMenus.begin(); it != pluginMenus.end(); it++) {
numMenusTotal += (it->second).size();
}
return numMenusTotal;
}
void cSDPluginManager::InitPluginMenuIterator(void) {
plugMenuIt = pluginMenus.begin();
}
map <int,skindesignerapi::sPlugMenu> *cSDPluginManager::GetPluginMenus(string &name, int &id) {
if (plugMenuIt == pluginMenus.end())
return NULL;
id = plugMenuIt->first;
map<int,string>::iterator hit = registeredPlugins.find(id);
if (hit != registeredPlugins.end())
name = hit->second;
map <int,skindesignerapi::sPlugMenu> *templates = &plugMenuIt->second;
plugMenuIt++;
return templates;
}
skindesignerapi::cTokenContainer *cSDPluginManager::GetTokenContainer(int plugId, int plugMenuId) {
map <int, map<int, skindesignerapi::sPlugMenu> >::iterator hit = pluginMenus.find(plugId);
if (hit == pluginMenus.end())
return NULL;
map<int, skindesignerapi::sPlugMenu>::iterator hit2 = (hit->second).find(plugMenuId);
if (hit2 == (hit->second).end())
return NULL;
skindesignerapi::cTokenContainer *tk = hit2->second.tokenContainer;
return tk;
}
void cSDPluginManager::AddSubviewMapping(int plugId, int plugMenuId, int subViewId) {
map <int, map<int,int> >::iterator hit = subViewMapping.find(plugId);
if (hit == subViewMapping.end()) {
map<int,int> menus;
menus.insert(pair<int,int>(plugMenuId, subViewId));
subViewMapping.insert(pair<int, map<int,int> >(plugId, menus));
} else {
(hit->second).insert(pair<int,int>(plugMenuId, subViewId));
}
}
int cSDPluginManager::GetSubviewId(int plugId, int plugMenuId) {
map <int, map<int,int> >::iterator hit = subViewMapping.find(plugId);
if (hit == subViewMapping.end())
return -1;
map<int,int>::iterator hit2 = (hit->second).find(plugMenuId);
if (hit2 == (hit->second).end())
return -1;
return hit2->second;
}
void cSDPluginManager::RegisterAdvancedPlugin(skindesignerapi::cPluginStructure *plugStructure) {
dsyslog("skindesigner: plugin %s uses libskindesigner API Version %s",
plugStructure->name.c_str(),
plugStructure->libskindesignerAPIVersion.c_str());
plugStructure->id = lastId;
registeredPlugins.insert(pair<int,string>(lastId, plugStructure->name));
lastId++;
rootviews.insert(pair<int,string>(plugStructure->id, plugStructure->rootview));
subviews.insert(pair<int,map<int,string> >(plugStructure->id, plugStructure->subviews));
multimap< int, skindesignerapi::sPlugViewElement > viewelementsNew;
for (map< int, skindesignerapi::sPlugViewElement >::iterator it = plugStructure->viewelements.begin(); it != plugStructure->viewelements.end(); it++) {
int key = it->first;
skindesignerapi::sPlugViewElement ve = it->second;
skindesignerapi::sPlugViewElement veNew;
veNew.id = ve.id;
veNew.viewId = ve.viewId;
veNew.name = ve.name;
veNew.tokenContainer = new skindesignerapi::cTokenContainer(*ve.tokenContainer);
viewelementsNew.insert(pair<int, skindesignerapi::sPlugViewElement>(key, veNew));
}
viewelements.insert(pair< int, multimap < int, skindesignerapi::sPlugViewElement > >(plugStructure->id, viewelementsNew));
multimap< int, skindesignerapi::sPlugViewGrid > viewgridsNew;
for (map< int, skindesignerapi::sPlugViewGrid >::iterator it = plugStructure->viewgrids.begin(); it != plugStructure->viewgrids.end(); it++) {
int key = it->first;
skindesignerapi::sPlugViewGrid vg = it->second;
skindesignerapi::sPlugViewGrid vgNew;
vgNew.id = vg.id;
vgNew.viewId = vg.viewId;
vgNew.name = vg.name;
vgNew.tokenContainer = new skindesignerapi::cTokenContainer(*vg.tokenContainer);
viewgridsNew.insert(pair<int, skindesignerapi::sPlugViewGrid>(key, vgNew));
}
viewgrids.insert(pair< int, multimap < int, skindesignerapi::sPlugViewGrid > >(plugStructure->id, viewgridsNew));
map< int, skindesignerapi::cTokenContainer* > viewtabsNew;
for (map<int,skindesignerapi::cTokenContainer*>::iterator it = plugStructure->viewtabs.begin(); it != plugStructure->viewtabs.end(); it++) {
int id = it->first;
skindesignerapi::cTokenContainer *tk = it->second;
viewtabsNew.insert(pair<int,skindesignerapi::cTokenContainer*>(id, new skindesignerapi::cTokenContainer(*tk)));
}
viewtabs.insert(pair< int, map<int,skindesignerapi::cTokenContainer*> >(plugStructure->id, viewtabsNew));
if (plugStructure->rootview.size() > 0)
dsyslog("skindesigner: plugin %s has registered %ld views with %ld viewelements and %ld viewgrids",
plugStructure->name.c_str(),
1 + plugStructure->subviews.size(),
plugStructure->viewelements.size(),
plugStructure->viewgrids.size());
}
void cSDPluginManager::InitPluginViewIterator(void) {
rootViewsIt = rootviews.begin();
}
bool cSDPluginManager::GetNextPluginView(string &plugName, int &plugId, string &tplName) {
if (rootViewsIt == rootviews.end())
return false;
plugId = rootViewsIt->first;
tplName = rootViewsIt->second;
map<int,string>::iterator hit = registeredPlugins.find(plugId);
if (hit != registeredPlugins.end())
plugName = hit->second;
rootViewsIt++;
return true;
}
int cSDPluginManager::GetNumSubviews(int plugId) {
map< int, map< int, string > >::iterator hit = subviews.find(plugId);
if (hit == subviews.end())
return 0;
return (hit->second).size();
}
void cSDPluginManager::InitPluginSubviewIterator(int plugId) {
map< int, map< int, string > >::iterator hit = subviews.find(plugId);
if (hit == subviews.end()) {
subviewsfound = false;
return;
}
subviewsCurrent = hit->second;
subviewsfound = true;
svIt = subviewsCurrent.begin();
}
bool cSDPluginManager::GetNextSubView(int &id, string &tplname) {
if (!subviewsfound)
return false;
if( svIt == subviewsCurrent.end() ) {
return false;
}
id = svIt->first;
tplname = svIt->second;
svIt++;
return true;
}
int cSDPluginManager::GetNumViewElements(int plugId, int viewId) {
map< int, multimap< int, skindesignerapi::sPlugViewElement > >::iterator hit = viewelements.find(plugId);
if (hit == viewelements.end())
return 0;
multimap<int, skindesignerapi::sPlugViewElement> *plugVEs = &hit->second;
pair<multimap<int, skindesignerapi::sPlugViewElement>::iterator, multimap<int, skindesignerapi::sPlugViewElement>::iterator> range;
range = plugVEs->equal_range(viewId);
int numVEs = 0;
for (multimap<int, skindesignerapi::sPlugViewElement>::iterator it=range.first; it!=range.second; ++it) {
numVEs++;
}
return numVEs;
}
void cSDPluginManager::InitViewElementIterator(int plugId, int viewId) {
map< int, multimap< int, skindesignerapi::sPlugViewElement > >::iterator hit = viewelements.find(plugId);
if (hit == viewelements.end())
return;
multimap<int, skindesignerapi::sPlugViewElement> *plugVEs = &hit->second;
veRange = plugVEs->equal_range(viewId);
veIt = veRange.first;
}
bool cSDPluginManager::GetNextViewElement(int &veId, string &veName) {
if (veIt == veRange.second)
return false;
skindesignerapi::sPlugViewElement *ve = &veIt->second;
veId = ve->id;
veName = ve->name;
veIt++;
return true;
}
skindesignerapi::cTokenContainer *cSDPluginManager::GetTokenContainerVE(int plugId, int viewId, int veId) {
map< int, multimap< int, skindesignerapi::sPlugViewElement > >::iterator hit = viewelements.find(plugId);
if (hit == viewelements.end())
return NULL;
multimap<int, skindesignerapi::sPlugViewElement> *plugVEs = &hit->second;
for (multimap<int, skindesignerapi::sPlugViewElement>::iterator it = plugVEs->begin(); it != plugVEs->end(); it++) {
int view = it->first;
if (view != viewId)
continue;
skindesignerapi::sPlugViewElement *ve = &it->second;
if (ve->id == veId)
return ve->tokenContainer;
}
return NULL;
}
int cSDPluginManager::GetNumViewGrids(int plugId, int viewId) {
map< int, multimap< int, skindesignerapi::sPlugViewGrid > >::iterator hit = viewgrids.find(plugId);
if (hit == viewgrids.end())
return 0;
multimap<int, skindesignerapi::sPlugViewGrid> *plugVGs = &hit->second;
pair<multimap<int, skindesignerapi::sPlugViewGrid>::iterator, multimap<int, skindesignerapi::sPlugViewGrid>::iterator> range;
range = plugVGs->equal_range(viewId);
int numVGs = 0;
for (multimap<int, skindesignerapi::sPlugViewGrid>::iterator it=range.first; it!=range.second; ++it) {
numVGs++;
}
return numVGs;
}
void cSDPluginManager::InitViewGridIterator(int plugId, int viewId) {
map< int, multimap< int, skindesignerapi::sPlugViewGrid > >::iterator hit = viewgrids.find(plugId);
if (hit == viewgrids.end())
return;
multimap<int, skindesignerapi::sPlugViewGrid> *plugGEs = &hit->second;
gRange = plugGEs->equal_range(viewId);
gIt = gRange.first;
}
bool cSDPluginManager::GetNextViewGrid(int &gId, string &gName) {
if (gIt == gRange.second)
return false;
skindesignerapi::sPlugViewGrid *ge = &gIt->second;
gId = ge->id;
gName = ge->name;
gIt++;
return true;
}
skindesignerapi::cTokenContainer *cSDPluginManager::GetTokenContainerGE(int plugId, int viewId, int gId) {
map< int, multimap< int, skindesignerapi::sPlugViewGrid > >::iterator hit = viewgrids.find(plugId);
if (hit == viewgrids.end())
return NULL;
multimap<int, skindesignerapi::sPlugViewGrid> *plugGEs = &hit->second;
for (multimap<int, skindesignerapi::sPlugViewGrid>::iterator it = plugGEs->begin(); it != plugGEs->end(); it++) {
int view = it->first;
if (view != viewId)
continue;
skindesignerapi::sPlugViewGrid *g = &it->second;
if (g->id == gId)
return g->tokenContainer;
}
return NULL;
}
skindesignerapi::cTokenContainer *cSDPluginManager::GetTokenContainerTab(int plugId, int viewId) {
map< int, map< int, skindesignerapi::cTokenContainer* > >::iterator hit = viewtabs.find(plugId);
if (hit == viewtabs.end())
return NULL;
map< int, skindesignerapi::cTokenContainer* > *tabs = &hit->second;
map< int, skindesignerapi::cTokenContainer* >::iterator hit2 = tabs->find(viewId);
if (hit2 == tabs->end())
return NULL;
return (hit2->second);
}

View File

@@ -0,0 +1,71 @@
#ifndef __PLUGINMANAGER_H
#define __PLUGINMANAGER_H
#include <string>
#include <map>
#include "libskindesignerapi/skindesignerapi.h"
using namespace std;
class cSDPluginManager {
private:
int lastId;
//plugin id --> plugin name
map < int, string > registeredPlugins;
//Basic Plugin Interface
//plugin id --> plugin definition
map < int, map < int, skindesignerapi::sPlugMenu > > pluginMenus;
map < int, map < int, skindesignerapi::sPlugMenu > >::iterator plugMenuIt;
//plugin id - menuId --> subviewid
map < int, map<int, int> > subViewMapping;
//Advanced Plugin Interface
//plugin id --> rootview templatename definition
map< int, string > rootviews;
map< int, string >::iterator rootViewsIt;
//plugin id --> subviewid /templatename definition
map< int, map< int, string > > subviews;
map< int, string> subviewsCurrent;
map< int, string>::iterator svIt;
bool subviewsfound;
//plugin id --> view id --> viewelement definition
map< int, multimap< int, skindesignerapi::sPlugViewElement > > viewelements;
pair<multimap<int, skindesignerapi::sPlugViewElement>::iterator, multimap<int, skindesignerapi::sPlugViewElement>::iterator> veRange;
multimap<int, skindesignerapi::sPlugViewElement>::iterator veIt;
//plugin id --> view id --> viewgrid definition
map< int, multimap< int, skindesignerapi::sPlugViewGrid > > viewgrids;
pair<multimap<int, skindesignerapi::sPlugViewGrid>::iterator, multimap<int, skindesignerapi::sPlugViewGrid>::iterator> gRange;
multimap<int, skindesignerapi::sPlugViewGrid>::iterator gIt;
//plugin id --> view id --> tokencontainer of detailedview definition
map< int, map< int, skindesignerapi::cTokenContainer* > > viewtabs;
public:
cSDPluginManager(void);
~cSDPluginManager(void);
void Reset(void);
//Basic Plugin Interface
void RegisterBasicPlugin(skindesignerapi::cPluginStructure *plugStructure);
int GetNumPluginMenus(void);
void InitPluginMenuIterator(void);
map <int,skindesignerapi::sPlugMenu> *GetPluginMenus(string &name, int &id);
skindesignerapi::cTokenContainer *GetTokenContainer(int plugId, int plugMenuId);
void AddSubviewMapping(int plugId, int plugMenuId, int subViewId);
int GetSubviewId(int plugId, int plugMenuId);
//Advanced Plugin Interface
void RegisterAdvancedPlugin(skindesignerapi::cPluginStructure *plugStructure);
void InitPluginViewIterator(void);
bool GetNextPluginView(string &plugName, int &plugId, string &tplName);
int GetNumSubviews(int plugId);
void InitPluginSubviewIterator(int plugId);
bool GetNextSubView(int &id, string &tplname);
int GetNumViewElements(int plugId, int viewId);
void InitViewElementIterator(int plugId, int viewId);
bool GetNextViewElement(int &veId, string &veName);
skindesignerapi::cTokenContainer *GetTokenContainerVE(int plugId, int viewId, int veId);
int GetNumViewGrids(int plugId, int viewId);
void InitViewGridIterator(int plugId, int viewId);
bool GetNextViewGrid(int &gId, string &gName);
skindesignerapi::cTokenContainer *GetTokenContainerGE(int plugId, int viewId, int gId);
skindesignerapi::cTokenContainer *GetTokenContainerTab(int plugId, int viewId);
};
#endif //__PLUGINMANAGER_H

219
extensions/recfolderinfo.c Normal file
View File

@@ -0,0 +1,219 @@
#include "recfolderinfo.h"
class cFolderInfoInternList;
class cRecordingsFolderInfo::cFolderInfoIntern:public cListObject {
private:
cFolderInfoIntern *_parent;
cList<cFolderInfoIntern> *_subFolders;
cString _name;
time_t _latest;
int _count;
cString _latestFileName;
void UpdateData(cRecording *Recording);
cFolderInfoIntern *FindSubFolder(const char *Name) const;
public:
cFolderInfoIntern(cFolderInfoIntern *Parent, const char *Name);
virtual ~cFolderInfoIntern(void);
// split Name and find folder-info in tree
// if "Add", missing folders are created
cFolderInfoIntern *Find(const char *Name, bool Add);
void Add(cRecording *Recording);
cRecordingsFolderInfo::cFolderInfo *GetInfo(void) const;
cString FullName(void) const;
cString ToText(void) const;
cString DebugOutput(void) const;
};
cRecordingsFolderInfo::cFolderInfo::cFolderInfo(const char *Name, const char *FullName, time_t Latest, int Count, const char *LatestFileName)
{
this->Name = Name;
this->FullName = FullName;
this->Latest = Latest;
this->Count = Count;
this->LatestFileName= LatestFileName;
}
cRecordingsFolderInfo::cRecordingsFolderInfo(cRecordings &Recordings)
:_recordings(Recordings)
,_root(NULL)
{
Rebuild();
}
cRecordingsFolderInfo::~cRecordingsFolderInfo(void)
{
delete _root;
_root = NULL;
}
void cRecordingsFolderInfo::Rebuild(void)
{
delete _root;
_root = new cFolderInfoIntern(NULL, "");
cThreadLock RecordingsLock(&_recordings);
// re-get state with lock held
_recordings.StateChanged(_recState);
cFolderInfoIntern *info;
cString folder;
for (cRecording *rec = _recordings.First(); rec; rec = _recordings.Next(rec)) {
#if APIVERSNUM < 20102
//cRecording::Folder() first available since VDR 2.1.2
const char *recName = rec->Name();
if (const char *s = strrchr(recName, FOLDERDELIMCHAR)) {
folder = recName;
folder.Truncate(s - recName);
}
else
folder = "";
#else
folder = rec->Folder();
#endif
info = _root->Find(*folder, true);
info->Add(rec);
}
}
cRecordingsFolderInfo::cFolderInfo *cRecordingsFolderInfo::Get(const char *Folder)
{
cMutexLock lock(&_rootLock);
if (_recordings.StateChanged(_recState) || (_root == NULL))
Rebuild();
cFolderInfoIntern *info = _root->Find(Folder, false);
if (info == NULL)
return NULL;
return info->GetInfo();
}
cString cRecordingsFolderInfo::DebugOutput(void) const
{
cMutexLock lock(&_rootLock);
return _root->DebugOutput();
}
cRecordingsFolderInfo::cFolderInfoIntern::cFolderInfoIntern(cFolderInfoIntern *Parent, const char *Name)
:_parent(Parent)
,_name(Name)
,_latest(0)
,_count(0)
,_latestFileName("")
{
_subFolders = new cList<cFolderInfoIntern>();
}
cRecordingsFolderInfo::cFolderInfoIntern::~cFolderInfoIntern(void)
{
delete _subFolders;
_subFolders = NULL;
}
cRecordingsFolderInfo::cFolderInfoIntern *cRecordingsFolderInfo::cFolderInfoIntern::Find(const char *Name, bool Add)
{
cFolderInfoIntern *info = NULL;
if (Add)
info = this;
if (Name && *Name) {
static char delim[2] = { FOLDERDELIMCHAR, 0 };
char *strtok_next;
cFolderInfoIntern *next;
char *folder = strdup(Name);
info = this;
for (char *t = strtok_r(folder, delim, &strtok_next); t; t = strtok_r(NULL, delim, &strtok_next)) {
next = info->FindSubFolder(t);
if (next == NULL) {
if (!Add) {
info = NULL;
break;
}
next = new cFolderInfoIntern(info, t);
info->_subFolders->Add(next);
}
info = next;
}
free(folder);
}
return info;
}
void cRecordingsFolderInfo::cFolderInfoIntern::UpdateData(cRecording *Recording)
{
// count every recording
_count++;
// update date if newer
time_t recdate = Recording->Start();
if (_latest < recdate) {
_latest = recdate;
_latestFileName = Recording->FileName();
}
}
cRecordingsFolderInfo::cFolderInfoIntern *cRecordingsFolderInfo::cFolderInfoIntern::FindSubFolder(const char *Name) const
{
for (cRecordingsFolderInfo::cFolderInfoIntern *info = _subFolders->First(); info; info = _subFolders->Next(info)) {
if (strcmp(info->_name, Name) == 0)
return info;
}
return NULL;
}
void cRecordingsFolderInfo::cFolderInfoIntern::Add(cRecording *Recording)
{
if (Recording == NULL)
return;
// update this and all parent folders
for (cFolderInfoIntern *p = this; p; p = p->_parent)
p->UpdateData(Recording);
}
cRecordingsFolderInfo::cFolderInfo *cRecordingsFolderInfo::cFolderInfoIntern::GetInfo(void) const
{
return new cRecordingsFolderInfo::cFolderInfo(*_name, *FullName(), _latest, _count, *_latestFileName);
}
cString cRecordingsFolderInfo::cFolderInfoIntern::FullName(void) const
{
static char delim[2] = { FOLDERDELIMCHAR, 0 };
cString name = _name;
for (cFolderInfoIntern *p = _parent; p; p = p->_parent) {
// don't add FOLDERDELIMCHAR at start of FullName
if (p->_parent == NULL)
break;
name = cString::sprintf("%s%s%s", *p->_name, delim, *name);
}
return name;
}
cString cRecordingsFolderInfo::cFolderInfoIntern::ToText(void) const
{
return cString::sprintf("%s (%d recordings, latest: %s)\n", *FullName(), _count, *ShortDateString(_latest));
}
cString cRecordingsFolderInfo::cFolderInfoIntern::DebugOutput(void) const
{
cString output = ToText();
for (cFolderInfoIntern *i = _subFolders->First(); i; i = _subFolders->Next(i))
output = cString::sprintf("%s%s", *output, *i->DebugOutput());
return output;
}

View File

@@ -0,0 +1,43 @@
#ifndef __RECFOLDERINFO_H
#define __RECFOLDERINFO_H
#include <vdr/recording.h>
class cRecordingsFolderInfo {
public:
class cFolderInfoIntern;
private:
cRecordings &_recordings;
int _recState;
cFolderInfoIntern *_root;
mutable cMutex _rootLock;
void Rebuild(void);
public:
class cFolderInfo {
public:
cString Name;
cString FullName;
time_t Latest;
int Count;
cString LatestFileName;
cFolderInfo(const char *Name, const char *FullName, time_t Latest, int Count, const char *LatestFileName);
};
cRecordingsFolderInfo(cRecordings &Recordings);
~cRecordingsFolderInfo(void);
// caller must delete the cInfo object!
// returns NULL if folder doesn't exists
// will rebuild tree if recordings' state has changed
// is thread-safe
cFolderInfo *Get(const char *Folder);
cString DebugOutput(void) const;
};
#endif // __RECFOLDERINFO_H

359
extensions/scrapmanager.c Normal file
View File

@@ -0,0 +1,359 @@
#include "scrapmanager.h"
#include "../coreengine/definitions.h"
#include "helpers.h"
cPlugin *cScrapManager::pScraper = NULL;
cScrapManager::cScrapManager(void) {
if (!pScraper) {
pScraper = GetScraperPlugin();
}
movie = NULL;
series = NULL;
}
cScrapManager::~cScrapManager(void) {
delete movie;
delete series;
}
bool cScrapManager::LoadFullScrapInfo(const cEvent *event, const cRecording *recording) {
if (!pScraper) {
return false;
}
delete movie;
movie = NULL;
delete series;
series = NULL;
ScraperGetEventType getType;
getType.event = event;
getType.recording = recording;
if (!pScraper->Service("GetEventType", &getType)) {
return false;
}
if (getType.type == tMovie) {
movie = new cMovie();
movie->movieId = getType.movieId;
pScraper->Service("GetMovie", movie);
return true;
} else if (getType.type == tSeries) {
series = new cSeries();
series->seriesId = getType.seriesId;
series->episodeId = getType.episodeId;
pScraper->Service("GetSeries", series);
return true;
}
return false;
}
void cScrapManager::SetFullScrapInfo(skindesignerapi::cTokenContainer *tk, int actorsIndex) {
if (series) {
tk->AddIntToken((int)eScraperIT::ismovie, 0);
tk->AddIntToken((int)eScraperIT::isseries, 1);
SetSeries(tk, actorsIndex);
} else if (movie) {
tk->AddIntToken((int)eScraperIT::ismovie, 1);
tk->AddIntToken((int)eScraperIT::isseries, 0);
SetMovie(tk, actorsIndex);
} else {
tk->AddIntToken((int)eScraperIT::ismovie, 0);
tk->AddIntToken((int)eScraperIT::isseries, 0);
}
}
int cScrapManager::NumActors(void) {
if (series) {
return series->actors.size();
} else if (movie) {
return movie->actors.size();
}
return 0;
}
void cScrapManager::SetHeaderScrapInfo(skindesignerapi::cTokenContainer *tk) {
if (series) {
tk->AddIntToken((int)eScraperHeaderIT::ismovie, 0);
tk->AddIntToken((int)eScraperHeaderIT::isseries, 1);
vector<cTvMedia>::iterator poster = series->posters.begin();
if (poster != series->posters.end()) {
tk->AddIntToken((int)eScraperHeaderIT::posteravailable, true);
tk->AddIntToken((int)eScraperHeaderIT::posterwidth, (*poster).width);
tk->AddIntToken((int)eScraperHeaderIT::posterheight, (*poster).height);
tk->AddStringToken((int)eScraperHeaderST::posterpath, (*poster).path.c_str());
}
vector<cTvMedia>::iterator banner = series->banners.begin();
if (banner != series->banners.end()) {
tk->AddIntToken((int)eScraperHeaderIT::banneravailable, true);
tk->AddIntToken((int)eScraperHeaderIT::bannerwidth, (*banner).width);
tk->AddIntToken((int)eScraperHeaderIT::bannerheight, (*banner).height);
tk->AddStringToken((int)eScraperHeaderST::bannerpath, (*banner).path.c_str());
}
} else if (movie) {
tk->AddIntToken((int)eScraperHeaderIT::ismovie, 1);
tk->AddIntToken((int)eScraperHeaderIT::isseries, 0);
tk->AddIntToken((int)eScraperHeaderIT::posteravailable, true);
tk->AddIntToken((int)eScraperHeaderIT::banneravailable, false);
tk->AddIntToken((int)eScraperHeaderIT::posterwidth, movie->poster.width);
tk->AddIntToken((int)eScraperHeaderIT::posterheight, movie->poster.height);
tk->AddStringToken((int)eScraperHeaderST::posterpath, movie->poster.path.c_str());
} else {
tk->AddIntToken((int)eScraperHeaderIT::ismovie, 0);
tk->AddIntToken((int)eScraperHeaderIT::isseries, 0);
}
}
void cScrapManager::SetScraperPosterBanner(skindesignerapi::cTokenContainer *tk) {
if (movie) {
tk->AddIntToken((int)eCeMenuSchedulesIT::hasposter, 1);
tk->AddStringToken((int)eCeMenuSchedulesST::posterpath, movie->poster.path.c_str());
tk->AddIntToken((int)eCeMenuSchedulesIT::posterwidth, movie->poster.width);
tk->AddIntToken((int)eCeMenuSchedulesIT::posterheight, movie->poster.height);
} else if (series) {
vector<cTvMedia>::iterator poster = series->posters.begin();
if (poster != series->posters.end()) {
tk->AddIntToken((int)eCeMenuSchedulesIT::hasposter, 1);
tk->AddIntToken((int)eCeMenuSchedulesIT::posterwidth, (*poster).width);
tk->AddIntToken((int)eCeMenuSchedulesIT::posterheight, (*poster).height);
tk->AddStringToken((int)eCeMenuSchedulesST::posterpath, (*poster).path.c_str());
}
vector<cTvMedia>::iterator banner = series->banners.begin();
if (banner != series->banners.end()) {
tk->AddIntToken((int)eCeMenuSchedulesIT::hasbanner, 1);
tk->AddIntToken((int)eCeMenuSchedulesIT::bannerwidth, (*banner).width);
tk->AddIntToken((int)eCeMenuSchedulesIT::bannerheight, (*banner).height);
tk->AddStringToken((int)eCeMenuSchedulesST::bannerpath, (*banner).path.c_str());
}
}
}
void cScrapManager::SetScraperRecordingPoster(skindesignerapi::cTokenContainer *tk, const cRecording *recording, bool isListElement) {
if (!pScraper) {
return;
}
ScraperGetPosterThumb call;
call.event = NULL;
call.recording = recording;
if (pScraper->Service("GetPosterThumb", &call)) {
if (isListElement) {
tk->AddIntToken((int)eLeMenuRecordingsIT::hasposterthumbnail, FileExists(call.poster.path));
tk->AddIntToken((int)eLeMenuRecordingsIT::thumbnailwidth, call.poster.width);
tk->AddIntToken((int)eLeMenuRecordingsIT::thumbnailheight, call.poster.height);
tk->AddStringToken((int)eLeMenuRecordingsST::thumbnailpath, call.poster.path.c_str());
} else {
tk->AddIntToken((int)eCeMenuRecordingsIT::hasposterthumbnail, FileExists(call.poster.path));
tk->AddIntToken((int)eCeMenuRecordingsIT::thumbnailwidth, call.poster.width);
tk->AddIntToken((int)eCeMenuRecordingsIT::thumbnailheight, call.poster.height);
tk->AddStringToken((int)eCeMenuRecordingsST::thumbnailpath, call.poster.path.c_str());
}
}
ScraperGetPoster call2;
call2.event = NULL;
call2.recording = recording;
if (pScraper->Service("GetPoster", &call2)) {
if (isListElement) {
tk->AddIntToken((int)eLeMenuRecordingsIT::hasposter, FileExists(call2.poster.path));
tk->AddIntToken((int)eLeMenuRecordingsIT::posterwidth, call2.poster.width);
tk->AddIntToken((int)eLeMenuRecordingsIT::posterheight, call2.poster.height);
tk->AddStringToken((int)eLeMenuRecordingsST::posterpath, call2.poster.path.c_str());
} else {
tk->AddIntToken((int)eCeMenuRecordingsIT::hasposter, FileExists(call2.poster.path));
tk->AddIntToken((int)eCeMenuRecordingsIT::posterwidth, call2.poster.width);
tk->AddIntToken((int)eCeMenuRecordingsIT::posterheight, call2.poster.height);
tk->AddStringToken((int)eCeMenuRecordingsST::posterpath, call2.poster.path.c_str());
}
}
}
cPlugin *cScrapManager::GetScraperPlugin(void) {
static cPlugin *pScraper = cPluginManager::GetPlugin("scraper2vdr");
if( !pScraper ) // if it doesn't exit, try tvscraper
pScraper = cPluginManager::GetPlugin("tvscraper");
return pScraper;
}
void cScrapManager::SetMovie(skindesignerapi::cTokenContainer *tk, int actorsIndex) {
tk->AddStringToken((int)eScraperST::movietitle, movie->title.c_str());
tk->AddStringToken((int)eScraperST::movieoriginalTitle, movie->originalTitle.c_str());
tk->AddStringToken((int)eScraperST::movietagline, movie->tagline.c_str());
tk->AddStringToken((int)eScraperST::movieoverview, movie->overview.c_str());
tk->AddStringToken((int)eScraperST::moviegenres, movie->genres.c_str());
tk->AddStringToken((int)eScraperST::moviehomepage, movie->homepage.c_str());
tk->AddStringToken((int)eScraperST::moviereleasedate, movie->releaseDate.c_str());
tk->AddStringToken((int)eScraperST::moviepopularity, *cString::sprintf("%f", movie->popularity));
tk->AddStringToken((int)eScraperST::movievoteaverage, *cString::sprintf("%f", movie->voteAverage));
tk->AddStringToken((int)eScraperST::posterpath, movie->poster.path.c_str());
tk->AddStringToken((int)eScraperST::fanartpath, movie->fanart.path.c_str());
tk->AddStringToken((int)eScraperST::collectionposterpath, movie->collectionPoster.path.c_str());
tk->AddStringToken((int)eScraperST::collectionfanartpath, movie->collectionFanart.path.c_str());
tk->AddIntToken((int)eScraperIT::movieadult, movie->adult);
tk->AddIntToken((int)eScraperIT::moviebudget, movie->budget);
tk->AddIntToken((int)eScraperIT::movierevenue, movie->revenue);
tk->AddIntToken((int)eScraperIT::movieruntime, movie->runtime);
tk->AddIntToken((int)eScraperIT::posterwidth, movie->poster.width);
tk->AddIntToken((int)eScraperIT::posterheight, movie->poster.height);
tk->AddIntToken((int)eScraperIT::fanartwidth, movie->fanart.width);
tk->AddIntToken((int)eScraperIT::fanartheight, movie->fanart.height);
tk->AddIntToken((int)eScraperIT::collectionposterwidth, movie->collectionPoster.width);
tk->AddIntToken((int)eScraperIT::collectionposterheight, movie->collectionPoster.height);
tk->AddIntToken((int)eScraperIT::collectionfanartwidth, movie->collectionFanart.width);
tk->AddIntToken((int)eScraperIT::collectionfanartheight, movie->collectionFanart.height);
if (movie->collectionPoster.path.size() > 0)
tk->AddIntToken((int)eScraperIT::movieiscollection, 1);
int i=0;
for (vector<cActor>::iterator act = movie->actors.begin(); act != movie->actors.end(); act++) {
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::name, (*act).name.c_str());
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::role, (*act).role.c_str());
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::thumb, (*act).actorThumb.path.c_str());
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::thumbwidth, *cString::sprintf("%d", (*act).actorThumb.width));
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::thumbheight, *cString::sprintf("%d", (*act).actorThumb.height));
i++;
}
}
void cScrapManager::SetSeries(skindesignerapi::cTokenContainer *tk, int actorsIndex) {
//Series Basics
tk->AddStringToken((int)eScraperST::seriesname, series->name.c_str());
tk->AddStringToken((int)eScraperST::seriesoverview, series->overview.c_str());
tk->AddStringToken((int)eScraperST::seriesfirstaired, series->firstAired.c_str());
tk->AddStringToken((int)eScraperST::seriesnetwork, series->network.c_str());
tk->AddStringToken((int)eScraperST::seriesgenre, series->genre.c_str());
tk->AddStringToken((int)eScraperST::seriesrating, *cString::sprintf("%f", series->rating));
tk->AddStringToken((int)eScraperST::seriesstatus, series->status.c_str());
//Episode Information
tk->AddIntToken((int)eScraperIT::episodenumber, series->episode.number);
tk->AddIntToken((int)eScraperIT::episodeseason, series->episode.season);
tk->AddStringToken((int)eScraperST::episodetitle, series->episode.name.c_str());
tk->AddStringToken((int)eScraperST::episodefirstaired, series->episode.firstAired.c_str());
tk->AddStringToken((int)eScraperST::episodegueststars, series->episode.guestStars.c_str());
tk->AddStringToken((int)eScraperST::episodeoverview, series->episode.overview.c_str());
tk->AddStringToken((int)eScraperST::episoderating, *cString::sprintf("%f", series->episode.rating));
tk->AddIntToken((int)eScraperIT::episodeimagewidth, series->episode.episodeImage.width);
tk->AddIntToken((int)eScraperIT::episodeimageheight, series->episode.episodeImage.height);
tk->AddStringToken((int)eScraperST::episodeimagepath, series->episode.episodeImage.path.c_str());
//Seasonposter
tk->AddIntToken((int)eScraperIT::seasonposterwidth, series->seasonPoster.width);
tk->AddIntToken((int)eScraperIT::seasonposterheight, series->seasonPoster.height);
tk->AddStringToken((int)eScraperST::seasonposterpath, series->seasonPoster.path.c_str());
//Posters
int indexInt = (int)eScraperIT::seriesposter1width;
int indexStr = (int)eScraperST::seriesposter1path;
for(vector<cTvMedia>::iterator poster = series->posters.begin(); poster != series->posters.end(); poster++) {
tk->AddIntToken(indexInt, (*poster).width);
tk->AddIntToken(indexInt+1, (*poster).height);
tk->AddStringToken(indexStr, (*poster).path.c_str());
indexInt += 2;
indexStr++;
}
//Banners
indexInt = (int)eScraperIT::seriesbanner1width;
indexStr = (int)eScraperST::seriesbanner1path;
for(vector<cTvMedia>::iterator banner = series->banners.begin(); banner != series->banners.end(); banner++) {
tk->AddIntToken(indexInt, (*banner).width);
tk->AddIntToken(indexInt+1, (*banner).height);
tk->AddStringToken(indexStr, (*banner).path.c_str());
indexInt += 2;
indexStr++;
}
//Fanarts
indexInt = (int)eScraperIT::seriesfanart1width;
indexStr = (int)eScraperST::seriesfanart1path;
for(vector<cTvMedia>::iterator fanart = series->fanarts.begin(); fanart != series->fanarts.end(); fanart++) {
tk->AddIntToken(indexInt, (*fanart).width);
tk->AddIntToken(indexInt+1, (*fanart).height);
tk->AddStringToken(indexStr, (*fanart).path.c_str());
indexInt += 2;
indexStr++;
}
//Actors
int i=0;
for (vector<cActor>::iterator act = series->actors.begin(); act != series->actors.end(); act++) {
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::name, (*act).name.c_str());
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::role, (*act).role.c_str());
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::thumb, (*act).actorThumb.path.c_str());
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::thumbwidth, *cString::sprintf("%d", (*act).actorThumb.width));
tk->AddLoopToken(actorsIndex, i, (int)eScraperLT::thumbheight, *cString::sprintf("%d", (*act).actorThumb.height));
i++;
}
}
void cScrapManager::RecPoster(const cRecording *rec, int &posterWidth, int &posterHeight, string &path, bool &hasPoster) {
if (!pScraper) {
return;
}
ScraperGetPoster callPoster;
callPoster.event = NULL;
callPoster.recording = rec;
if (pScraper->Service("GetPoster", &callPoster)) {
posterWidth = callPoster.poster.width;
posterHeight = callPoster.poster.height;
path = callPoster.poster.path;
if (path.size() > 0)
hasPoster = true;
}
}
void cScrapManager::SetPosterBanner(skindesignerapi::cTokenContainer *tk, const cEvent *event, const cRecording *recording) {
if (!pScraper) {
return;
}
int mediaWidth = 0;
int mediaHeight = 0;
string mediaPath = "";
bool isBanner = false;
int posterWidth = 0;
int posterHeight = 0;
string posterPath = "";
bool hasPoster = false;
int bannerWidth = 0;
int bannerHeight = 0;
string bannerPath = "";
bool hasBanner = false;
ScraperGetPosterBannerV2 call;
call.event = event;
call.recording = recording;
if (pScraper->Service("GetPosterBannerV2", &call)) {
if ((call.type == tSeries) && call.banner.path.size() > 0) {
mediaWidth = call.banner.width;
mediaHeight = call.banner.height;
mediaPath = call.banner.path;
isBanner = true;
bannerWidth = mediaWidth;
bannerHeight = mediaHeight;
bannerPath = mediaPath;
hasBanner = true;
ScraperGetPoster callPoster;
callPoster.event = event;
callPoster.recording = recording;
if (pScraper->Service("GetPoster", &callPoster)) {
posterWidth = callPoster.poster.width;
posterHeight = callPoster.poster.height;
posterPath = callPoster.poster.path;
hasPoster = true;
}
} else if (call.type == tMovie && call.poster.path.size() > 0 && call.poster.height > 0) {
mediaWidth = call.poster.width;
mediaHeight = call.poster.height;
mediaPath = call.poster.path;
posterWidth = call.poster.width;
posterHeight = call.poster.height;
posterPath = call.poster.path;
hasPoster = true;
}
}
tk->AddIntToken((int)eScraperPosterBannerIT::mediawidth, mediaWidth);
tk->AddIntToken((int)eScraperPosterBannerIT::mediaheight, mediaHeight);
tk->AddIntToken((int)eScraperPosterBannerIT::isbanner, isBanner);
tk->AddStringToken((int)eScraperPosterBannerST::mediapath, mediaPath.c_str());
tk->AddIntToken((int)eScraperPosterBannerIT::posterwidth, posterWidth);
tk->AddIntToken((int)eScraperPosterBannerIT::posterheight, posterHeight);
tk->AddStringToken((int)eScraperPosterBannerST::posterpath, posterPath.c_str());
tk->AddIntToken((int)eScraperPosterBannerIT::hasposter, hasPoster);
tk->AddIntToken((int)eScraperPosterBannerIT::bannerwidth, bannerWidth);
tk->AddIntToken((int)eScraperPosterBannerIT::bannerheight, bannerHeight);
tk->AddStringToken((int)eScraperPosterBannerST::bannerpath, bannerPath.c_str());
tk->AddIntToken((int)eScraperPosterBannerIT::hasbanner, hasBanner);
}

28
extensions/scrapmanager.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef __SCRAPMANAGER_H
#define __SCRAPMANAGER_H
#include "../services/scraper2vdr.h"
#include "../libskindesignerapi/tokencontainer.h"
class cScrapManager {
private:
static cPlugin *pScraper;
cMovie *movie;
cSeries *series;
cPlugin *GetScraperPlugin(void);
void SetMovie(skindesignerapi::cTokenContainer *tk, int actorsIndex);
void SetSeries(skindesignerapi::cTokenContainer *tk, int actorsIndex);
protected:
bool LoadFullScrapInfo(const cEvent *event, const cRecording *recording);
void SetFullScrapInfo(skindesignerapi::cTokenContainer *tk, int actorsIndex);
int NumActors(void);
void SetHeaderScrapInfo(skindesignerapi::cTokenContainer *tk);
void SetScraperPosterBanner(skindesignerapi::cTokenContainer *tk);
void SetScraperRecordingPoster(skindesignerapi::cTokenContainer *tk, const cRecording *recording, bool isListElement);
void RecPoster(const cRecording *rec, int &posterWidth, int &posterHeight, string &path, bool &hasPoster);
void SetPosterBanner(skindesignerapi::cTokenContainer *tk, const cEvent *event, const cRecording *recording);
public:
cScrapManager(void);
virtual ~cScrapManager(void);
};
#endif //__SCRAPMANAGER_H

375
extensions/skinrepo.c Normal file
View File

@@ -0,0 +1,375 @@
#include <iostream>
#include <fstream>
#include "skinrepo.h"
#include "helpers.h"
using namespace std;
// --- cSkinRepo -------------------------------------------------------------
cSkinRepo::cSkinRepo(void) {
name = "";
repoType = rtUndefined;
action = eaUndefined;
url = "";
author = "unknown";
minSDVersion = "0.0.1";
command = "";
command2 = "";
tempfile = "";
result = -1;
skinPath = "";
themesPath = "";
}
cSkinRepo::~cSkinRepo() {
}
bool cSkinRepo::Valid(void) {
if (!name.size())
return false;
if (repoType == rtUndefined)
return false;
if (!url.size())
return false;
return true;
}
void cSkinRepo::Install(string path, string themesPath) {
if (Running())
return;
action = eaInstall;
this->skinPath = path + name;
this->themesPath = themesPath;
if (repoType == rtGit) {
command = *cString::sprintf("git clone --depth=1 --progress %s %s", url.c_str(), skinPath.c_str());
tempfile = *cString::sprintf("gitclone_%s_%ld.out", name.c_str(), time(0));
dsyslog("skindesigner: installing skin from Git, command: %s, logfile: %s", command.c_str(), tempfile.c_str());
Start();
} else if (repoType == rtZipUrl) {
size_t hit = url.find_last_of('/');
if (hit == string::npos)
return;
string filename = url.substr(hit+1);
command = *cString::sprintf("wget -P /tmp/ %s", url.c_str());
command2 = *cString::sprintf("unzip /tmp/%s -d %s", filename.c_str(), path.c_str());
dsyslog("skindesigner: installing skin from Zip, command: %s, %s", command.c_str(), command2.c_str());
Start();
}
}
void cSkinRepo::Update(string path) {
if (Running())
return;
action = eaUpdate;
this->skinPath = path + name;
if (repoType == rtGit) {
command = *cString::sprintf("cd %s; git pull", skinPath.c_str());
tempfile = *cString::sprintf("gitpull_%s_%ld.out", name.c_str(), time(0));
dsyslog("skindesigner: updating skin from Git, command: %s, logfile: /tmp/%s", command.c_str(), tempfile.c_str());
Start();
} else if (repoType == rtZipUrl) {
//TODO
}
}
void cSkinRepo::Action(void) {
if (command.size() < 1)
return;
if (tempfile.size() > 0) {
command = *cString::sprintf("%s > /tmp/%s 2>&1", command.c_str(), tempfile.c_str());
}
result = system (command.c_str());
if (result == 0 && command2.size() > 0) {
result = system (command2.c_str());
}
if (result == 0) {
if (action == eaInstall)
CreateThemeFiles();
dsyslog("skindesigner: %s successfully executed", command.c_str());
} else {
esyslog("skindesigner: ERROR executing %s", command.c_str());
}
}
void cSkinRepo::CreateThemeFiles(void) {
string availableThemesPath = skinPath + "/themes/";
DIR *folder = NULL;
struct dirent *dirEntry;
folder = opendir(availableThemesPath.c_str());
if (!folder) {
return;
}
vector<string> skinThemes;
while (dirEntry = readdir(folder)) {
string dirEntryName = dirEntry->d_name;
int dirEntryType = dirEntry->d_type;
if (!dirEntryName.compare(".") || !dirEntryName.compare("..") || dirEntryType != DT_DIR)
continue;
skinThemes.push_back(dirEntryName);
}
for (vector<string>::iterator it = skinThemes.begin(); it != skinThemes.end(); it++) {
string themeName = *it;
string themeFileName = themesPath;
themeFileName += name + "-" + themeName + ".theme";
if (FileExists(themeFileName)) {
continue;
}
ofstream themeFile (themeFileName.c_str());
if (themeFile.is_open()) {
themeFile << "Description = ";
themeFile << themeName << "\n";
themeFile.close();
}
}
}
bool cSkinRepo::SuccessfullyUpdated(void) {
string logfilePath = "/tmp/" + tempfile;
bool updated = true;
string line;
ifstream logfile(logfilePath.c_str());
if (logfile.is_open()) {
while ( getline (logfile, line) ) {
if (line.find("up-to-date") != string::npos) {
updated = false;
break;
}
}
logfile.close();
}
return updated;
}
void cSkinRepo::Debug() {
string strRepoType = "Undefined";
if (repoType == rtGit)
strRepoType = "Git";
else if (repoType == rtZipUrl)
strRepoType = "ZipUrl";
dsyslog("skindesigner: --- skinrepo %s, Type %s ---", name.c_str(), strRepoType.c_str());
dsyslog("skindesigner: url %s", url.c_str());
dsyslog("skindesigner: author %s", author.c_str());
dsyslog("skindesigner: minimum Skindesigner Version required %s", minSDVersion.c_str());
if (specialFonts.size() > 0) {
for (vector<string>::iterator it = specialFonts.begin(); it != specialFonts.end(); it++) {
dsyslog("skindesigner: special font %s", (*it).c_str());
}
}
if (supportedPlugins.size() > 0) {
for (vector<string>::iterator it = supportedPlugins.begin(); it != supportedPlugins.end(); it++) {
dsyslog("skindesigner: supported plugin %s", (*it).c_str());
}
}
if (screenshots.size() > 0) {
for (vector<pair<string,string> >::iterator it = screenshots.begin(); it != screenshots.end(); it++) {
string desc = (it->first).c_str();
string url = (it->second).c_str();
dsyslog("skindesigner: screenshot \"%s\", url %s", desc.c_str(), url.c_str());
}
}
}
// --- cSkinRepos -------------------------------------------------------------
cSkinRepos::cSkinRepos(void) {
skinRepoUrl = "https://github.com/louisbraun/skinrepository.git";
repoFolder = "skinrepositories/";
}
cSkinRepos::~cSkinRepos() {
for (vector<cSkinRepo*>::iterator it = repos.begin(); it != repos.end(); it++) {
delete (*it);
}
}
void cSkinRepos::Init(string path) {
string repoPath = path + repoFolder;
if (FolderExists(repoPath)) {
PullRepoGit(repoPath);
} else {
InitRepoGit(repoPath);
}
}
void cSkinRepos::Read(string path) {
string repoPath = path + repoFolder;
DIR *folder = NULL;
struct dirent *dirEntry;
folder = opendir(repoPath.c_str());
if (!folder) {
esyslog("skindesigner: no skinrepository folder available in %s", repoPath.c_str());
return;
}
while (dirEntry = readdir(folder)) {
string fileName = dirEntry->d_name;
if (!fileName.compare(".") || !fileName.compare("..") || !fileName.compare(".git"))
continue;
string filePath = repoPath + fileName;
if (! ReadXMLFile(filePath.c_str(), false) ) {
esyslog("skindesigner: error reading skinrepo %s", filePath.c_str());
continue;
}
if (! SetDocument() )
continue;
if (!ParseRepository())
esyslog("skindesigner: error parsing skinrepository %s", filePath.c_str());
DeleteDocument();
}
}
cSkinRepo *cSkinRepos::GetRepo(string name) {
for (vector<cSkinRepo*>::iterator it = repos.begin(); it != repos.end(); it++) {
cSkinRepo *repo = (*it);
if (!name.compare(repo->Name()))
return repo;
}
return NULL;
}
cSkinRepo *cSkinRepos::GetNextRepo(void) {
if (repoIt == repos.end())
return NULL;
cSkinRepo *repo = *repoIt;
repoIt++;
return repo;
}
void cSkinRepos::Debug(void) {
for (vector<cSkinRepo*>::iterator it = repos.begin(); it != repos.end(); it++) {
(*it)->Debug();
}
}
bool cSkinRepos::ParseRepository(void) {
if (!LevelDown())
return false;
cSkinRepo *repo = new cSkinRepo();
do {
string value = "";
if (CheckNodeName("name")) {
if (GetNodeValue(value)) {
repo->SetName(value);
}
} else if (CheckNodeName("type")) {
if (GetNodeValue(value)) {
eRepoType repoType = rtUndefined;
if (!value.compare("git"))
repoType = rtGit;
else if (!value.compare("zip"))
repoType = rtZipUrl;
repo->SetRepoType(repoType);
}
} else if (CheckNodeName("url")) {
if (GetNodeValue(value)) {
repo->SetUrl(value);
}
} else if (CheckNodeName("author")) {
if (GetNodeValue(value)) {
repo->SetAuthor(value);
}
} else if (CheckNodeName("minimumskindesignerversion")) {
if (GetNodeValue(value)) {
repo->SetMinSDVersion(value);
}
} else if (CheckNodeName("specialfonts")) {
if (!LevelDown())
continue;
do {
if (CheckNodeName("font")) {
if (GetNodeValue(value)) {
repo->SetSpecialFont(value);
}
}
} while (NextNode());
LevelUp();
} else if (CheckNodeName("supportedplugins")) {
if (!LevelDown())
continue;
do {
if (CheckNodeName("plugin")) {
if (GetNodeValue(value)) {
repo->SetSupportedPlugin(value);
}
}
} while (NextNode());
LevelUp();
} else if (CheckNodeName("screenshots")) {
if (!LevelDown()) {
continue;
}
do {
if (CheckNodeName("screenshot")) {
if (!LevelDown()) {
continue;
}
string desc = "";
string url = "";
do {
if (CheckNodeName("description")) {
GetNodeValue(desc);
} else if (CheckNodeName("url")) {
GetNodeValue(url);
}
} while (NextNode());
LevelUp();
if (desc.size() && url.size())
repo->SetScreenshot(desc, url);
}
} while (NextNode());
LevelUp();
}
} while (NextNode());
LevelUp();
if (repo->Valid()) {
repos.push_back(repo);
return true;
}
return false;
}
void cSkinRepos::InitRepoGit(string path) {
dsyslog("skindesigner: initiating skin repository %s", path.c_str());
CreateFolder(path);
cString command = cString::sprintf("git clone --depth=1 %s %s", skinRepoUrl.c_str(), path.c_str());
int result = system (*command);
if (result == 0) {
dsyslog("skindesigner: skinrepository successfully initiated");
} else {
esyslog("skindesigner: ERROR initiating skinrepository. Command: %s", *command);
}
}
void cSkinRepos::PullRepoGit(string path) {
dsyslog("skindesigner: updating skin repository %s", path.c_str());
cString command = *cString::sprintf("cd %s; git pull", path.c_str());
int result = system (*command);
if (result == 0) {
dsyslog("skindesigner: skinrepository successfully updated");
} else {
esyslog("skindesigner: ERROR updating skinrepository. Command: %s", *command);
}
}

98
extensions/skinrepo.h Normal file
View File

@@ -0,0 +1,98 @@
#ifndef __SKINREPO_H
#define __SKINREPO_H
#include <string>
#include <vector>
#include <map>
#include <set>
#include "libxmlwrapper.h"
#include <vdr/plugin.h>
using namespace std;
enum eRepoType {
rtUndefined,
rtGit,
rtZipUrl
};
enum eAction {
eaUndefined,
eaInstall,
eaUpdate
};
// --- cSkinRepo -------------------------------------------------------------
class cSkinRepo : public cThread {
private:
string name;
eRepoType repoType;
eAction action;
string url;
string author;
string minSDVersion;
vector<string> specialFonts;
vector<string> supportedPlugins;
vector< pair < string, string > > screenshots;
//helpers for execution
string command;
string command2;
string tempfile;
int result;
string skinPath;
string themesPath;
virtual void Action(void);
void CreateThemeFiles(void);
public:
cSkinRepo(void);
virtual ~cSkinRepo(void);
void SetName(string name) { this->name = name; };
void SetRepoType(eRepoType type) { this->repoType = type; };
void SetUrl(string url) { this->url = url; };
void SetAuthor(string author) { this->author = author; };
void SetMinSDVersion(string minSDVersion) { this->minSDVersion = minSDVersion; };
void SetSpecialFont(string font) { specialFonts.push_back(font); };
void SetSupportedPlugin(string plugin) { supportedPlugins.push_back(plugin); };
void SetScreenshot(string desc, string url) { screenshots.push_back(pair<string, string>(desc, url)); };
bool Valid(void);
eRepoType Type(void) { return repoType; };
string Name(void) { return name; };
string Author(void) { return author; };
string MinSDVersion(void) { return minSDVersion; };
string Url(void) { return url; };
vector<string> *SpecialFonts(void) { return &specialFonts; };
vector<string> *SupportedPlugins(void) { return &supportedPlugins; };
vector< pair < string, string > > *Screenshots(void) { return &screenshots; };
void Install(string path, string themesPath);
void Update(string path);
bool InstallationFinished(void) { return !(Running()); };
bool SuccessfullyInstalled(void) { if (result == 0) return true; return false; };
bool SuccessfullyUpdated(void);
void Debug(void);
};
// --- cSkinRepos -------------------------------------------------------------
class cSkinRepos : public cLibXMLWrapper {
private:
string skinRepoUrl;
string repoFolder;
vector<cSkinRepo*> repos;
vector<cSkinRepo*>::iterator repoIt;
bool ParseRepository(void);
void InitRepoGit(string path);
void PullRepoGit(string path);
public:
cSkinRepos(void);
virtual ~cSkinRepos(void);
void Init(string path);
void Read(string path);
int Count(void) { return repos.size(); };
cSkinRepo *GetRepo(string name);
void InitRepoIterator(void) { repoIt = repos.begin(); };
cSkinRepo *GetNextRepo(void);
void Debug(void);
};
#endif //__SKINREPO_H

377
extensions/skinsetup.c Normal file
View File

@@ -0,0 +1,377 @@
#include "skinsetup.h"
#include "../config.h"
#include "../coreengine/xmlparser.h"
#include "helpers.h"
// --- cSkinSetupParameter -----------------------------------------------------------
cSkinSetupParameter::cSkinSetupParameter(void) {
type = sptUnknown;
name = "";
displayText = "";
helpText = "";
min = 0;
max = 1000;
value = 0;
options = NULL;
optionsTranslated = NULL;
numOptions = 0;
}
cSkinSetupParameter::~cSkinSetupParameter(void) {
if (numOptions > 0 && options && optionsTranslated) {
delete[] options;
delete[] optionsTranslated;
}
}
void cSkinSetupParameter::Debug(void) {
string sType = "unknown";
if (type == sptBool)
sType = "bool";
else if (type == sptInt)
sType = "int";
else if (type == sptString)
sType = "string";
dsyslog("skindesigner: name \"%s\", type %s, displayText \"%s\", Value: %d", name.c_str(), sType.c_str(), displayText.c_str(), value);
if (type == sptInt)
dsyslog("skindesigner: min %d, max %d", min, max);
if (type == sptString && options) {
for (int i=0; i < numOptions; i++) {
dsyslog("skindesigner: option %d: %s", i+1, options[i]);
}
}
}
// --- cSkinSetupMenu -----------------------------------------------------------
cSkinSetupMenu::cSkinSetupMenu(void) {
name = "";
displayText = "";
parent = NULL;
}
cSkinSetupMenu::~cSkinSetupMenu(void) {
for (vector < cSkinSetupParameter* >::iterator p = parameters.begin(); p != parameters.end(); p++) {
delete (*p);
}
for (vector < cSkinSetupMenu* >::iterator s = subMenus.begin(); s != subMenus.end(); s++) {
delete (*s);
}
}
cSkinSetupParameter *cSkinSetupMenu::GetNextParameter(bool deep) {
cSkinSetupParameter *param = NULL;
if (paramIt != parameters.end()) {
param = *paramIt;
paramIt++;
return param;
}
if (!deep)
return NULL;
if (subMenuIt != subMenus.end()) {
param = (*subMenuIt)->GetNextParameter();
if (!param) {
subMenuIt++;
if (subMenuIt != subMenus.end()) {
(*subMenuIt)->InitIterators();
param = (*subMenuIt)->GetNextParameter();
}
}
}
return param;
}
cSkinSetupParameter *cSkinSetupMenu::GetParameter(string name) {
for (vector < cSkinSetupParameter* >::iterator it = parameters.begin(); it != parameters.end(); it++) {
if (!name.compare((*it)->name))
return *it;
}
cSkinSetupParameter *paramHit = NULL;
for (vector < cSkinSetupMenu* >::iterator subMenu = subMenus.begin(); subMenu != subMenus.end(); subMenu++) {
paramHit = (*subMenu)->GetParameter(name);
if (paramHit)
return paramHit;
}
return NULL;
}
void cSkinSetupMenu::InitIterators(void) {
paramIt = parameters.begin();
subMenuIt = subMenus.begin();
while (subMenuIt != subMenus.end()) {
(*subMenuIt)->InitIterators();
subMenuIt++;
}
subMenuIt = subMenus.begin();
}
void cSkinSetupMenu::SetParameter(eSetupParameterType paramType, string name, string displayText, string helpText, string min, string max, string value, string options) {
cSkinSetupParameter *param = new cSkinSetupParameter();
param->type = paramType;
param->name = name;
param->displayText = displayText;
param->helpText = helpText;
if (min.size() && paramType == sptInt) {
param->min = atoi(min.c_str());
}
if (max.size() && paramType == sptInt) {
param->max = atoi(max.c_str());
}
param->value = atoi(value.c_str());
if (paramType == sptString) {
splitstring o(options.c_str());
vector<string> opts = o.split(',', 1);
int numOpts = opts.size();
if (numOpts > 0) {
param->numOptions = numOpts;
param->options = new const char*[numOpts];
int i=0;
for (vector<string>::iterator it = opts.begin(); it != opts.end(); it++) {
string option = trim(*it);
char *s = new char[option.size()];
strcpy(s, option.c_str());
param->options[i++] = s;
}
}
}
parameters.push_back(param);
}
cSkinSetupMenu *cSkinSetupMenu::GetMenu(string &name) {
for (vector<cSkinSetupMenu*>::iterator m = subMenus.begin(); m != subMenus.end(); m++) {
cSkinSetupMenu *menu = (*m);
if (!name.compare(menu->GetName()))
return menu;
menu = menu->GetMenu(name);
if (menu)
return menu;
}
return NULL;
}
cSkinSetupMenu *cSkinSetupMenu::GetNextSubMenu(bool deep) {
cSkinSetupMenu *menu = NULL;
if (subMenuIt != subMenus.end()) {
if (deep) {
menu = (*subMenuIt)->GetNextSubMenu(deep);
if (menu)
return menu;
}
menu = *subMenuIt;
subMenuIt++;
return menu;
}
return NULL;
}
void cSkinSetupMenu::Debug(bool deep) {
dsyslog("skindesigner: Menu %s Setup Parameters", name.c_str());
for (vector < cSkinSetupParameter* >::iterator p = parameters.begin(); p != parameters.end(); p++) {
(*p)->Debug();
}
if (subMenus.empty())
return;
for (vector < cSkinSetupMenu* >::iterator s = subMenus.begin(); s != subMenus.end(); s++) {
dsyslog("skindesigner: SubMenu %s, Parent %s", ((*s)->GetName()).c_str(), ((*s)->GetParent()->GetName()).c_str());
if (deep)
(*s)->Debug();
}
}
// --- cSkinSetup -----------------------------------------------------------
cSkinSetup::cSkinSetup(string skin) {
this->skin = skin;
rootMenu = new cSkinSetupMenu();
rootMenu->SetName("root");
currentMenu = rootMenu;
}
cSkinSetup::~cSkinSetup() {
delete rootMenu;
}
bool cSkinSetup::ReadFromXML(void) {
string xmlPath = *cString::sprintf("%s%s/setup.xml", *config.GetSkinPath(skin), skin.c_str());
cXmlParser parser;
if (!parser.ReadSkinSetup(this, xmlPath)) {
return false;
}
parser.ParseSkinSetup(skin);
return true;
}
void cSkinSetup::SetSubMenu(string name, string displayText) {
cSkinSetupMenu *subMenu = new cSkinSetupMenu();
subMenu->SetName(name);
subMenu->SetDisplayText(displayText);
subMenu->SetParent(currentMenu);
currentMenu->AddSubMenu(subMenu);
currentMenu = subMenu;
}
void cSkinSetup::SubMenuDone(void) {
cSkinSetupMenu *parent = currentMenu->GetParent();
if (parent) {
currentMenu = parent;
}
}
void cSkinSetup::SetParameter(string type, string name, string displayText, string helpText, string min, string max, string value, string options) {
if (!type.size() || !name.size() || !displayText.size() || !value.size()) {
esyslog("skindesigner: invalid setup parameter for skin %s", skin.c_str());
return;
}
eSetupParameterType paramType = sptUnknown;
if (!type.compare("int")) {
paramType = sptInt;
} else if (!type.compare("bool")) {
paramType = sptBool;
} else if (!type.compare("string")) {
paramType = sptString;
}
if (paramType == sptUnknown) {
esyslog("skindesigner: invalid setup parameter for skin %s", skin.c_str());
return;
}
currentMenu->SetParameter(paramType, name, displayText, helpText, min, max, value, options);
}
cSkinSetupParameter *cSkinSetup::GetNextParameter(void) {
return rootMenu->GetNextParameter();
}
cSkinSetupParameter *cSkinSetup::GetParameter(string name) {
return rootMenu->GetParameter(name);
}
void cSkinSetup::SetTranslation(string translationToken, map < string, string > transl) {
translations.insert(pair<string, map < string, string > >(translationToken, transl));
}
void cSkinSetup::AddToGlobals(cGlobals *globals) {
if (!globals)
return;
rootMenu->InitIterators();
cSkinSetupParameter *param = NULL;
while (param = rootMenu->GetNextParameter()) {
if (param->type == sptString) {
string value = param->options[param->value];
globals->AddString(param->name, value);
string intName = "index" + param->name;
globals->AddInt(intName, param->value);
} else {
globals->AddInt(param->name, param->value);
}
}
}
void cSkinSetup::TranslateSetup(void) {
rootMenu->InitIterators();
cSkinSetupParameter *param = NULL;
while (param = rootMenu->GetNextParameter()) {
string transl = "";
if (Translate(param->displayText, transl)) {
param->displayText = transl;
}
string translHelp = "";
if (Translate(param->helpText, translHelp)) {
param->helpText = translHelp;
}
if (param->type == sptString && param->numOptions > 0) {
param->optionsTranslated = new const char*[param->numOptions];
for (int i = 0; i < param->numOptions; i++) {
string option = param->options[i];
string optionTransToken = "{tr(" + option + ")}";
string optionTranslated = "";
if (Translate(optionTransToken, optionTranslated)) {
char *s = new char[optionTranslated.size()];
strcpy(s, optionTranslated.c_str());
param->optionsTranslated[i] = s;
} else {
char *s = new char[option.size()];
strcpy(s, option.c_str());
param->optionsTranslated[i] = s;
}
}
}
}
rootMenu->InitIterators();
cSkinSetupMenu *subMenu = NULL;
while (subMenu = rootMenu->GetNextSubMenu()) {
string transl = "";
if (Translate(subMenu->GetDisplayText(), transl)) {
subMenu->SetDisplayText(transl);
}
}
}
cSkinSetupMenu *cSkinSetup::GetMenu(string &name) {
if (name.size() == 0)
return rootMenu;
return rootMenu->GetMenu(name);
}
bool cSkinSetup::Translate(string text, string &translation) {
string transStart = "{tr(";
string transEnd = ")}";
size_t foundStart = text.find(transStart);
size_t foundEnd = text.find(transEnd);
bool translated = false;
while (foundStart != string::npos && foundEnd != string::npos) {
string token = text.substr(foundStart + 1, foundEnd - foundStart);
string transToken = DoTranslate(token);
if (transToken.size() > 0)
translated = true;
else
return false;
text.replace(foundStart, foundEnd - foundStart + 2, transToken);
foundStart = text.find(transStart);
foundEnd = text.find(transEnd);
}
if (translated)
translation = text;
return translated;
}
string cSkinSetup::DoTranslate(string token) {
string translation = "";
map <string, map< string, string > >::iterator hit = translations.find(token);
if (hit == translations.end()) {
esyslog("skindesigner: invalid translation token %s", token.c_str());
return translation;
}
map< string, string > translats = hit->second;
map< string, string >::iterator trans = translats.find(Setup.OSDLanguage);
if (trans != translats.end()) {
translation = trans->second;
} else {
map< string, string >::iterator transDefault = translats.find("en_EN");
if (transDefault != translats.end()) {
translation = transDefault->second;
}
}
return translation;
}
void cSkinSetup::Debug(void) {
rootMenu->Debug();
return;
dsyslog("skindesigner: Skin \"%s\" Setup Parameter Translations", skin.c_str());
for (map<string, map<string,string> >::iterator trans = translations.begin(); trans != translations.end(); trans++) {
dsyslog("skindesigner: translation token %s", (trans->first).c_str());
map<string,string> transValues = trans->second;
for (map<string,string>::iterator trans2 = transValues.begin(); trans2 != transValues.end(); trans2++) {
dsyslog("skindesigner: translation language %s value \"%s\"", (trans2->first).c_str(), (trans2->second).c_str());
}
}
}

102
extensions/skinsetup.h Normal file
View File

@@ -0,0 +1,102 @@
#ifndef __SKINSETUP_H
#define __SKINSETUP_H
#include <string>
#include <vector>
#include <map>
#include <set>
#include <sstream>
#include <vdr/plugin.h>
#include <libxml/xmlstring.h>
#include "../coreengine/globals.h"
using namespace std;
enum eSetupParameterType {
sptInt,
sptBool,
sptString,
sptUnknown
};
// --- cSkinSetupParameter -----------------------------------------------------------
class cSkinSetupParameter {
private:
public:
cSkinSetupParameter(void);
virtual ~cSkinSetupParameter(void);
eSetupParameterType type;
string name;
string displayText;
string helpText;
int min;
int max;
int value;
const char* *options;
const char* *optionsTranslated;
int numOptions;
void Debug(void);
};
// --- cSkinSetupMenu -----------------------------------------------------------
class cSkinSetupMenu {
private:
string name;
string displayText;
cSkinSetupMenu *parent;
vector < cSkinSetupMenu* > subMenus;
vector < cSkinSetupMenu* >::iterator subMenuIt;
vector < cSkinSetupParameter* > parameters;
vector < cSkinSetupParameter* >::iterator paramIt;
public:
cSkinSetupMenu(void);
virtual ~cSkinSetupMenu(void);
void SetName(string name) { this->name = name; };
void SetDisplayText(string displayText) { this->displayText = displayText; };
string GetName(void) { return name; };
string GetDisplayText(void) { return displayText; };
void SetParent(cSkinSetupMenu *p) { parent = p; };
cSkinSetupMenu *GetParent(void) { return parent; };
void AddSubMenu(cSkinSetupMenu *sub) { subMenus.push_back(sub); };
void SetParameter(eSetupParameterType paramType, string name, string displayText, string helpText, string min, string max, string value, string options);
void InitIterators(void);
void InitParameterIterator(void) { paramIt = parameters.begin(); };
cSkinSetupParameter *GetNextParameter(bool deep = true);
cSkinSetupParameter *GetParameter(string name);
void InitSubmenuIterator(void) { subMenuIt = subMenus.begin(); };
cSkinSetupMenu *GetNextSubMenu(bool deep = true);
cSkinSetupMenu *GetMenu(string &name);
void Debug(bool deep = true);
};
// --- cSkinSetup -----------------------------------------------------------
class cSkinSetup {
private:
string skin;
cSkinSetupMenu *rootMenu;
cSkinSetupMenu *currentMenu;
map < string, map< string, string > > translations;
string DoTranslate(string token);
bool Translate(string text, string &translation);
public:
cSkinSetup(string skin);
virtual ~cSkinSetup(void);
bool ReadFromXML(void);
void SetSubMenu(string name, string displayText);
void SubMenuDone(void);
void SetParameter(string type, string name, string displayText, string helpText, string min, string max, string value, string options);
void InitParameterIterator(void) { rootMenu->InitIterators(); };
cSkinSetupParameter *GetNextParameter(void);
cSkinSetupParameter *GetParameter(string name);
void SetTranslation(string translationToken, map < string, string > transl);
void AddToGlobals(cGlobals *globals);
void TranslateSetup(void);
string GetSkin(void) { return skin; };
cSkinSetupMenu *GetMenu(string &name);
void Debug(void);
};
#endif //__SKINSETUP_H

121
extensions/timers.c Normal file
View File

@@ -0,0 +1,121 @@
#include "timers.h"
#include "../services/epgsearch.h"
#include "../services/remotetimers.h"
static int CompareTimers(const void *a, const void *b) {
return (*(const cTimer **)a)->Compare(**(const cTimer **)b);
}
cGlobalSortedTimers::cGlobalSortedTimers(bool forceRefresh) : cVector<const cTimer *>(Timers.Count()) {
static bool initial = true;
static cRemoteTimerRefresh *remoteTimerRefresh = NULL;
localTimer = NULL;
if (forceRefresh)
initial = true;
//check if remotetimers plugin is available
static cPlugin* pRemoteTimers = cPluginManager::GetPlugin("remotetimers");
cSchedulesLock SchedulesLock;
const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
if (pRemoteTimers && initial) {
cString errorMsg;
pRemoteTimers->Service("RemoteTimers::RefreshTimers-v1.0", &errorMsg);
initial = false;
}
for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
if (Timer->HasFlags(tfActive))
Append(Timer);
}
//if remotetimers plugin is available, take timers also from him
if (pRemoteTimers) {
cTimer* remoteTimer = NULL;
while (pRemoteTimers->Service("RemoteTimers::ForEach-v1.0", &remoteTimer) && remoteTimer != NULL) {
remoteTimer->SetEventFromSchedule(Schedules); // make sure the event is current
if (remoteTimer->HasFlags(tfActive))
Append(remoteTimer);
}
}
Sort(CompareTimers);
int numTimers = Size();
if (numTimers > 0) {
localTimer = new bool[numTimers];
for (int i=0; i < numTimers; i++) {
if (!pRemoteTimers) {
localTimer[i] = true;
} else {
localTimer[i] = false;
for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
if (Timer == At(i)) {
localTimer[i] = true;
break;
}
}
}
}
}
if (pRemoteTimers && (remoteTimerRefresh == NULL))
remoteTimerRefresh = new cRemoteTimerRefresh();
}
cGlobalSortedTimers::~cGlobalSortedTimers(void) {
if (localTimer) {
delete[] localTimer;
}
}
bool cGlobalSortedTimers::IsRemoteTimer(int i) {
if (!localTimer)
return true;
if (i >= Size())
return true;
return !(localTimer[i]);
}
int cGlobalSortedTimers::NumTimerConfilicts(void) {
int numConflicts = 0;
cPlugin *p = cPluginManager::GetPlugin("epgsearch");
if (p) {
Epgsearch_lastconflictinfo_v1_0 *serviceData = new Epgsearch_lastconflictinfo_v1_0;
if (serviceData) {
serviceData->nextConflict = 0;
serviceData->relevantConflicts = 0;
serviceData->totalConflicts = 0;
p->Service("Epgsearch-lastconflictinfo-v1.0", serviceData);
if (serviceData->relevantConflicts > 0) {
numConflicts = serviceData->relevantConflicts;
}
delete serviceData;
}
}
return numConflicts;
}
cRemoteTimerRefresh::cRemoteTimerRefresh(): cThread("skindesigner: RemoteTimers::RefreshTimers") {
Start();
}
cRemoteTimerRefresh::~cRemoteTimerRefresh(void) {
Cancel(-1);
while (Active())
cCondWait::SleepMs(10);
}
void cRemoteTimerRefresh::Action(void) {
#define REFESH_INTERVALL_MS 30000
while (Running()) {
cCondWait::SleepMs(REFESH_INTERVALL_MS);
if (!cOsd::IsOpen()) {//make sure that no timer is currently being edited
cGlobalSortedTimers(true);
Timers.SetModified();
}
}
}

24
extensions/timers.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef __NOPACITY_TIMERS_H
#define __NOPACITY_TIMERS_H
#include <vdr/timers.h>
#include <vdr/plugin.h>
class cGlobalSortedTimers : public cVector<const cTimer *> {
private:
bool *localTimer;
public:
cGlobalSortedTimers(bool forceRefresh = false);
virtual ~cGlobalSortedTimers(void);
bool IsRemoteTimer(int i);
int NumTimerConfilicts(void);
};
class cRemoteTimerRefresh: public cThread {
protected:
virtual void Action(void);
public:
cRemoteTimerRefresh(void);
virtual ~cRemoteTimerRefresh(void);
};
#endif //__NOPACITY_TIMERS_H