mirror of
https://projects.vdr-developer.org/git/vdr-plugin-skindesigner.git
synced 2023-10-19 15:58:31 +00:00
Version 0.8.0 beta
This commit is contained in:
100
extensions/cairoimage.c
Normal file
100
extensions/cairoimage.c
Normal 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
27
extensions/cairoimage.h
Normal 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
237
extensions/curlfuncs.c
Normal 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
45
extensions/curlfuncs.h
Normal 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
96
extensions/extrecinfo.c
Normal 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
36
extensions/extrecinfo.h
Normal 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
181
extensions/fontmanager.c
Normal 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
35
extensions/fontmanager.h
Normal 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
240
extensions/helpers.c
Normal 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 <rim(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
50
extensions/helpers.h
Normal 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 <rim(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
579
extensions/imagecache.c
Normal 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
79
extensions/imagecache.h
Normal 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
507
extensions/imageloader.c
Normal 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
115
extensions/imageloader.h
Normal 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
189
extensions/libxmlwrapper.c
Normal 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;
|
||||
}
|
46
extensions/libxmlwrapper.h
Normal file
46
extensions/libxmlwrapper.h
Normal 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
301
extensions/pluginmanager.c
Normal 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);
|
||||
}
|
71
extensions/pluginmanager.h
Normal file
71
extensions/pluginmanager.h
Normal 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
219
extensions/recfolderinfo.c
Normal 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;
|
||||
}
|
43
extensions/recfolderinfo.h
Normal file
43
extensions/recfolderinfo.h
Normal 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
359
extensions/scrapmanager.c
Normal 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
28
extensions/scrapmanager.h
Normal 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
375
extensions/skinrepo.c
Normal 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
98
extensions/skinrepo.h
Normal 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
377
extensions/skinsetup.c
Normal 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
102
extensions/skinsetup.h
Normal 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
121
extensions/timers.c
Normal 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
24
extensions/timers.h
Normal 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
|
Reference in New Issue
Block a user