1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
|
#ifndef VDR_LIVE_TOOLS_H
#define VDR_LIVE_TOOLS_H
// uncomment to debug lock sequence
// #define DEBUG_LOCK
// STL headers need to be before VDR tools.h (included by <vdr/channels.h>)
#include <istream>
#include <sstream>
#include <stdexcept>
#include <vector>
#if TNTVERSION >= 30000
#include <cxxtools/log.h> // must be loaded before any VDR include because of duplicate macros (LOG_ERROR, LOG_DEBUG, LOG_INFO)
#include "cxxtools/serializationinfo.h"
#endif
#ifndef DISABLE_TEMPLATES_COLLIDING_WITH_STL
// To get rid of the swap definition in vdr/tools.h
#define DISABLE_TEMPLATES_COLLIDING_WITH_STL
#endif
#include "stringhelpers.h"
#include "xxhash.h"
#include <vdr/channels.h>
// ================ XXH128_hash_t ============================================
inline cToSvHex<32>& operator<<(cToSvHex<32> &h, const XXH128_hash_t &value) {
stringhelpers_internal::addCharsHex(h.m_buffer, 16, value.high64);
stringhelpers_internal::addCharsHex(h.m_buffer+16, 16, value.low64);
return h;
}
template <size_t N>
inline cToSvConcat<N>& operator<<(cToSvConcat<N>& s, const XXH128_hash_t &value) {
s.appendHex(value.high64);
s.appendHex(value.low64);
return s;
}
// ================ Channels ============================================
template <size_t N>
inline cToSvConcat<N> &stringAppendChannel(cToSvConcat<N> &target, const tChannelID &channelID, char point = '.', char minus = '-') {
const int st_Mask = 0xFF000000;
const int st_Pos = 0x0000FFFF;
target.concat((char) ((channelID.Source() & st_Mask) >> 24));
if (int16_t n = channelID.Source() & st_Pos) {
char ew = 'E';
if (n < 0) {
ew = 'W';
n = -n;
}
uint16_t q = (uint16_t)n / 10;
target.concat(q, point, (char)((uint16_t)n - 10*q + '0'), ew);
}
target.concat(minus, channelID.Nid(), minus, channelID.Tid(), minus, channelID.Sid());
if (channelID.Rid() ) target.concat(minus, channelID.Rid() );
return target;
}
template <size_t N>
inline cToSvConcat<N>& operator<<(cToSvConcat<N>& s, const tChannelID &channelID) {
return stringAppendChannel(s, channelID);
}
inline void stringAppend(std::string &str, const tChannelID &channelID) {
str.append(cToSvConcat(channelID));
}
template<class T, std::enable_if_t<std::is_same_v<T, tChannelID>, bool> = true>
inline T lexical_cast(cSv sv, T returnOnError = T(), const char *context = nullptr) {
char buf[sv.length()+1];
buf[sv.length()] = 0;
memcpy(buf, sv.data(), sv.length());
tChannelID ret = tChannelID::FromString(buf);
if (context && !ret.Valid() )
isyslog(PLUGIN_NAME_I18N ": WARNING, converted \"%.*s\" to invalid tChannelID, context %s", (int)sv.length(), sv.data(), context);
return ret;
}
inline std::ostream& operator<<( std::ostream& os, tChannelID const& id ) {
return os << cToSvConcat(id);
}
std::istream& operator>>( std::istream& is, tChannelID& ret );
namespace vdrlive {
extern const std::locale g_locale;
extern const std::collate<char>& g_collate_char;
template <size_t N>
inline cToSvConcat<N>& AppendHtmlEscapedAndCorrectNonUTF8(cToSvConcat<N>& target, cSv text, bool tooltip = false, const char* lf = nullptr) {
if (!lf) lf = "<br/>";
size_t i = 0; // number of not yet appended chars
const char* notAppended = text.data(); // position of the first character which is not yet appended
for (size_t pos = 0; pos < text.length(); ++pos) {
if ((unsigned char)text[pos] <= '\'') {
switch(text[pos]) {
case '\t': target.append(notAppended, i); target.append("&tab;"); notAppended += i + 1; i = 0; break;
case '\n':
case '\r':
// target.append(notAppended, i); target.append("<br/>"); notAppended += i + 1; i = 0; break;
target.append(notAppended, i); target.append(lf); notAppended += i + 1; i = 0; break;
case '&': target.append(notAppended, i); target.append("&"); notAppended += i + 1; i = 0; break;
case '\"': target.append(notAppended, i); target.append("""); notAppended += i + 1; i = 0; break;
case '\'': target.append(notAppended, i); target.append("'"); notAppended += i + 1; i = 0; break;
case ' ':
case '!':
case '#':
case '$':
case '%':
++i; break; // just append these characters, no encoding
default: // replace control characters with ?
target.append(notAppended, i); target.append("?"); notAppended += i + 1; i = 0; break;
}
continue;
}
if ((unsigned char)text[pos] <= '~') {
switch(text[pos]) {
case '\\': target.append(notAppended, i); target.append("\"); notAppended += i + 1; i = 0; break;
case '<': target.append(notAppended, i); target.append("<"); notAppended += i + 1; i = 0; break;
case '>': target.append(notAppended, i); target.append(">"); notAppended += i + 1; i = 0; break;
default:
++i; break; // just append these characters, no encoding
}
continue;
}
if (text[pos] == 127) { // replace control characters with ?
target.append(notAppended, i); target.append("?"); notAppended += i + 1; i = 0;
continue;
}
int l = utf8CodepointIsValid(text, pos);
if (l == 0) {
// invalid UTF8, replace with ?
target.append(notAppended, i); target.append("?"); notAppended += i + 1; i = 0;
continue;
}
if (l == 3 && text[pos] == '\xEE' && text[pos+1] == '\x80') {
target.append(notAppended, i);
switch (text[pos+2]) {
// mapping of VDR private-use symbols onto well-known UTF symbols
case '\x80':
// reversion symbol (counter-clockwise arrow)
target.append("\u21BA");
break;
case '\x82':
// U+E002: folder symbol
target.append("\U0001F4C1");
break;
case '\x83':
// U+E003: non-breaking space
target.append("\u00A0");
break;
case '\x8B':
// U+E00B: recording symbol
target.append("\u00AE");
break;
case '\x8C':
// U+E00C: timer symbol (full coverage)
target.append("\u23F2");
break;
case '\x91':
// U+E011: continuation symbol (clockwise arrow)
target.append("\u21BB");
break;
case '\x92':
// U+E012: running symbol
target.append("\u25B6");
break;
case '\x93':
// U+E013: VPS symbol
target.append("\U0001F185");
break;
case '\x94':
// U+E014: partial timer symbol
target.append("\u26AC");
break;
case '\x95':
// U+E015: inactive timer symbol
target.append("\u29B8");
break;
default:
target.append(text.substr(pos, 3));
break;
}
notAppended += i + 3; i = 0;
pos += 2;
continue;
}
i += l;
pos += l-1;
}
target.append(notAppended, i);
return target;
}
template <size_t N>
inline cToSvConcat<N>& AppendHtmlEscapedAndCorrectNonUTF8(cToSvConcat<N>& target, cSv text, const char* lf) {
return AppendHtmlEscapedAndCorrectNonUTF8(target, text, false, lf);
}
template <size_t N>
inline cToSvConcat<N>& AppendQuoteEscapedAndCorrectNonUTF8(cToSvConcat<N>& target, cSv text) {
size_t i = 0; // number of not yet appended chars
const char* notAppended = text.data(); // position of the first character which is not yet appended
for (size_t pos = 0; pos < text.length(); ++pos) {
if ((unsigned char)text[pos] <= '\"') {
switch(text[pos]) {
case '\t': target.append(notAppended, i); target.append("\\t"); notAppended += i + 1; i = 0; break;
case '\n': target.append(notAppended, i); target.append("\\n"); notAppended += i + 1; i = 0; break;
case '\r': target.append(notAppended, i); target.append("\\r"); notAppended += i + 1; i = 0; break;
case '\"': target.append(notAppended, i); target.append("\\"); notAppended += i; i = 1; break;
case ' ':
case '!':
++i; break; // just append these characters, no encoding
default: // replace control characters with ?
target.append(notAppended, i); target.append("?"); notAppended += i + 1; i = 0; break;
}
continue;
}
if ((unsigned char)text[pos] <= '~') {
if (text[pos] == '\\') {
target.append(notAppended, i); target.append("\\"); notAppended += i; i = 1; // this results in appending two backslashs
} else {
++i; // just append these characters, no encoding
}
continue;
}
if (text[pos] == 127) { // replace control characters with ?
target.append(notAppended, i); target.append("?"); notAppended += i + 1; i = 0;
continue;
}
int l = utf8CodepointIsValid(text, pos);
if (l == 0) {
// invalid UTF8, replace with ?
target.append(notAppended, i); target.append("?"); notAppended += i + 1; i = 0;
} else {
i += l;
pos += l-1;
}
}
target.append(notAppended, i);
return target;
}
cSv StringWordTruncate(cSv text, size_t maxLen, bool& truncated);
inline cSv StringWordTruncate(cSv text, size_t maxLen) { bool dummy; return StringWordTruncate(text, maxLen, dummy); }
template <size_t N>
inline cToSvConcat<N>& AppendTextTruncateOnWord(cToSvConcat<N>& target, cSv text, int max_len, bool tooltip = false) {
// append text to target, but only up to max_len characters. If such truncation is required, truncate at ' ' \n, ... and similar
// escape HTML characters, and correct invalid UTF8
bool truncated;
AppendHtmlEscapedAndCorrectNonUTF8(target, StringWordTruncate(text, max_len, truncated), tooltip);
if (truncated) target.append(" ...");
return target;
}
template<size_t N>
inline cToSvConcat<N>& AppendDuration(cToSvConcat<N>& target, char const* format, int duration) {
int minutes = (duration + 30) / 60;
int hours = minutes / 60;
minutes %= 60;
target.appendFormatted(format, hours, minutes);
return target;
}
std::string FormatDuration( char const* format, int duration );
cSv StringTrim(cSv str);
std::string MD5Hash(std::string const& str);
class cToSvXxHash32: public cToSvHex<8> {
public:
cToSvXxHash32(XXH32_hash_t value): cToSvHex<8>::cToSvHex(value) {}
cToSvXxHash32(cSv str): cToSvHex<8>::cToSvHex(XXH32(str.data(), str.length(), 20)) {}
};
XXH64_hash_t parse_hex_64(cSv str);
class cToSvXxHash64: public cToSvHex<16> {
public:
cToSvXxHash64(XXH64_hash_t value): cToSvHex<16>::cToSvHex(value) {}
cToSvXxHash64(cSv str): cToSvHex<16>::cToSvHex(XXH3_64bits(str.data(), str.length() )) {}
};
XXH128_hash_t parse_hex_128(cSv str);
class cToSvXxHash128: public cToSvHex<32> {
public:
cToSvXxHash128(XXH128_hash_t value): cToSvHex<32>::cToSvHex(value) { }
cToSvXxHash128(cSv str): cToSvHex<32>::cToSvHex(XXH3_128bits(str.data(), str.length() )) {}
};
time_t GetTimeT(cSv timestring); // timestring in HH:MM
std::string ExpandTimeString(std::string timestring);
time_t GetDateFromDatePicker(cSv datestring, cSv format);
std::string DatePickerToC(time_t date, cSv format);
std::string intToTimeString(int tm);
int timeStringToInt(const char *t);
int timeStringToInt(const std::string &t);
void EncodeDomId(char *toEncode_s, char *toEncode_e, char const * from = ".-:", char const * to = "pmc");
inline void DecodeDomId(char *toDecode_s, char *toDecode_e, char const * from = "pmc", char const * to = ".-:") {
EncodeDomId(toDecode_s, toDecode_e, from, to);
}
std::string EncodeDomId(cSv toEncode, char const * from = ".-:", char const * to = "pmc");
inline std::string DecodeDomId(cSv toDecode, char const * from = "pmc", char const * to = ".-:") {
return EncodeDomId(toDecode, from, to);
}
std::string FileSystemExchangeChars(cSv s, bool ToFileSystem);
bool MoveDirectory(cSv sourceDir, cSv targetDir, bool copy = false);
// methods for scraper **************************************
// tool for images returned by Tvscraper or scraper2vdr:
// convert path (valid in local file system) to path which can be used by live (in browser) to access the image
// Note: final browser path is: "/tvscraper/" + ScraperImagePath2Live(...)
cSv ScraperImagePath2Live(cSv path);
// call the service Id
// return false if there is no scraper plugin, or if the service does not exist
// otherwise, return true
// can be called with Data == Null to check is the service exits
bool ScraperCallService(const char *Id, void *Data);
} // namespace vdrlive
#endif // VDR_LIVE_TOOLS_H
|