Add multi-lingual support to the EPG. Fixes #227.
This commit is contained in:
parent
7b5cf6bfe6
commit
00902f4613
26 changed files with 1387 additions and 282 deletions
2
Makefile
2
Makefile
|
@ -99,6 +99,8 @@ SRCS = src/main.c \
|
|||
src/filebundle.c \
|
||||
src/muxes.c \
|
||||
src/config2.c \
|
||||
src/lang_codes.c \
|
||||
src/lang_str.c \
|
||||
|
||||
SRCS += src/epggrab/module.c\
|
||||
src/epggrab/channel.c\
|
||||
|
|
|
@ -43,6 +43,22 @@ htsmsg_t *config_get_all ( void )
|
|||
return htsmsg_copy(config);
|
||||
}
|
||||
|
||||
const char *config_get_language ( void )
|
||||
{
|
||||
return htsmsg_get_str(config, "language");
|
||||
}
|
||||
|
||||
int config_set_language ( const char *lang )
|
||||
{
|
||||
const char *c = config_get_language();
|
||||
if (!c || strcmp(c, lang)) {
|
||||
if (c) htsmsg_delete_field(config, "language");
|
||||
htsmsg_add_str(config, "language", lang);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *config_get_muxconfpath ( void )
|
||||
{
|
||||
return htsmsg_get_str(config, "muxconfpath");
|
||||
|
|
|
@ -32,4 +32,8 @@ const char *config_get_muxconfpath ( void );
|
|||
int config_set_muxconfpath ( const char *str )
|
||||
__attribute__((warn_unused_result));
|
||||
|
||||
const char *config_get_language ( void );
|
||||
int config_set_language ( const char *str )
|
||||
__attribute__((warn_unused_result));
|
||||
|
||||
#endif /* __TVH_CONFIG__H__ */
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "channels.h"
|
||||
#include "subscriptions.h"
|
||||
#include "muxer.h"
|
||||
#include "lang_str.h"
|
||||
|
||||
typedef struct dvr_config {
|
||||
char *dvr_config_name;
|
||||
|
@ -129,10 +130,8 @@ typedef struct dvr_entry {
|
|||
char *de_creator;
|
||||
char *de_filename; /* Initially null if no filename has been
|
||||
generated yet */
|
||||
char *de_title; /* Title in UTF-8 (from EPG) */
|
||||
char *de_ititle; /* Internal title optionally with channelname
|
||||
date and time pre/post/fixed */
|
||||
char *de_desc; /* Description in UTF-8 (from EPG) */
|
||||
lang_str_t *de_title; /* Title in UTF-8 (from EPG) */
|
||||
lang_str_t *de_desc; /* Description in UTF-8 (from EPG) */
|
||||
epg_genre_t de_content_type; /* Content type (from EPG) */
|
||||
|
||||
dvr_prio_t de_pri;
|
||||
|
@ -235,6 +234,8 @@ typedef struct dvr_autorec_entry {
|
|||
* Prototypes
|
||||
*/
|
||||
|
||||
void dvr_make_title(char *output, size_t outlen, dvr_entry_t *de);
|
||||
|
||||
dvr_config_t *dvr_config_find_by_name(const char *name);
|
||||
|
||||
dvr_config_t *dvr_config_find_by_name_default(const char *name);
|
||||
|
@ -266,7 +267,9 @@ dvr_entry_t *dvr_entry_create(const char *dvr_config_name,
|
|||
const char *creator, dvr_autorec_entry_t *dae,
|
||||
dvr_prio_t pri);
|
||||
|
||||
dvr_entry_t *dvr_entry_update(dvr_entry_t *de, const char* de_title, int de_start, int de_stop);
|
||||
dvr_entry_t *dvr_entry_update
|
||||
(dvr_entry_t *de, const char* de_title, const char *lang,
|
||||
int de_start, int de_stop);
|
||||
|
||||
void dvr_init(void);
|
||||
|
||||
|
|
|
@ -89,9 +89,11 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
|
|||
if (!e->episode->brand || dae->dae_brand != e->episode->brand) return 0;
|
||||
|
||||
if(dae->dae_title != NULL && dae->dae_title[0] != '\0') {
|
||||
if(e->episode->title == NULL ||
|
||||
regexec(&dae->dae_title_preg, e->episode->title, 0, NULL, 0))
|
||||
return 0;
|
||||
lang_str_ele_t *ls;
|
||||
if(!e->episode->title) return 0;
|
||||
RB_FOREACH(ls, e->episode->title, link)
|
||||
if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break;
|
||||
if (!ls) return 0;
|
||||
}
|
||||
|
||||
// Note: ignore channel test if we allow quality unlocking
|
||||
|
@ -560,7 +562,7 @@ void dvr_autorec_add_series_link
|
|||
epg_episode_get_epnum(ee, &epnum);
|
||||
epnump = &epnum;
|
||||
}
|
||||
_dvr_autorec_add(dvr_config_name, event->episode->title,
|
||||
_dvr_autorec_add(dvr_config_name, NULL,/*TODO DVR lang_str event->episode->title,*/
|
||||
cfg->dvr_sl_channel_lock ? event->channel : NULL,
|
||||
NULL, 0, // tag/content type
|
||||
cfg->dvr_sl_brand_lock ? ee->brand : NULL,
|
||||
|
|
135
src/dvr/dvr_db.c
135
src/dvr/dvr_db.c
|
@ -153,7 +153,7 @@ dvr_entry_notify(dvr_entry_t *de)
|
|||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
void
|
||||
dvr_make_title(char *output, size_t outlen, dvr_entry_t *de)
|
||||
{
|
||||
struct tm tm;
|
||||
|
@ -167,7 +167,7 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de)
|
|||
output[0] = 0;
|
||||
|
||||
snprintf(output + strlen(output), outlen - strlen(output),
|
||||
"%s", de->de_title);
|
||||
"%s", lang_str_get(de->de_title, NULL));
|
||||
|
||||
localtime_r(&de->de_start, &tm);
|
||||
|
||||
|
@ -210,13 +210,8 @@ static void
|
|||
dvr_entry_link(dvr_entry_t *de)
|
||||
{
|
||||
time_t now, preamble;
|
||||
char buf[100];
|
||||
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
|
||||
|
||||
dvr_make_title(buf, sizeof(buf), de);
|
||||
|
||||
de->de_ititle = strdup(buf);
|
||||
|
||||
de->de_refcnt = 1;
|
||||
|
||||
LIST_INSERT_HEAD(&dvrentries, de, de_global_link);
|
||||
|
@ -301,8 +296,22 @@ static dvr_entry_t *_dvr_entry_create (
|
|||
de->de_stop_extra = cfg->dvr_extra_time_post;
|
||||
de->de_config_name = strdup(cfg->dvr_config_name);
|
||||
de->de_creator = strdup(creator);
|
||||
de->de_title = strdup(title);
|
||||
de->de_desc = description ? strdup(description) : NULL;
|
||||
|
||||
de->de_desc = NULL;
|
||||
if (e && e->episode) {
|
||||
de->de_title = lang_str_copy(e->episode->title);
|
||||
if (e->episode->description)
|
||||
de->de_desc = lang_str_copy(e->episode->description);
|
||||
else if (e->episode->summary)
|
||||
de->de_desc = lang_str_copy(e->episode->summary);
|
||||
} else if (title) {
|
||||
de->de_title = lang_str_create();
|
||||
lang_str_add(de->de_title, title, NULL, 0);
|
||||
if (description) {
|
||||
de->de_desc = lang_str_create();
|
||||
lang_str_add(de->de_desc, description, NULL, 0);
|
||||
}
|
||||
}
|
||||
if (content_type) de->de_content_type = *content_type;
|
||||
de->de_bcast = e;
|
||||
if (e) e->getref((epg_object_t*)e);
|
||||
|
@ -320,7 +329,7 @@ static dvr_entry_t *_dvr_entry_create (
|
|||
|
||||
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" starting at %s, "
|
||||
"scheduled for recording by \"%s\"",
|
||||
de->de_title, de->de_channel->ch_name, tbuf, creator);
|
||||
lang_str_get(de->de_title, NULL), de->de_channel->ch_name, tbuf, creator);
|
||||
|
||||
dvrdb_changed();
|
||||
dvr_entry_save(de);
|
||||
|
@ -362,9 +371,7 @@ dvr_entry_create_by_event(const char *config_name,
|
|||
return _dvr_entry_create(config_name, e,
|
||||
e->channel, e->start, e->stop,
|
||||
start_extra, stop_extra,
|
||||
e->episode->title,
|
||||
e->episode->description ? e->episode->description
|
||||
: e->episode->summary,
|
||||
NULL, NULL,
|
||||
LIST_FIRST(&e->episode->genre),
|
||||
creator, dae, pri);
|
||||
}
|
||||
|
@ -416,9 +423,8 @@ dvr_entry_dec_ref(dvr_entry_t *de)
|
|||
|
||||
free(de->de_config_name);
|
||||
free(de->de_creator);
|
||||
free(de->de_title);
|
||||
free(de->de_ititle);
|
||||
free(de->de_desc);
|
||||
if (de->de_title) lang_str_destroy(de->de_title);
|
||||
if (de->de_desc) lang_str_destroy(de->de_desc);
|
||||
if(de->de_bcast) de->de_bcast->putref((epg_object_t*)de->de_bcast);
|
||||
|
||||
free(de);
|
||||
|
@ -456,11 +462,12 @@ static void
|
|||
dvr_db_load_one(htsmsg_t *c, int id)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
const char *s, *title, *creator;
|
||||
const char *s, *creator;
|
||||
channel_t *ch;
|
||||
uint32_t start, stop, bcid;
|
||||
int d;
|
||||
dvr_config_t *cfg;
|
||||
lang_str_t *title, *ls;
|
||||
|
||||
if(htsmsg_get_u32(c, "start", &start))
|
||||
return;
|
||||
|
@ -475,7 +482,7 @@ dvr_db_load_one(htsmsg_t *c, int id)
|
|||
s = htsmsg_get_str(c, "config_name");
|
||||
cfg = dvr_config_find_by_name_default(s);
|
||||
|
||||
if((title = htsmsg_get_str(c, "title")) == NULL)
|
||||
if(!(title = lang_str_deserialize(c, "title")))
|
||||
return;
|
||||
|
||||
if((creator = htsmsg_get_str(c, "creator")) == NULL)
|
||||
|
@ -493,7 +500,7 @@ dvr_db_load_one(htsmsg_t *c, int id)
|
|||
de->de_stop = stop;
|
||||
de->de_config_name = strdup(cfg->dvr_config_name);
|
||||
de->de_creator = strdup(creator);
|
||||
de->de_title = strdup(title);
|
||||
de->de_title = title;
|
||||
de->de_pri = dvr_pri2val(htsmsg_get_str(c, "pri"));
|
||||
|
||||
if(htsmsg_get_s32(c, "start_extra", &d))
|
||||
|
@ -513,7 +520,8 @@ dvr_db_load_one(htsmsg_t *c, int id)
|
|||
de->de_stop_extra = d;
|
||||
|
||||
|
||||
tvh_str_set(&de->de_desc, htsmsg_get_str(c, "description"));
|
||||
if ((ls = lang_str_deserialize(c, "description")))
|
||||
de->de_desc = ls;
|
||||
tvh_str_set(&de->de_filename, htsmsg_get_str(c, "filename"));
|
||||
|
||||
htsmsg_get_u32(c, "errorcode", &de->de_last_error);
|
||||
|
@ -592,10 +600,10 @@ dvr_entry_save(dvr_entry_t *de)
|
|||
if(de->de_filename != NULL)
|
||||
htsmsg_add_str(m, "filename", de->de_filename);
|
||||
|
||||
htsmsg_add_str(m, "title", de->de_title);
|
||||
lang_str_serialize(de->de_title, m, "title");
|
||||
|
||||
if(de->de_desc != NULL)
|
||||
htsmsg_add_str(m, "description", de->de_desc);
|
||||
lang_str_serialize(de->de_desc, m, "description");
|
||||
|
||||
htsmsg_add_str(m, "pri", dvr_val2pri(de->de_pri));
|
||||
|
||||
|
@ -635,64 +643,59 @@ dvr_timer_expire(void *aux)
|
|||
}
|
||||
|
||||
static dvr_entry_t *_dvr_entry_update
|
||||
( dvr_entry_t *de, epg_broadcast_t *e, const char *de_title,
|
||||
int de_start, int de_stop )
|
||||
( dvr_entry_t *de, epg_broadcast_t *e,
|
||||
const char *title, const char *lang,
|
||||
int start, int stop )
|
||||
{
|
||||
int save = 0;
|
||||
const char *title;
|
||||
int start, stop;
|
||||
|
||||
/* Start/Stop */
|
||||
if (e) {
|
||||
if (e->episode)
|
||||
title = e->episode->title;
|
||||
else
|
||||
title = NULL;
|
||||
start = e->start;
|
||||
stop = e->stop;
|
||||
} else {
|
||||
title = de_title;
|
||||
start = de_start;
|
||||
stop = de_stop;
|
||||
}
|
||||
|
||||
if (title && (!de->de_title || strcmp(de->de_title, title))) {
|
||||
free(de->de_title);
|
||||
de->de_title = strdup(title);
|
||||
save = 1;
|
||||
}
|
||||
|
||||
if (stop != de->de_stop) {
|
||||
de->de_stop = stop;
|
||||
save = 1;
|
||||
}
|
||||
|
||||
if (start != de->de_start) {
|
||||
if (start && (start != de->de_start)) {
|
||||
de->de_start = start;
|
||||
save = 1;
|
||||
}
|
||||
if (stop && (stop != de->de_stop)) {
|
||||
de->de_stop = stop;
|
||||
save = 1;
|
||||
}
|
||||
|
||||
if (e) {
|
||||
epg_genre_t *g;
|
||||
if (e->episode && (g = LIST_FIRST(&e->episode->genre))) {
|
||||
if (g->code != de->de_content_type.code) {
|
||||
de->de_content_type.code = g->code;
|
||||
save = 1;
|
||||
}
|
||||
}
|
||||
if (de->de_bcast != e) {
|
||||
de->de_bcast->putref((epg_object_t*)de->de_bcast);
|
||||
de->de_bcast = e;
|
||||
e->getref((epg_object_t*)e);
|
||||
/* Title */
|
||||
if (e && e->episode && e->episode->title) {
|
||||
if (de->de_title) lang_str_destroy(de->de_title);
|
||||
de->de_title = lang_str_copy(e->episode->title);
|
||||
} else if (title) {
|
||||
if (!de->de_title) de->de_title = lang_str_create();
|
||||
save = lang_str_add(de->de_title, title, lang, 1);
|
||||
}
|
||||
|
||||
/* Genre */
|
||||
if (e && e->episode) {
|
||||
epg_genre_t *g = LIST_FIRST(&e->episode->genre);
|
||||
if (g && (g->code != de->de_content_type.code)) {
|
||||
de->de_content_type.code = g->code;
|
||||
save = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Broadcast */
|
||||
if (e && (de->de_bcast != e)) {
|
||||
de->de_bcast->putref(de->de_bcast);
|
||||
de->de_bcast = e;
|
||||
e->getref(e);
|
||||
save = 1;
|
||||
}
|
||||
|
||||
/* Save changes */
|
||||
if (save) {
|
||||
dvr_entry_save(de);
|
||||
htsp_dvr_entry_update(de);
|
||||
dvr_entry_notify(de);
|
||||
|
||||
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\": Updated Timer", de->de_title, de->de_channel->ch_name);
|
||||
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\": Updated Timer",
|
||||
lang_str_get(de->de_title, NULL), de->de_channel->ch_name);
|
||||
}
|
||||
|
||||
return de;
|
||||
|
@ -702,9 +705,11 @@ static dvr_entry_t *_dvr_entry_update
|
|||
*
|
||||
*/
|
||||
dvr_entry_t *
|
||||
dvr_entry_update(dvr_entry_t *de, const char* de_title, int de_start, int de_stop)
|
||||
dvr_entry_update
|
||||
(dvr_entry_t *de, const char* de_title, const char *lang,
|
||||
int de_start, int de_stop)
|
||||
{
|
||||
return _dvr_entry_update(de, NULL, de_title, de_start, de_stop);
|
||||
return _dvr_entry_update(de, NULL, de_title, lang, de_start, de_stop);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -725,7 +730,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
|
|||
if (ude == NULL && de->de_sched_state == DVR_SCHEDULED)
|
||||
dvr_entry_cancel(de);
|
||||
else if(new_e->episode && new_e->episode->title)
|
||||
_dvr_entry_update(de, new_e, NULL, 0, 0);
|
||||
_dvr_entry_update(de, new_e, NULL, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -733,7 +738,7 @@ void dvr_event_updated ( epg_broadcast_t *e )
|
|||
{
|
||||
dvr_entry_t *de;
|
||||
de = dvr_entry_find_by_event(e);
|
||||
if (de) _dvr_entry_update(de, e, NULL, 0, 0);
|
||||
if (de) _dvr_entry_update(de, e, NULL, NULL, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -69,7 +69,7 @@ dvr_rec_subscribe(dvr_entry_t *de)
|
|||
else
|
||||
weight = 300;
|
||||
|
||||
snprintf(buf, sizeof(buf), "DVR: %s", de->de_title);
|
||||
snprintf(buf, sizeof(buf), "DVR: %s", lang_str_get(de->de_title, NULL));
|
||||
|
||||
if(de->de_mc == MC_PASS) {
|
||||
streaming_queue_init(&de->de_sq, SMT_PACKET);
|
||||
|
@ -196,11 +196,11 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
char path[500];
|
||||
int tally = 0;
|
||||
struct stat st;
|
||||
char *filename;
|
||||
char filename[1000];
|
||||
struct tm tm;
|
||||
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
|
||||
|
||||
filename = strdup(de->de_ititle);
|
||||
dvr_make_title(filename, sizeof(filename), de);
|
||||
cleanupfilename(filename,cfg->dvr_flags);
|
||||
|
||||
snprintf(path, sizeof(path), "%s", cfg->dvr_storage);
|
||||
|
@ -232,7 +232,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
|
||||
if(cfg->dvr_flags & DVR_DIR_PER_TITLE) {
|
||||
|
||||
char *title = strdup(de->de_title);
|
||||
char *title = strdup(lang_str_get(de->de_title, NULL));
|
||||
cleanupfilename(title,cfg->dvr_flags);
|
||||
snprintf(path + strlen(path), sizeof(path) - strlen(path),
|
||||
"/%s", title);
|
||||
|
@ -242,7 +242,6 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
|
||||
/* */
|
||||
if(makedirs(path) != 0) {
|
||||
free(filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -270,7 +269,6 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
|
||||
tvh_str_set(&de->de_filename, fullname);
|
||||
|
||||
free(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -290,7 +288,7 @@ dvr_rec_fatal_error(dvr_entry_t *de, const char *fmt, ...)
|
|||
|
||||
tvhlog(LOG_ERR, "dvr",
|
||||
"Recording error: \"%s\": %s",
|
||||
de->de_filename ?: de->de_title, msgbuf);
|
||||
de->de_filename ?: lang_str_get(de->de_title, NULL), msgbuf);
|
||||
}
|
||||
|
||||
|
||||
|
@ -336,7 +334,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
return;
|
||||
}
|
||||
|
||||
if(muxer_init(de->de_mux, ss, de->de_ititle)) {
|
||||
if(muxer_init(de->de_mux, ss, lang_str_get(de->de_title, NULL))) {
|
||||
dvr_rec_fatal_error(de, "Unable to init file");
|
||||
return;
|
||||
}
|
||||
|
@ -352,7 +350,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
"adapter: \"%s\", "
|
||||
"network: \"%s\", mux: \"%s\", provider: \"%s\", "
|
||||
"service: \"%s\"",
|
||||
de->de_ititle,
|
||||
de->de_filename ?: lang_str_get(de->de_title, NULL),
|
||||
si->si_adapter ?: "<N/A>",
|
||||
si->si_network ?: "<N/A>",
|
||||
si->si_mux ?: "<N/A>",
|
||||
|
@ -459,7 +457,7 @@ dvr_thread(void *aux)
|
|||
|
||||
tvhlog(LOG_INFO,
|
||||
"dvr", "Recording completed: \"%s\"",
|
||||
de->de_filename ?: de->de_title);
|
||||
de->de_filename ?: lang_str_get(de->de_title, NULL));
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -468,7 +466,7 @@ dvr_thread(void *aux)
|
|||
|
||||
tvhlog(LOG_ERR,
|
||||
"dvr", "Recording stopped: \"%s\": %s",
|
||||
de->de_filename ?: de->de_title,
|
||||
de->de_filename ?: lang_str_get(de->de_title, NULL),
|
||||
streaming_code2txt(sm->sm_code));
|
||||
}
|
||||
}
|
||||
|
@ -494,7 +492,7 @@ dvr_thread(void *aux)
|
|||
dvr_rec_set_state(de, DVR_RS_ERROR, code);
|
||||
tvhlog(LOG_ERR,
|
||||
"dvr", "Streaming error: \"%s\": %s",
|
||||
de->de_filename ?: de->de_title,
|
||||
de->de_filename ?: lang_str_get(de->de_title, NULL),
|
||||
streaming_code2txt(code));
|
||||
}
|
||||
}
|
||||
|
@ -507,7 +505,7 @@ dvr_thread(void *aux)
|
|||
|
||||
tvhlog(LOG_ERR,
|
||||
"dvr", "Recording unable to start: \"%s\": %s",
|
||||
de->de_filename ?: de->de_title,
|
||||
de->de_filename ?: lang_str_get(de->de_title, NULL),
|
||||
streaming_code2txt(sm->sm_code));
|
||||
}
|
||||
break;
|
||||
|
@ -531,7 +529,7 @@ dvr_thread(void *aux)
|
|||
static void
|
||||
dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc)
|
||||
{
|
||||
char *fmap[256];
|
||||
const char *fmap[256];
|
||||
char **args;
|
||||
char start[16];
|
||||
char stop[16];
|
||||
|
@ -554,8 +552,8 @@ dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc)
|
|||
fmap['b'] = basename(fbasename); /* basename of recoding */
|
||||
fmap['c'] = de->de_channel->ch_name; /* channel name */
|
||||
fmap['C'] = de->de_creator; /* user who created this recording */
|
||||
fmap['t'] = de->de_title; /* program title */
|
||||
fmap['d'] = de->de_desc; /* program description */
|
||||
fmap['t'] = lang_str_get(de->de_title, NULL); /* program title */
|
||||
fmap['d'] = lang_str_get(de->de_desc, NULL); /* program description */
|
||||
/* error message, empty if no error (FIXME:?) */
|
||||
fmap['e'] = tvh_strdupa(streaming_code2txt(de->de_last_error));
|
||||
fmap['S'] = start; /* start time, unix epoch */
|
||||
|
|
|
@ -416,7 +416,7 @@ mk_write_segment_header(mk_mux_t *mkm, int64_t size)
|
|||
*
|
||||
*/
|
||||
static htsbuf_queue_t *
|
||||
build_tag_string(const char *name, const char *value,
|
||||
build_tag_string(const char *name, const char *value, const char *lang,
|
||||
int targettype, const char *targettypename)
|
||||
{
|
||||
htsbuf_queue_t *q = htsbuf_queue_alloc(0);
|
||||
|
@ -432,7 +432,7 @@ build_tag_string(const char *name, const char *value,
|
|||
ebml_append_string(st, 0x45a3, name);
|
||||
ebml_append_string(st, 0x4487, value);
|
||||
ebml_append_uint(st, 0x4484, 1);
|
||||
ebml_append_string(st, 0x447a, "und");
|
||||
ebml_append_string(st, 0x447a, lang ?: "und");
|
||||
|
||||
ebml_append_master(q, 0x67c8, st);
|
||||
return q;
|
||||
|
@ -448,7 +448,7 @@ build_tag_int(const char *name, int value,
|
|||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), "%d", value);
|
||||
return build_tag_string(name, str, targettype, targettypename);
|
||||
return build_tag_string(name, str, NULL, targettype, targettypename);
|
||||
}
|
||||
|
||||
|
||||
|
@ -475,6 +475,7 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
localtime_r(de ? &de->de_start : &ebc->start, &tm);
|
||||
epg_episode_t *ee = NULL;
|
||||
channel_t *ch;
|
||||
lang_str_t *ls = NULL;
|
||||
|
||||
if (ebc) ee = ebc->episode;
|
||||
else if (de->de_bcast) ee = de->de_bcast->episode;
|
||||
|
@ -491,9 +492,9 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
tm.tm_min,
|
||||
tm.tm_sec);
|
||||
|
||||
addtag(q, build_tag_string("DATE_BROADCASTED", datestr, 0, NULL));
|
||||
addtag(q, build_tag_string("DATE_BROADCASTED", datestr, NULL, 0, NULL));
|
||||
|
||||
addtag(q, build_tag_string("ORIGINAL_MEDIA_TYPE", "TV", 0, NULL));
|
||||
addtag(q, build_tag_string("ORIGINAL_MEDIA_TYPE", "TV", NULL, 0, NULL));
|
||||
|
||||
if(de && de->de_content_type.code) {
|
||||
eg = &de->de_content_type;
|
||||
|
@ -501,17 +502,22 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
eg = LIST_FIRST(&ee->genre);
|
||||
}
|
||||
if(eg && epg_genre_get_str(eg, 1, 0, ctype, 100))
|
||||
addtag(q, build_tag_string("CONTENT_TYPE", ctype, 0, NULL));
|
||||
addtag(q, build_tag_string("CONTENT_TYPE", ctype, NULL, 0, NULL));
|
||||
|
||||
if(ch)
|
||||
addtag(q, build_tag_string("TVCHANNEL", ch->ch_name, 0, NULL));
|
||||
addtag(q, build_tag_string("TVCHANNEL", ch->ch_name, NULL, 0, NULL));
|
||||
|
||||
if(de && de->de_desc)
|
||||
addtag(q, build_tag_string("SUMMARY", de->de_desc, 0, NULL));
|
||||
ls = de->de_desc;
|
||||
else if (ee && ee->description)
|
||||
addtag(q, build_tag_string("SUMMARY", ee->description, 0, NULL));
|
||||
ls = ee->description;
|
||||
else if (ee && ee->summary)
|
||||
addtag(q, build_tag_string("SUMMARY", ee->summary, 0, NULL));
|
||||
ls = ee->summary;
|
||||
if (ls) {
|
||||
lang_str_ele_t *e;
|
||||
RB_FOREACH(e, ls, link)
|
||||
addtag(q, build_tag_string("SUMMARY", e->str, e->lang, 0, NULL));
|
||||
}
|
||||
|
||||
if (ee) {
|
||||
epg_episode_num_t num;
|
||||
|
@ -527,7 +533,7 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
40, "PART"));
|
||||
if (num.text)
|
||||
addtag(q, build_tag_string("SYNOPSIS",
|
||||
num.text, 0, NULL));
|
||||
num.text, NULL, 0, NULL));
|
||||
}
|
||||
|
||||
return q;
|
||||
|
|
232
src/epg.c
232
src/epg.c
|
@ -272,7 +272,7 @@ static int _epg_object_set_str
|
|||
( void *o, char **old, const char *new, epggrab_module_t *src )
|
||||
{
|
||||
int save = 0;
|
||||
epg_object_t *eo = (epg_object_t*)o;
|
||||
epg_object_t *eo = o;
|
||||
if ( !eo || !new ) return 0;
|
||||
if ( !_epg_object_set_grabber(eo, src) && *old ) return 0;
|
||||
if ( !*old || strcmp(*old, new) ) {
|
||||
|
@ -284,6 +284,21 @@ static int _epg_object_set_str
|
|||
return save;
|
||||
}
|
||||
|
||||
static int _epg_object_set_lang_str
|
||||
( void *o, lang_str_t **old, const char *newstr, const char *newlang,
|
||||
epggrab_module_t *src )
|
||||
{
|
||||
int update, save;
|
||||
epg_object_t *eo = o;
|
||||
if ( !eo || !newstr ) return 0;
|
||||
update = _epg_object_set_grabber(eo, src);
|
||||
if (!*old) *old = lang_str_create();
|
||||
save = lang_str_add(*old, newstr, newlang, update);
|
||||
if (save)
|
||||
_epg_object_set_updated(eo);
|
||||
return save;
|
||||
}
|
||||
|
||||
static int _epg_object_set_u8
|
||||
( void *o, uint8_t *old, const uint8_t new, epggrab_module_t *src )
|
||||
{
|
||||
|
@ -326,8 +341,8 @@ static void _epg_brand_destroy ( void *eo )
|
|||
assert(0);
|
||||
}
|
||||
_epg_object_destroy(eo, &epg_brands);
|
||||
if (eb->title) free(eb->title);
|
||||
if (eb->summary) free(eb->summary);
|
||||
if (eb->title) lang_str_destroy(eb->title);
|
||||
if (eb->summary) lang_str_destroy(eb->summary);
|
||||
if (eb->image) free(eb->image);
|
||||
free(eb);
|
||||
}
|
||||
|
@ -364,17 +379,19 @@ epg_brand_t *epg_brand_find_by_id ( uint64_t id )
|
|||
}
|
||||
|
||||
int epg_brand_set_title
|
||||
( epg_brand_t *brand, const char *title, epggrab_module_t *src )
|
||||
( epg_brand_t *brand, const char *title, const char *lang,
|
||||
epggrab_module_t *src )
|
||||
{
|
||||
if (!brand || !title) return 0;
|
||||
return _epg_object_set_str(brand, &brand->title, title, src);
|
||||
if (!brand || !title || !*title) return 0;
|
||||
return _epg_object_set_lang_str(brand, &brand->title, title, lang, src);
|
||||
}
|
||||
|
||||
int epg_brand_set_summary
|
||||
( epg_brand_t *brand, const char *summary, epggrab_module_t *src )
|
||||
( epg_brand_t *brand, const char *summary, const char *lang,
|
||||
epggrab_module_t *src )
|
||||
{
|
||||
if (!brand || !summary) return 0;
|
||||
return _epg_object_set_str(brand, &brand->summary, summary, src);
|
||||
if (!brand || !summary || !*summary) return 0;
|
||||
return _epg_object_set_lang_str(brand, &brand->summary, summary, lang, src);
|
||||
}
|
||||
|
||||
int epg_brand_set_image
|
||||
|
@ -429,9 +446,9 @@ htsmsg_t *epg_brand_serialize ( epg_brand_t *brand )
|
|||
if ( !brand || !brand->uri ) return NULL;
|
||||
if ( !(m = _epg_object_serialize(brand)) ) return NULL;
|
||||
if (brand->title)
|
||||
htsmsg_add_str(m, "title", brand->title);
|
||||
lang_str_serialize(brand->title, m, "title");
|
||||
if (brand->summary)
|
||||
htsmsg_add_str(m, "summary", brand->summary);
|
||||
lang_str_serialize(brand->summary, m, "summary");
|
||||
if (brand->season_count)
|
||||
htsmsg_add_u32(m, "season-count", brand->season_count);
|
||||
return m;
|
||||
|
@ -442,15 +459,22 @@ epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
epg_object_t **skel = _epg_brand_skel();
|
||||
epg_brand_t *eb;
|
||||
uint32_t u32;
|
||||
const char *str;
|
||||
lang_str_t *ls;
|
||||
lang_str_ele_t *e;
|
||||
|
||||
if ( !_epg_object_deserialize(m, *skel) ) return NULL;
|
||||
if ( !(eb = epg_brand_find_by_uri((*skel)->uri, create, save)) ) return NULL;
|
||||
|
||||
if ( (str = htsmsg_get_str(m, "title")) )
|
||||
*save |= epg_brand_set_title(eb, str, NULL);
|
||||
if ( (str = htsmsg_get_str(m, "summary")) )
|
||||
*save |= epg_brand_set_summary(eb, str, NULL);
|
||||
if ((ls = lang_str_deserialize(m, "title"))) {
|
||||
RB_FOREACH(e, ls, link)
|
||||
*save |= epg_brand_set_title(eb, e->str, e->lang, NULL);
|
||||
lang_str_destroy(ls);
|
||||
}
|
||||
if ((ls = lang_str_deserialize(m, "summary"))) {
|
||||
RB_FOREACH(e, ls, link)
|
||||
*save |= epg_brand_set_summary(eb, e->str, e->lang, NULL);
|
||||
lang_str_destroy(ls);
|
||||
}
|
||||
if ( !htsmsg_get_u32(m, "season-count", &u32) )
|
||||
*save |= epg_brand_set_season_count(eb, u32, NULL);
|
||||
|
||||
|
@ -470,6 +494,18 @@ htsmsg_t *epg_brand_list ( void )
|
|||
return a;
|
||||
}
|
||||
|
||||
const char *epg_brand_get_title ( const epg_brand_t *b, const char *lang )
|
||||
{
|
||||
if (!b || !b->title) return NULL;
|
||||
return lang_str_get(b->title, lang);
|
||||
}
|
||||
|
||||
const char *epg_brand_get_summary ( const epg_brand_t *b, const char *lang )
|
||||
{
|
||||
if (!b || !b->summary) return NULL;
|
||||
return lang_str_get(b->summary, lang);
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Season
|
||||
* *************************************************************************/
|
||||
|
@ -482,8 +518,8 @@ static void _epg_season_destroy ( void *eo )
|
|||
assert(0);
|
||||
}
|
||||
_epg_object_destroy(eo, &epg_seasons);
|
||||
if (es->brand) _epg_brand_rem_season(es->brand, es);
|
||||
if (es->summary) free(es->summary);
|
||||
if (es->brand) _epg_brand_rem_season(es->brand, es);
|
||||
if (es->summary) lang_str_destroy(es->summary);
|
||||
if (es->image) free(es->image);
|
||||
free(es);
|
||||
}
|
||||
|
@ -520,10 +556,11 @@ epg_season_t *epg_season_find_by_id ( uint64_t id )
|
|||
}
|
||||
|
||||
int epg_season_set_summary
|
||||
( epg_season_t *season, const char *summary, epggrab_module_t *src )
|
||||
( epg_season_t *season, const char *summary, const char *lang,
|
||||
epggrab_module_t *src )
|
||||
{
|
||||
if (!season || !summary) return 0;
|
||||
return _epg_object_set_str(season, &season->summary, summary, src);
|
||||
if (!season || !summary || !*summary) return 0;
|
||||
return _epg_object_set_lang_str(season, &season->summary, summary, lang, src);
|
||||
}
|
||||
|
||||
int epg_season_set_image
|
||||
|
@ -585,7 +622,7 @@ htsmsg_t *epg_season_serialize ( epg_season_t *season )
|
|||
if (!season || !season->uri) return NULL;
|
||||
if (!(m = _epg_object_serialize((epg_object_t*)season))) return NULL;
|
||||
if (season->summary)
|
||||
htsmsg_add_str(m, "summary", season->summary);
|
||||
lang_str_serialize(season->summary, m, "summary");
|
||||
if (season->number)
|
||||
htsmsg_add_u32(m, "number", season->number);
|
||||
if (season->episode_count)
|
||||
|
@ -602,12 +639,18 @@ epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
epg_brand_t *eb;
|
||||
uint32_t u32;
|
||||
const char *str;
|
||||
lang_str_t *ls;
|
||||
lang_str_ele_t *e;
|
||||
|
||||
if ( !_epg_object_deserialize(m, *skel) ) return NULL;
|
||||
if ( !(es = epg_season_find_by_uri((*skel)->uri, create, save)) ) return NULL;
|
||||
|
||||
if ( (str = htsmsg_get_str(m, "summary")) )
|
||||
*save |= epg_season_set_summary(es, str, NULL);
|
||||
if ((ls = lang_str_deserialize(m, "summary"))) {
|
||||
RB_FOREACH(e, ls, link) {
|
||||
*save |= epg_season_set_summary(es, e->str, e->lang, NULL);
|
||||
}
|
||||
lang_str_destroy(ls);
|
||||
}
|
||||
if ( !htsmsg_get_u32(m, "number", &u32) )
|
||||
*save |= epg_season_set_number(es, u32, NULL);
|
||||
if ( !htsmsg_get_u32(m, "episode-count", &u32) )
|
||||
|
@ -620,6 +663,13 @@ epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
return es;
|
||||
}
|
||||
|
||||
const char *epg_season_get_summary
|
||||
( const epg_season_t *s, const char *lang )
|
||||
{
|
||||
if (!s || !s->summary) return NULL;
|
||||
return lang_str_get(s->summary, lang);
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Episode
|
||||
* *************************************************************************/
|
||||
|
@ -684,10 +734,10 @@ static void _epg_episode_destroy ( void *eo )
|
|||
_epg_object_destroy(eo, &epg_episodes);
|
||||
if (ee->brand) _epg_brand_rem_episode(ee->brand, ee);
|
||||
if (ee->season) _epg_season_rem_episode(ee->season, ee);
|
||||
if (ee->title) free(ee->title);
|
||||
if (ee->subtitle) free(ee->subtitle);
|
||||
if (ee->summary) free(ee->summary);
|
||||
if (ee->description) free(ee->description);
|
||||
if (ee->title) lang_str_destroy(ee->title);
|
||||
if (ee->subtitle) lang_str_destroy(ee->subtitle);
|
||||
if (ee->summary) lang_str_destroy(ee->summary);
|
||||
if (ee->description) lang_str_destroy(ee->description);
|
||||
while ((g = LIST_FIRST(&ee->genre))) {
|
||||
LIST_REMOVE(g, link);
|
||||
free(g);
|
||||
|
@ -728,31 +778,38 @@ epg_episode_t *epg_episode_find_by_id ( uint64_t id )
|
|||
}
|
||||
|
||||
int epg_episode_set_title
|
||||
( epg_episode_t *episode, const char *title, epggrab_module_t *src )
|
||||
( epg_episode_t *episode, const char *title, const char *lang,
|
||||
epggrab_module_t *src )
|
||||
{
|
||||
if (!episode || !title) return 0;
|
||||
return _epg_object_set_str(episode, &episode->title, title, src);
|
||||
if (!episode || !title || !*title) return 0;
|
||||
return _epg_object_set_lang_str(episode, &episode->title, title, lang, src);
|
||||
}
|
||||
|
||||
int epg_episode_set_subtitle
|
||||
( epg_episode_t *episode, const char *subtitle, epggrab_module_t *src )
|
||||
( epg_episode_t *episode, const char *subtitle, const char *lang,
|
||||
epggrab_module_t *src )
|
||||
{
|
||||
if (!episode || !subtitle) return 0;
|
||||
return _epg_object_set_str(episode, &episode->subtitle, subtitle, src);
|
||||
if (!episode || !subtitle || !*subtitle) return 0;
|
||||
return _epg_object_set_lang_str(episode, &episode->subtitle,
|
||||
subtitle, lang, src);
|
||||
}
|
||||
|
||||
int epg_episode_set_summary
|
||||
( epg_episode_t *episode, const char *summary, epggrab_module_t *src )
|
||||
( epg_episode_t *episode, const char *summary, const char *lang,
|
||||
epggrab_module_t *src )
|
||||
{
|
||||
if (!episode || !summary) return 0;
|
||||
return _epg_object_set_str(episode, &episode->summary, summary, src);
|
||||
if (!episode || !summary || !*summary) return 0;
|
||||
return _epg_object_set_lang_str(episode, &episode->summary,
|
||||
summary, lang, src);
|
||||
}
|
||||
|
||||
int epg_episode_set_description
|
||||
( epg_episode_t *episode, const char *desc, epggrab_module_t *src )
|
||||
( epg_episode_t *episode, const char *desc, const char *lang,
|
||||
epggrab_module_t *src )
|
||||
{
|
||||
if (!episode || !desc) return 0;
|
||||
return _epg_object_set_str(episode, &episode->description, desc, src);
|
||||
if (!episode || !desc || !*desc) return 0;
|
||||
return _epg_object_set_lang_str(episode, &episode->description,
|
||||
desc, lang, src);
|
||||
}
|
||||
|
||||
int epg_episode_set_image
|
||||
|
@ -948,6 +1005,7 @@ int epg_episode_number_cmp ( epg_episode_num_t *a, epg_episode_num_t *b )
|
|||
// WIBNI: this could do with soem proper matching, maybe some form of
|
||||
// fuzzy string match. I did try a few things, but none of them
|
||||
// were very reliable.
|
||||
#if TODO_FUZZY_MATCH
|
||||
int epg_episode_fuzzy_match
|
||||
( epg_episode_t *episode, const char *uri, const char *title,
|
||||
const char *summary, const char *description )
|
||||
|
@ -957,6 +1015,7 @@ int epg_episode_fuzzy_match
|
|||
if ( title && episode->title && (strstr(title, episode->title) || strstr(episode->title, title)) ) return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
htsmsg_t *epg_episode_serialize ( epg_episode_t *episode )
|
||||
{
|
||||
|
@ -965,13 +1024,13 @@ htsmsg_t *epg_episode_serialize ( epg_episode_t *episode )
|
|||
if (!episode || !episode->uri) return NULL;
|
||||
if (!(m = _epg_object_serialize((epg_object_t*)episode))) return NULL;
|
||||
if (episode->title)
|
||||
htsmsg_add_str(m, "title", episode->title);
|
||||
lang_str_serialize(episode->title, m, "title");
|
||||
if (episode->subtitle)
|
||||
htsmsg_add_str(m, "subtitle", episode->subtitle);
|
||||
lang_str_serialize(episode->subtitle, m, "subtitle");
|
||||
if (episode->summary)
|
||||
htsmsg_add_str(m, "summary", episode->summary);
|
||||
lang_str_serialize(episode->summary, m, "summary");
|
||||
if (episode->description)
|
||||
htsmsg_add_str(m, "description", episode->description);
|
||||
lang_str_serialize(episode->description, m, "description");
|
||||
htsmsg_add_msg(m, "epnum", epg_episode_num_serialize(&episode->epnum));
|
||||
LIST_FOREACH(eg, &episode->genre, link) {
|
||||
if (!a) a = htsmsg_create_list();
|
||||
|
@ -998,18 +1057,33 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
htsmsg_t *sub;
|
||||
htsmsg_field_t *f;
|
||||
uint32_t u32;
|
||||
lang_str_t *ls;
|
||||
lang_str_ele_t *e;
|
||||
|
||||
if ( !_epg_object_deserialize(m, *skel) ) return NULL;
|
||||
if ( !(ee = epg_episode_find_by_uri((*skel)->uri, create, save)) ) return NULL;
|
||||
if ( !(ee = epg_episode_find_by_uri((*skel)->uri, create, save)) )
|
||||
return NULL;
|
||||
|
||||
if ( (str = htsmsg_get_str(m, "title")) )
|
||||
*save |= epg_episode_set_title(ee, str, NULL);
|
||||
if ( (str = htsmsg_get_str(m, "subtitle")) )
|
||||
*save |= epg_episode_set_subtitle(ee, str, NULL);
|
||||
if ( (str = htsmsg_get_str(m, "summary")) )
|
||||
*save |= epg_episode_set_summary(ee, str, NULL);
|
||||
if ( (str = htsmsg_get_str(m, "description")) )
|
||||
*save |= epg_episode_set_description(ee, str, NULL);
|
||||
if ((ls = lang_str_deserialize(m, "title"))) {
|
||||
RB_FOREACH(e, ls, link)
|
||||
*save |= epg_episode_set_title(ee, e->str, e->lang, NULL);
|
||||
lang_str_destroy(ls);
|
||||
}
|
||||
if ((ls = lang_str_deserialize(m, "subtitle"))) {
|
||||
RB_FOREACH(e, ls, link)
|
||||
*save |= epg_episode_set_subtitle(ee, e->str, e->lang, NULL);
|
||||
lang_str_destroy(ls);
|
||||
}
|
||||
if ((ls = lang_str_deserialize(m, "summary"))) {
|
||||
RB_FOREACH(e, ls, link)
|
||||
*save |= epg_episode_set_summary(ee, e->str, e->lang, NULL);
|
||||
lang_str_destroy(ls);
|
||||
}
|
||||
if ((ls = lang_str_deserialize(m, "description"))) {
|
||||
RB_FOREACH(e, ls, link)
|
||||
*save |= epg_episode_set_description(ee, e->str, e->lang, NULL);
|
||||
lang_str_destroy(ls);
|
||||
}
|
||||
if ( (sub = htsmsg_get_map(m, "epnum")) ) {
|
||||
epg_episode_num_deserialize(sub, &num);
|
||||
*save |= epg_episode_set_epnum(ee, &num, NULL);
|
||||
|
@ -1039,6 +1113,34 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
return ee;
|
||||
}
|
||||
|
||||
const char *epg_episode_get_title
|
||||
( const epg_episode_t *e, const char *lang )
|
||||
{
|
||||
if (!e || !e->title) return NULL;
|
||||
return lang_str_get(e->title, lang);
|
||||
}
|
||||
|
||||
const char *epg_episode_get_subtitle
|
||||
( const epg_episode_t *e, const char *lang )
|
||||
{
|
||||
if (!e || !e->subtitle) return NULL;
|
||||
return lang_str_get(e->subtitle, lang);
|
||||
}
|
||||
|
||||
const char *epg_episode_get_summary
|
||||
( const epg_episode_t *e, const char *lang )
|
||||
{
|
||||
if (!e || !e->summary) return NULL;
|
||||
return lang_str_get(e->summary, lang);
|
||||
}
|
||||
|
||||
const char *epg_episode_get_description
|
||||
( const epg_episode_t *e, const char *lang )
|
||||
{
|
||||
if (!e || !e->description) return NULL;
|
||||
return lang_str_get(e->description, lang);
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Channel
|
||||
* *************************************************************************/
|
||||
|
@ -1757,13 +1859,15 @@ htsmsg_t *epg_genres_list_all ( int major_only, int major_prefix )
|
|||
|
||||
static void _eqr_add
|
||||
( epg_query_result_t *eqr, epg_broadcast_t *e,
|
||||
epg_genre_t *genre, regex_t *preg, time_t start )
|
||||
epg_genre_t *genre, regex_t *preg, time_t start, const char *lang )
|
||||
{
|
||||
const char *title;
|
||||
|
||||
/* Ignore */
|
||||
if ( e->stop < start ) return;
|
||||
if ( !e->episode->title ) return;
|
||||
if ( !(title = epg_episode_get_title(e->episode, lang)) ) return;
|
||||
if ( genre && !epg_genre_list_contains(&e->episode->genre, genre, 1) ) return;
|
||||
if ( preg && regexec(preg, e->episode->title, 0, NULL, 0) ) return;
|
||||
if ( preg && regexec(preg, title, 0, NULL, 0)) return;
|
||||
|
||||
/* More space */
|
||||
if ( eqr->eqr_entries == eqr->eqr_alloced ) {
|
||||
|
@ -1778,17 +1882,17 @@ static void _eqr_add
|
|||
|
||||
static void _eqr_add_channel
|
||||
( epg_query_result_t *eqr, channel_t *ch, epg_genre_t *genre,
|
||||
regex_t *preg, time_t start )
|
||||
regex_t *preg, time_t start, const char *lang )
|
||||
{
|
||||
epg_broadcast_t *ebc;
|
||||
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
|
||||
if ( ebc->episode ) _eqr_add(eqr, ebc, genre, preg, start);
|
||||
if ( ebc->episode ) _eqr_add(eqr, ebc, genre, preg, start, lang);
|
||||
}
|
||||
}
|
||||
|
||||
void epg_query0
|
||||
( epg_query_result_t *eqr, channel_t *channel, channel_tag_t *tag,
|
||||
epg_genre_t *genre, const char *title )
|
||||
epg_genre_t *genre, const char *title, const char *lang )
|
||||
{
|
||||
time_t now;
|
||||
channel_tag_mapping_t *ctm;
|
||||
|
@ -1809,19 +1913,19 @@ void epg_query0
|
|||
|
||||
/* Single channel */
|
||||
if (channel && !tag) {
|
||||
_eqr_add_channel(eqr, channel, genre, preg, now);
|
||||
_eqr_add_channel(eqr, channel, genre, preg, now, lang);
|
||||
|
||||
/* Tag based */
|
||||
} else if ( tag ) {
|
||||
LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
|
||||
if(channel == NULL || ctm->ctm_channel == channel)
|
||||
_eqr_add_channel(eqr, ctm->ctm_channel, genre, preg, now);
|
||||
_eqr_add_channel(eqr, ctm->ctm_channel, genre, preg, now, lang);
|
||||
}
|
||||
|
||||
/* All channels */
|
||||
} else {
|
||||
RB_FOREACH(channel, &channel_name_tree, ch_name_link) {
|
||||
_eqr_add_channel(eqr, channel, genre, preg, now);
|
||||
_eqr_add_channel(eqr, channel, genre, preg, now, lang);
|
||||
}
|
||||
}
|
||||
if (preg) regfree(preg);
|
||||
|
@ -1830,11 +1934,11 @@ void epg_query0
|
|||
}
|
||||
|
||||
void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
|
||||
epg_genre_t *genre, const char *title)
|
||||
epg_genre_t *genre, const char *title, const char *lang)
|
||||
{
|
||||
channel_t *ch = channel ? channel_find_by_name(channel, 0, 0) : NULL;
|
||||
channel_tag_t *ct = tag ? channel_tag_find_by_name(tag, 0) : NULL;
|
||||
epg_query0(eqr, ch, ct, genre, title);
|
||||
epg_query0(eqr, ch, ct, genre, title, lang);
|
||||
}
|
||||
|
||||
void epg_query_free(epg_query_result_t *eqr)
|
||||
|
|
61
src/epg.h
61
src/epg.h
|
@ -20,6 +20,7 @@
|
|||
#define EPG_H
|
||||
|
||||
#include "settings.h"
|
||||
#include "lang_str.h"
|
||||
|
||||
/*
|
||||
* External forward decls
|
||||
|
@ -136,8 +137,8 @@ struct epg_brand
|
|||
{
|
||||
epg_object_t; ///< Base object
|
||||
|
||||
char *title; ///< Brand name
|
||||
char *summary; ///< Brand summary
|
||||
lang_str_t *title; ///< Brand name
|
||||
lang_str_t *summary; ///< Brand summary
|
||||
uint16_t season_count; ///< Total number of seasons
|
||||
char *image; ///< Brand image
|
||||
|
||||
|
@ -150,12 +151,20 @@ epg_brand_t *epg_brand_find_by_uri
|
|||
( const char *uri, int create, int *save );
|
||||
epg_brand_t *epg_brand_find_by_id ( uint64_t id );
|
||||
|
||||
/* Accessors */
|
||||
const char *epg_brand_get_title
|
||||
( const epg_brand_t *b, const char *lang );
|
||||
const char *epg_brand_get_summary
|
||||
( const epg_brand_t *b, const char *lang );
|
||||
|
||||
/* Mutators */
|
||||
int epg_brand_set_title
|
||||
( epg_brand_t *b, const char *title, struct epggrab_module *src )
|
||||
( epg_brand_t *b, const char *title, const char *lang,
|
||||
struct epggrab_module *src )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_brand_set_summary
|
||||
( epg_brand_t *b, const char *summary, struct epggrab_module *src )
|
||||
( epg_brand_t *b, const char *summary, const char *lang,
|
||||
struct epggrab_module *src )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_brand_set_season_count
|
||||
( epg_brand_t *b, uint16_t season_count, struct epggrab_module *src )
|
||||
|
@ -180,7 +189,7 @@ struct epg_season
|
|||
{
|
||||
epg_object_t; ///< Parent object
|
||||
|
||||
char *summary; ///< Season summary
|
||||
lang_str_t *summary; ///< Season summary
|
||||
uint16_t number; ///< The season number
|
||||
uint16_t episode_count; ///< Total number of episodes
|
||||
char *image; ///< Season image
|
||||
|
@ -196,9 +205,14 @@ epg_season_t *epg_season_find_by_uri
|
|||
( const char *uri, int create, int *save );
|
||||
epg_season_t *epg_season_find_by_id ( uint64_t id );
|
||||
|
||||
/* Accessors */
|
||||
const char *epg_season_get_summary
|
||||
( const epg_season_t *s, const char *lang );
|
||||
|
||||
/* Mutators */
|
||||
int epg_season_set_summary
|
||||
( epg_season_t *s, const char *summary, struct epggrab_module *src )
|
||||
( epg_season_t *s, const char *summary, const char *lang,
|
||||
struct epggrab_module *src )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_season_set_number
|
||||
( epg_season_t *s, uint16_t number, struct epggrab_module *src )
|
||||
|
@ -240,10 +254,10 @@ struct epg_episode
|
|||
{
|
||||
epg_object_t; ///< Parent object
|
||||
|
||||
char *title; ///< Title
|
||||
char *subtitle; ///< Sub-title
|
||||
char *summary; ///< Summary
|
||||
char *description; ///< An extended description
|
||||
lang_str_t *title; ///< Title
|
||||
lang_str_t *subtitle; ///< Sub-title
|
||||
lang_str_t *summary; ///< Summary
|
||||
lang_str_t *description; ///< An extended description
|
||||
char *image; ///< Episode image
|
||||
epg_genre_list_t genre; ///< Episode genre(s)
|
||||
epg_episode_num_t epnum; ///< Episode numbering
|
||||
|
@ -265,18 +279,32 @@ epg_episode_t *epg_episode_find_by_uri
|
|||
( const char *uri, int create, int *save );
|
||||
epg_episode_t *epg_episode_find_by_id ( uint64_t id );
|
||||
|
||||
/* Accessors */
|
||||
const char *epg_episode_get_title
|
||||
( const epg_episode_t *e, const char *lang );
|
||||
const char *epg_episode_get_subtitle
|
||||
( const epg_episode_t *e, const char *lang );
|
||||
const char *epg_episode_get_summary
|
||||
( const epg_episode_t *e, const char *lang );
|
||||
const char *epg_episode_get_description
|
||||
( const epg_episode_t *e, const char *lang );
|
||||
|
||||
/* Mutators */
|
||||
int epg_episode_set_title
|
||||
( epg_episode_t *e, const char *title, struct epggrab_module *src )
|
||||
( epg_episode_t *e, const char *title, const char *lang,
|
||||
struct epggrab_module *src )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_subtitle
|
||||
( epg_episode_t *e, const char *subtitle, struct epggrab_module *src )
|
||||
( epg_episode_t *e, const char *subtitle, const char *lang,
|
||||
struct epggrab_module *src )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_summary
|
||||
( epg_episode_t *e, const char *summary, struct epggrab_module *src )
|
||||
( epg_episode_t *e, const char *summary, const char *lang,
|
||||
struct epggrab_module *src )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_description
|
||||
( epg_episode_t *e, const char *description, struct epggrab_module *src )
|
||||
( epg_episode_t *e, const char *description, const char *lang,
|
||||
struct epggrab_module *src )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_number
|
||||
( epg_episode_t *e, uint16_t number, struct epggrab_module *src )
|
||||
|
@ -444,9 +472,10 @@ void epg_query_sort(epg_query_result_t *eqr);
|
|||
|
||||
/* Query routines */
|
||||
void epg_query0(epg_query_result_t *eqr, struct channel *ch,
|
||||
struct channel_tag *ct, epg_genre_t *genre, const char *title);
|
||||
struct channel_tag *ct, epg_genre_t *genre, const char *title,
|
||||
const char *lang);
|
||||
void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
|
||||
epg_genre_t *genre, const char *title);
|
||||
epg_genre_t *genre, const char *title, const char *lang);
|
||||
|
||||
|
||||
/* ************************************************************************
|
||||
|
|
|
@ -77,9 +77,9 @@ static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats )
|
|||
if (!ee) return;
|
||||
if (save) stats->episodes.total++;
|
||||
if (title)
|
||||
save |= epg_episode_set_title(ee, title, NULL);
|
||||
save |= epg_episode_set_title(ee, title, NULL, NULL);
|
||||
if (desc)
|
||||
save |= epg_episode_set_summary(ee, desc, NULL);
|
||||
save |= epg_episode_set_summary(ee, desc, NULL, NULL);
|
||||
if (!htsmsg_get_u32(c, "episode", &u32))
|
||||
save |= epg_episode_set_number(ee, u32, NULL);
|
||||
if (!htsmsg_get_u32(c, "part", &u32))
|
||||
|
|
|
@ -93,10 +93,10 @@ typedef struct eit_event
|
|||
{
|
||||
char uri[257];
|
||||
char suri[257];
|
||||
|
||||
char title[257];
|
||||
char summary[257];
|
||||
char desc[2000];
|
||||
|
||||
lang_str_t *title;
|
||||
lang_str_t *summary;
|
||||
lang_str_t *desc;
|
||||
|
||||
char *default_charset;
|
||||
|
||||
|
@ -169,26 +169,38 @@ static int _eit_desc_short_event
|
|||
( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev )
|
||||
{
|
||||
int r;
|
||||
char lang[4];
|
||||
char buf[256];
|
||||
|
||||
if ( len < 5 ) return -1;
|
||||
|
||||
/* Language (skip) */
|
||||
/* Language */
|
||||
memcpy(lang, ptr, 3);
|
||||
lang[3] = '\0';
|
||||
len -= 3;
|
||||
ptr += 3;
|
||||
|
||||
/* Title */
|
||||
if ( (r = _eit_get_string_with_len(mod, ev->title, sizeof(ev->title),
|
||||
ptr, len, ev->default_charset)) < 0 )
|
||||
if ( (r = _eit_get_string_with_len(mod, buf, sizeof(buf),
|
||||
ptr, len, ev->default_charset)) < 0 ) {
|
||||
return -1;
|
||||
} else {
|
||||
if (!ev->title) ev->title = lang_str_create();
|
||||
lang_str_add(ev->title, buf, lang, 0);
|
||||
}
|
||||
|
||||
len -= r;
|
||||
ptr += r;
|
||||
if ( len < 1 ) return -1;
|
||||
|
||||
/* Summary */
|
||||
if ( (r = _eit_get_string_with_len(mod, ev->summary, sizeof(ev->summary),
|
||||
ptr, len, ev->default_charset)) < 0 )
|
||||
if ( (r = _eit_get_string_with_len(mod, buf, sizeof(buf),
|
||||
ptr, len, ev->default_charset)) < 0 ) {
|
||||
return -1;
|
||||
} else {
|
||||
if (!ev->summary) ev->summary = lang_str_create();
|
||||
lang_str_add(ev->summary, buf, lang, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -201,6 +213,7 @@ static int _eit_desc_ext_event
|
|||
{
|
||||
int r, nitem;
|
||||
char ikey[256], ival[256];
|
||||
char buf[256], lang[4];
|
||||
|
||||
if (len < 6) return -1;
|
||||
|
||||
|
@ -208,7 +221,9 @@ static int _eit_desc_ext_event
|
|||
len -= 1;
|
||||
ptr += 1;
|
||||
|
||||
/* Language (skip) */
|
||||
/* Language */
|
||||
memcpy(lang, ptr, 3);
|
||||
lang[3] = '\0';
|
||||
len -= 3;
|
||||
ptr += 3;
|
||||
|
||||
|
@ -248,11 +263,14 @@ static int _eit_desc_ext_event
|
|||
|
||||
/* Description */
|
||||
if ( _eit_get_string_with_len(mod,
|
||||
ev->desc + strlen(ev->desc),
|
||||
sizeof(ev->desc) - strlen(ev->desc),
|
||||
buf, sizeof(buf),
|
||||
ptr, len,
|
||||
ev->default_charset) < 0 )
|
||||
ev->default_charset) < 0 ) {
|
||||
return -1;
|
||||
} else {
|
||||
if (!ev->desc) ev->desc = lang_str_create();
|
||||
lang_str_append(ev->desc, buf, lang);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -394,6 +412,7 @@ static int _eit_process_event
|
|||
epg_episode_t *ee;
|
||||
epg_season_t *es;
|
||||
eit_event_t ev;
|
||||
lang_str_ele_t *ls;
|
||||
|
||||
if ( len < 12 ) return -1;
|
||||
|
||||
|
@ -432,7 +451,7 @@ static int _eit_process_event
|
|||
|
||||
dllen -= 2;
|
||||
ptr += 2;
|
||||
if (dllen < dlen) return ret;
|
||||
if (dllen < dlen) break;
|
||||
|
||||
switch (dtag) {
|
||||
case DVB_DESC_SHORT_EVENT:
|
||||
|
@ -461,7 +480,7 @@ static int _eit_process_event
|
|||
break;
|
||||
}
|
||||
|
||||
if (r < 0) return ret;
|
||||
if (r < 0) break;
|
||||
dllen -= dlen;
|
||||
ptr += dlen;
|
||||
}
|
||||
|
@ -478,7 +497,9 @@ static int _eit_process_event
|
|||
ee = epg_episode_find_by_uri(ev.uri, 1, save);
|
||||
} else if ( !(ee = ebc->episode) ) {
|
||||
char *uri;
|
||||
uri = epg_hash(ev.title, ev.summary, ev.desc);
|
||||
uri = epg_hash(lang_str_get(ev.title, NULL),
|
||||
lang_str_get(ev.summary, NULL),
|
||||
lang_str_get(ev.desc, NULL));
|
||||
if (uri) {
|
||||
ee = epg_episode_find_by_uri(uri, 1, save);
|
||||
free(uri);
|
||||
|
@ -491,12 +512,18 @@ static int _eit_process_event
|
|||
/* Update Episode */
|
||||
if (ee) {
|
||||
*save |= epg_episode_set_is_bw(ee, ev.bw, mod);
|
||||
if ( *ev.title )
|
||||
*save |= epg_episode_set_title(ee, ev.title, mod);
|
||||
if ( *ev.summary )
|
||||
*save |= epg_episode_set_summary(ee, ev.summary, mod);
|
||||
if ( *ev.desc )
|
||||
*save |= epg_episode_set_description(ee, ev.desc, mod);
|
||||
if ( ev.title ) {
|
||||
RB_FOREACH(ls, ev.title, link)
|
||||
*save |= epg_episode_set_title(ee, ls->str, ls->lang, mod);
|
||||
}
|
||||
if ( ev.summary ) {
|
||||
RB_FOREACH(ls, ev.summary, link)
|
||||
*save |= epg_episode_set_summary(ee, ls->str, ls->lang, mod);
|
||||
}
|
||||
if ( ev.desc ) {
|
||||
RB_FOREACH(ls, ev.desc, link)
|
||||
*save |= epg_episode_set_description(ee, ls->str, ls->lang, mod);
|
||||
}
|
||||
if ( ev.genre )
|
||||
*save |= epg_episode_set_genre(ee, ev.genre, mod);
|
||||
#if TODO_ADD_EXTRA
|
||||
|
@ -514,9 +541,12 @@ static int _eit_process_event
|
|||
|
||||
/* Tidy up */
|
||||
#if TODO_ADD_EXTRA
|
||||
if (ev.extra) htsmsg_destroy(ev.extra);
|
||||
if (ev.extra) htsmsg_destroy(ev.extra);
|
||||
#endif
|
||||
if (ev.genre) epg_genre_list_destroy(ev.genre);
|
||||
if (ev.genre) epg_genre_list_destroy(ev.genre);
|
||||
if (ev.title) lang_str_destroy(ev.title);
|
||||
if (ev.summary) lang_str_destroy(ev.summary);
|
||||
if (ev.desc) lang_str_destroy(ev.desc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -355,6 +355,12 @@ static int _opentv_parse_event_section
|
|||
epg_season_t *es;
|
||||
opentv_event_t ev;
|
||||
epggrab_module_t *src = (epggrab_module_t*)mod;
|
||||
const char *lang = NULL;
|
||||
const char *str;
|
||||
|
||||
/* Get language (bit of a hack) */
|
||||
if (!strcmp(mod->dict->id, "skyit")) lang = "it";
|
||||
else if (!strcmp(mod->dict->id, "skyeng")) lang = "eng";
|
||||
|
||||
/* Channel */
|
||||
cid = ((int)buf[0] << 8) | buf[1];
|
||||
|
@ -398,14 +404,15 @@ static int _opentv_parse_event_section
|
|||
|
||||
/* Update */
|
||||
if (ee) {
|
||||
if (!ev.title && ebc->episode)
|
||||
save |= epg_episode_set_title(ee, ebc->episode->title, src);
|
||||
else if (ev.title)
|
||||
save |= epg_episode_set_title(ee, ev.title, src);
|
||||
if (!ev.title && ebc->episode) {
|
||||
if ((str = epg_episode_get_title(ebc->episode, NULL)))
|
||||
save |= epg_episode_set_title(ee, str, lang, NULL);
|
||||
} else if (ev.title)
|
||||
save |= epg_episode_set_title(ee, ev.title, lang, src);
|
||||
if (ev.summary)
|
||||
save |= epg_episode_set_summary(ee, ev.summary, src);
|
||||
save |= epg_episode_set_summary(ee, ev.summary, lang, src);
|
||||
if (ev.desc)
|
||||
save |= epg_episode_set_description(ee, ev.desc, src);
|
||||
save |= epg_episode_set_description(ee, ev.desc, lang, src);
|
||||
if (ev.cat) {
|
||||
epg_genre_list_t *egl = calloc(1, sizeof(epg_genre_list_t));
|
||||
epg_genre_list_add_by_eit(egl, ev.cat);
|
||||
|
|
|
@ -127,12 +127,12 @@ static int _pyepg_parse_brand
|
|||
|
||||
/* Set title */
|
||||
if ((str = htsmsg_xml_get_cdata_str(tags, "title"))) {
|
||||
save |= epg_brand_set_title(brand, str, mod);
|
||||
save |= epg_brand_set_title(brand, str, NULL, mod);
|
||||
}
|
||||
|
||||
/* Set summary */
|
||||
if ((str = htsmsg_xml_get_cdata_str(tags, "summary"))) {
|
||||
save |= epg_brand_set_summary(brand, str, mod);
|
||||
save |= epg_brand_set_summary(brand, str, NULL, mod);
|
||||
}
|
||||
|
||||
/* Set image */
|
||||
|
@ -182,7 +182,7 @@ static int _pyepg_parse_season
|
|||
|
||||
/* Set summary */
|
||||
if ((str = htsmsg_xml_get_cdata_str(tags, "summary"))) {
|
||||
save |= epg_season_set_summary(season, str, mod);
|
||||
save |= epg_season_set_summary(season, str, NULL, mod);
|
||||
}
|
||||
|
||||
/* Set image */
|
||||
|
@ -246,15 +246,15 @@ static int _pyepg_parse_episode
|
|||
|
||||
/* Set title/subtitle */
|
||||
if ((str = htsmsg_xml_get_cdata_str(tags, "title"))) {
|
||||
save |= epg_episode_set_title(episode, str, mod);
|
||||
save |= epg_episode_set_title(episode, str, NULL, mod);
|
||||
}
|
||||
if ((str = htsmsg_xml_get_cdata_str(tags, "subtitle"))) {
|
||||
save |= epg_episode_set_subtitle(episode, str, mod);
|
||||
save |= epg_episode_set_subtitle(episode, str, NULL, mod);
|
||||
}
|
||||
|
||||
/* Set summary */
|
||||
if ((str = htsmsg_xml_get_cdata_str(tags, "summary"))) {
|
||||
save |= epg_episode_set_summary(episode, str, mod);
|
||||
save |= epg_episode_set_summary(episode, str, NULL, mod);
|
||||
}
|
||||
|
||||
/* Number */
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "spawn.h"
|
||||
#include "htsstr.h"
|
||||
|
||||
#include "lang_str.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/private.h"
|
||||
|
@ -323,6 +324,27 @@ static epg_genre_list_t
|
|||
return egl;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a series of language strings
|
||||
*/
|
||||
static void
|
||||
_xmltv_parse_lang_str ( lang_str_t **ls, htsmsg_t *tags, const char *tname )
|
||||
{
|
||||
htsmsg_t *e, *attrib;
|
||||
htsmsg_field_t *f;
|
||||
const char *lang;
|
||||
|
||||
HTSMSG_FOREACH(f, tags) {
|
||||
if (!strcmp(f->hmf_name, tname) && (e = htsmsg_get_map_by_field(f))) {
|
||||
if (!*ls) *ls = lang_str_create();
|
||||
lang = NULL;
|
||||
if ((attrib = htsmsg_get_map(e, "attrib")))
|
||||
lang = htsmsg_get_str(attrib, "lang");
|
||||
lang_str_add(*ls, htsmsg_get_str(e, "cdata"), lang, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse tags inside of a programme
|
||||
*/
|
||||
|
@ -338,11 +360,9 @@ static int _xmltv_parse_programme_tags
|
|||
int sn = 0, sc = 0, en = 0, ec = 0, pn = 0, pc = 0;
|
||||
const char *onscreen = NULL;
|
||||
char *suri = NULL, *uri = NULL;
|
||||
const char *title = htsmsg_xml_get_cdata_str(tags, "title");
|
||||
const char *desc = htsmsg_xml_get_cdata_str(tags, "desc");
|
||||
|
||||
/* Ignore */
|
||||
if (!title) return 0;
|
||||
lang_str_t *title = NULL;
|
||||
lang_str_t *desc = NULL;
|
||||
lang_str_ele_t *ls;
|
||||
|
||||
/*
|
||||
* Broadcast
|
||||
|
@ -369,6 +389,8 @@ static int _xmltv_parse_programme_tags
|
|||
/* Get episode info */
|
||||
get_episode_info(mod, tags, &uri, &suri, &onscreen,
|
||||
&sn, &sc, &en, &ec, &pn, &pc);
|
||||
_xmltv_parse_lang_str(&title, tags, "title");
|
||||
_xmltv_parse_lang_str(&desc, tags, "desc");
|
||||
|
||||
/*
|
||||
* Season
|
||||
|
@ -383,12 +405,15 @@ static int _xmltv_parse_programme_tags
|
|||
/*
|
||||
* Episode
|
||||
*/
|
||||
if (!uri)
|
||||
uri = epg_hash(title, NULL, desc);
|
||||
if (!uri && title) {
|
||||
uri = epg_hash(lang_str_get(title, NULL), NULL, lang_str_get(desc, NULL));
|
||||
}
|
||||
if (uri) {
|
||||
ee = epg_episode_find_by_uri(uri, 1, &save);
|
||||
free(uri);
|
||||
}
|
||||
|
||||
/* Update */
|
||||
if (ee) {
|
||||
stats->episodes.total++;
|
||||
if (save) stats->episodes.created++;
|
||||
|
@ -397,10 +422,17 @@ static int _xmltv_parse_programme_tags
|
|||
|
||||
if (es)
|
||||
save |= epg_episode_set_season(ee, es, mod);
|
||||
if (title)
|
||||
save |= epg_episode_set_title(ee, title, mod);
|
||||
if (desc)
|
||||
save |= epg_episode_set_description(ee, desc, mod);
|
||||
if (title) {
|
||||
RB_FOREACH(ls, title, link) {
|
||||
save |= epg_episode_set_title(ee, ls->str, ls->lang, mod);
|
||||
}
|
||||
}
|
||||
if (desc) {
|
||||
RB_FOREACH(ls, desc, link) {
|
||||
save |= epg_episode_set_description(ee, ls->str, ls->lang, mod);
|
||||
}
|
||||
}
|
||||
|
||||
if ((egl = _xmltv_parse_categories(tags))) {
|
||||
save |= epg_episode_set_genre(ee, egl, mod);
|
||||
epg_genre_list_destroy(egl);
|
||||
|
@ -417,6 +449,10 @@ static int _xmltv_parse_programme_tags
|
|||
/* Stats */
|
||||
if (save2) stats->broadcasts.modified++;
|
||||
|
||||
/* Cleanup */
|
||||
if (title) lang_str_destroy(title);
|
||||
if (desc) lang_str_destroy(desc);
|
||||
|
||||
return save | save2 | save3;
|
||||
}
|
||||
|
||||
|
|
35
src/htsp.c
35
src/htsp.c
|
@ -382,10 +382,10 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
|
|||
htsmsg_add_s32(out, "start", de->de_start);
|
||||
htsmsg_add_s32(out, "stop", de->de_stop);
|
||||
|
||||
if( de->de_title != NULL )
|
||||
htsmsg_add_str(out, "title", de->de_title);
|
||||
if( de->de_desc != NULL )
|
||||
htsmsg_add_str(out, "description", de->de_desc);
|
||||
if( de->de_title && (s = lang_str_get(de->de_title, NULL)))
|
||||
htsmsg_add_str(out, "title", s);
|
||||
if( de->de_desc && (s = lang_str_get(de->de_desc, NULL)))
|
||||
htsmsg_add_str(out, "description", s);
|
||||
|
||||
switch(de->de_sched_state) {
|
||||
case DVR_SCHEDULED:
|
||||
|
@ -629,17 +629,11 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
if( (de = dvr_entry_find_by_id(dvrEntryId)) == NULL)
|
||||
return htsp_error("id not found");
|
||||
|
||||
if(htsmsg_get_u32(in, "start", &start))
|
||||
start = de->de_start;
|
||||
|
||||
if(htsmsg_get_u32(in, "stop", &stop))
|
||||
stop = de->de_stop;
|
||||
|
||||
start = htsmsg_get_u32_or_default(in, "start", 0);
|
||||
stop = htsmsg_get_u32_or_default(in, "stop", 0);
|
||||
title = htsmsg_get_str(in, "title");
|
||||
if (title == NULL)
|
||||
title = de->de_title;
|
||||
|
||||
de = dvr_entry_update(de, title, start, stop);
|
||||
de = dvr_entry_update(de, title, NULL, start, stop);
|
||||
|
||||
//create response
|
||||
out = htsmsg_create_map();
|
||||
|
@ -730,7 +724,7 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
}
|
||||
|
||||
//do the query
|
||||
epg_query0(&eqr, ch, ct, eg, query);
|
||||
epg_query0(&eqr, ch, ct, eg, query, NULL);
|
||||
c = eqr.eqr_entries;
|
||||
|
||||
// create reply
|
||||
|
@ -758,6 +752,7 @@ htsp_build_event(epg_broadcast_t *e)
|
|||
epg_broadcast_t *n;
|
||||
dvr_entry_t *de;
|
||||
epg_genre_t *g;
|
||||
const char *str;
|
||||
|
||||
out = htsmsg_create_map();
|
||||
|
||||
|
@ -766,12 +761,12 @@ htsp_build_event(epg_broadcast_t *e)
|
|||
htsmsg_add_u32(out, "start", e->start);
|
||||
htsmsg_add_u32(out, "stop", e->stop);
|
||||
if (e->episode) {
|
||||
if(e->episode->title != NULL)
|
||||
htsmsg_add_str(out, "title", e->episode->title);
|
||||
if(e->episode->description != NULL)
|
||||
htsmsg_add_str(out, "description", e->episode->description);
|
||||
else if(e->episode->summary != NULL)
|
||||
htsmsg_add_str(out, "description", e->episode->summary);
|
||||
if ((str = epg_episode_get_title(e->episode, NULL)))
|
||||
htsmsg_add_str(out, "title", str);
|
||||
if ((str = epg_episode_get_description(e->episode, NULL)))
|
||||
htsmsg_add_str(out, "description", str);
|
||||
else if((str = epg_episode_get_summary(e->episode, NULL)))
|
||||
htsmsg_add_str(out, "description", str);
|
||||
|
||||
if((g = LIST_FIRST(&e->episode->genre)))
|
||||
htsmsg_add_u32(out, "contentType", g->code);
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
|
||||
static void htsstr_argsplit_add(char ***argv, int *argc, char *s);
|
||||
static int htsstr_format0(const char *str, char *out, char **map);
|
||||
static int htsstr_format0(const char *str, char *out, const char **map);
|
||||
|
||||
char *
|
||||
hts_strndup(const char *src, size_t len)
|
||||
|
@ -150,9 +150,9 @@ htsstr_argsplit_free(char **argv) {
|
|||
}
|
||||
|
||||
static int
|
||||
htsstr_format0(const char *str, char *out, char **map) {
|
||||
htsstr_format0(const char *str, char *out, const char **map) {
|
||||
const char *s = str;
|
||||
char *f;
|
||||
const char *f;
|
||||
int n = 0;
|
||||
|
||||
while(*s) {
|
||||
|
@ -183,7 +183,7 @@ htsstr_format0(const char *str, char *out, char **map) {
|
|||
}
|
||||
|
||||
char *
|
||||
htsstr_format(const char *str, char **map)
|
||||
htsstr_format(const char *str, const char **map)
|
||||
{
|
||||
char *s;
|
||||
|
||||
|
|
|
@ -29,6 +29,6 @@ char **htsstr_argsplit(const char *str);
|
|||
|
||||
void htsstr_argsplit_free(char **argv);
|
||||
|
||||
char *htsstr_format(const char *str, char **map);
|
||||
char *htsstr_format(const char *str, const char **map);
|
||||
|
||||
#endif /* HTSSTR_H__ */
|
||||
|
|
561
src/lang_codes.c
Normal file
561
src/lang_codes.c
Normal file
|
@ -0,0 +1,561 @@
|
|||
/*
|
||||
* Multi-language Support - language codes
|
||||
* Copyright (C) 2012 Adam Sutton
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lang_codes.h"
|
||||
#include "config2.h"
|
||||
|
||||
/* **************************************************************************
|
||||
* Code list
|
||||
* *************************************************************************/
|
||||
|
||||
const lang_code_t lang_codes[] = {
|
||||
{ "und", NULL, NULL , "Undetermined" },
|
||||
{ "aar", "aa", NULL , "Afar" },
|
||||
{ "abk", "ab", NULL , "Abkhazian" },
|
||||
{ "ace", NULL, NULL , "Achinese" },
|
||||
{ "ach", NULL, NULL , "Acoli" },
|
||||
{ "ada", NULL, NULL , "Adangme" },
|
||||
{ "ady", NULL, NULL , "Adyghe; Adygei" },
|
||||
{ "afa", NULL, NULL , "Afro-Asiatic languages" },
|
||||
{ "afh", NULL, NULL , "Afrihili" },
|
||||
{ "afr", "af", NULL , "Afrikaans" },
|
||||
{ "ain", NULL, NULL , "Ainu" },
|
||||
{ "aka", "ak", NULL , "Akan" },
|
||||
{ "akk", NULL, NULL , "Akkadian" },
|
||||
{ "alb", "sq", "sqi", "Albanian" },
|
||||
{ "ale", NULL, NULL , "Aleut" },
|
||||
{ "alg", NULL, NULL , "Algonquian languages" },
|
||||
{ "alt", NULL, NULL , "Southern Altai" },
|
||||
{ "amh", "am", NULL , "Amharic" },
|
||||
{ "anp", NULL, NULL , "Angika" },
|
||||
{ "apa", NULL, NULL , "Apache languages" },
|
||||
{ "ara", "ar", NULL , "Arabic" },
|
||||
{ "arc", NULL, NULL , "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)" },
|
||||
{ "arg", "an", NULL , "Aragonese" },
|
||||
{ "arm", "hy", "hye", "Armenian" },
|
||||
{ "arn", NULL, NULL , "Mapudungun; Mapuche" },
|
||||
{ "arp", NULL, NULL , "Arapaho" },
|
||||
{ "art", NULL, NULL , "Artificial languages" },
|
||||
{ "arw", NULL, NULL , "Arawak" },
|
||||
{ "asm", "as", NULL , "Assamese" },
|
||||
{ "ast", NULL, NULL , "Asturian; Bable; Leonese; Asturleonese" },
|
||||
{ "ath", NULL, NULL , "Athapascan languages" },
|
||||
{ "aus", NULL, NULL , "Australian languages" },
|
||||
{ "ava", "av", NULL , "Avaric" },
|
||||
{ "ave", "ae", NULL , "Avestan" },
|
||||
{ "awa", NULL, NULL , "Awadhi" },
|
||||
{ "aym", "ay", NULL , "Aymara" },
|
||||
{ "aze", "az", NULL , "Azerbaijani" },
|
||||
{ "bad", NULL, NULL , "Banda languages" },
|
||||
{ "bai", NULL, NULL , "Bamileke languages" },
|
||||
{ "bak", "ba", NULL , "Bashkir" },
|
||||
{ "bal", NULL, NULL , "Baluchi" },
|
||||
{ "bam", "bm", NULL , "Bambara" },
|
||||
{ "ban", NULL, NULL , "Balinese" },
|
||||
{ "baq", "eu", "eus", "Basque" },
|
||||
{ "bas", NULL, NULL , "Basa" },
|
||||
{ "bat", NULL, NULL , "Baltic languages" },
|
||||
{ "bej", NULL, NULL , "Beja; Bedawiyet" },
|
||||
{ "bel", "be", NULL , "Belarusian" },
|
||||
{ "bem", NULL, NULL , "Bemba" },
|
||||
{ "ben", "bn", NULL , "Bengali" },
|
||||
{ "ber", NULL, NULL , "Berber languages" },
|
||||
{ "bho", NULL, NULL , "Bhojpuri" },
|
||||
{ "bih", "bh", NULL , "Bihari languages" },
|
||||
{ "bik", NULL, NULL , "Bikol" },
|
||||
{ "bin", NULL, NULL , "Bini; Edo" },
|
||||
{ "bis", "bi", NULL , "Bislama" },
|
||||
{ "bla", NULL, NULL , "Siksika" },
|
||||
{ "bnt", NULL, NULL , "Bantu languages" },
|
||||
{ "bos", "bs", NULL , "Bosnian" },
|
||||
{ "bra", NULL, NULL , "Braj" },
|
||||
{ "bre", "br", NULL , "Breton" },
|
||||
{ "btk", NULL, NULL , "Batak languages" },
|
||||
{ "bua", NULL, NULL , "Buriat" },
|
||||
{ "bug", NULL, NULL , "Buginese" },
|
||||
{ "bul", "bg", NULL , "Bulgarian" },
|
||||
{ "bur", "my", "mya", "Burmese" },
|
||||
{ "byn", NULL, NULL , "Blin; Bilin" },
|
||||
{ "cad", NULL, NULL , "Caddo" },
|
||||
{ "cai", NULL, NULL , "Central American Indian languages" },
|
||||
{ "car", NULL, NULL , "Galibi Carib" },
|
||||
{ "cat", "ca", NULL , "Catalan; Valencian" },
|
||||
{ "cau", NULL, NULL , "Caucasian languages" },
|
||||
{ "ceb", NULL, NULL , "Cebuano" },
|
||||
{ "cel", NULL, NULL , "Celtic languages" },
|
||||
{ "cha", "ch", NULL , "Chamorro" },
|
||||
{ "chb", NULL, NULL , "Chibcha" },
|
||||
{ "che", "ce", NULL , "Chechen" },
|
||||
{ "chg", NULL, NULL , "Chagatai" },
|
||||
{ "chi", "zh", "zho", "Chinese" },
|
||||
{ "chk", NULL, NULL , "Chuukese" },
|
||||
{ "chm", NULL, NULL , "Mari" },
|
||||
{ "chn", NULL, NULL , "Chinook jargon" },
|
||||
{ "cho", NULL, NULL , "Choctaw" },
|
||||
{ "chp", NULL, NULL , "Chipewyan; Dene Suline" },
|
||||
{ "chr", NULL, NULL , "Cherokee" },
|
||||
{ "chu", "cu", NULL , "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" },
|
||||
{ "chv", "cv", NULL , "Chuvash" },
|
||||
{ "chy", NULL, NULL , "Cheyenne" },
|
||||
{ "cmc", NULL, NULL , "Chamic languages" },
|
||||
{ "cop", NULL, NULL , "Coptic" },
|
||||
{ "cor", "kw", NULL , "Cornish" },
|
||||
{ "cos", "co", NULL , "Corsican" },
|
||||
{ "cre", "cr", NULL , "Cree" },
|
||||
{ "crh", NULL, NULL , "Crimean Tatar; Crimean Turkish" },
|
||||
{ "crp", NULL, NULL , "Creoles and pidgins" },
|
||||
{ "csb", NULL, NULL , "Kashubian" },
|
||||
{ "cus", NULL, NULL , "Cushitic languages" },
|
||||
{ "cze", "cs", "ces", "Czech" },
|
||||
{ "dak", NULL, NULL , "Dakota" },
|
||||
{ "dan", "da", NULL , "Danish" },
|
||||
{ "dar", NULL, NULL , "Dargwa" },
|
||||
{ "day", NULL, NULL , "Land Dayak languages" },
|
||||
{ "del", NULL, NULL , "Delaware" },
|
||||
{ "den", NULL, NULL , "Slave (Athapascan)" },
|
||||
{ "dgr", NULL, NULL , "Dogrib" },
|
||||
{ "din", NULL, NULL , "Dinka" },
|
||||
{ "div", "dv", NULL , "Divehi; Dhivehi; Maldivian" },
|
||||
{ "doi", NULL, NULL , "Dogri" },
|
||||
{ "dra", NULL, NULL , "Dravidian languages" },
|
||||
{ "dsb", NULL, NULL , "Lower Sorbian" },
|
||||
{ "dua", NULL, NULL , "Duala" },
|
||||
{ "dut", "nl", "nld", "Dutch; Flemish" },
|
||||
{ "dyu", NULL, NULL , "Dyula" },
|
||||
{ "dzo", "dz", NULL , "Dzongkha" },
|
||||
{ "efi", NULL, NULL , "Efik" },
|
||||
{ "egy", NULL, NULL , "Egyptian (Ancient)" },
|
||||
{ "eka", NULL, NULL , "Ekajuk" },
|
||||
{ "elx", NULL, NULL , "Elamite" },
|
||||
{ "eng", "en", NULL , "English" },
|
||||
{ "epo", "eo", NULL , "Esperanto" },
|
||||
{ "est", "et", NULL , "Estonian" },
|
||||
{ "ewe", "ee", NULL , "Ewe" },
|
||||
{ "ewo", NULL, NULL , "Ewondo" },
|
||||
{ "fan", NULL, NULL , "Fang" },
|
||||
{ "fao", "fo", NULL , "Faroese" },
|
||||
{ "fat", NULL, NULL , "Fanti" },
|
||||
{ "fij", "fj", NULL , "Fijian" },
|
||||
{ "fil", NULL, NULL , "Filipino; Pilipino" },
|
||||
{ "fin", "fi", NULL , "Finnish" },
|
||||
{ "fiu", NULL, NULL , "Finno-Ugrian languages" },
|
||||
{ "fon", NULL, NULL , "Fon" },
|
||||
{ "fre", "fr", "fra", "French" },
|
||||
{ "frr", NULL, NULL , "Northern Frisian" },
|
||||
{ "frs", NULL, NULL , "Eastern Frisian" },
|
||||
{ "fry", "fy", NULL , "Western Frisian" },
|
||||
{ "ful", "ff", NULL , "Fulah" },
|
||||
{ "fur", NULL, NULL , "Friulian" },
|
||||
{ "gaa", NULL, NULL , "Ga" },
|
||||
{ "gay", NULL, NULL , "Gayo" },
|
||||
{ "gba", NULL, NULL , "Gbaya" },
|
||||
{ "gem", NULL, NULL , "Germanic languages" },
|
||||
{ "geo", "ka", "kat", "Georgian" },
|
||||
{ "ger", "de", "deu", "German" },
|
||||
{ "gez", NULL, NULL , "Geez" },
|
||||
{ "gil", NULL, NULL , "Gilbertese" },
|
||||
{ "gla", "gd", NULL , "Gaelic; Scottish Gaelic" },
|
||||
{ "gle", "ga", NULL , "Irish" },
|
||||
{ "glg", "gl", NULL , "Galician" },
|
||||
{ "glv", "gv", NULL , "Manx" },
|
||||
{ "gon", NULL, NULL , "Gondi" },
|
||||
{ "gor", NULL, NULL , "Gorontalo" },
|
||||
{ "got", NULL, NULL , "Gothic" },
|
||||
{ "grb", NULL, NULL , "Grebo" },
|
||||
{ "grn", "gn", NULL , "Guarani" },
|
||||
{ "gsw", NULL, NULL , "Swiss German; Alemannic; Alsatian" },
|
||||
{ "guj", "gu", NULL , "Gujarati" },
|
||||
{ "gwi", NULL, NULL , "Gwich'in" },
|
||||
{ "hai", NULL, NULL , "Haida" },
|
||||
{ "hat", "ht", NULL , "Haitian; Haitian Creole" },
|
||||
{ "hau", "ha", NULL , "Hausa" },
|
||||
{ "haw", NULL, NULL , "Hawaiian" },
|
||||
{ "heb", "he", NULL , "Hebrew" },
|
||||
{ "her", "hz", NULL , "Herero" },
|
||||
{ "hil", NULL, NULL , "Hiligaynon" },
|
||||
{ "him", NULL, NULL , "Himachali languages; Western Pahari languages" },
|
||||
{ "hin", "hi", NULL , "Hindi" },
|
||||
{ "hit", NULL, NULL , "Hittite" },
|
||||
{ "hmn", NULL, NULL , "Hmong; Mong" },
|
||||
{ "hmo", "ho", NULL , "Hiri Motu" },
|
||||
{ "hrv", "hr", NULL , "Croatian" },
|
||||
{ "hsb", NULL, NULL , "Upper Sorbian" },
|
||||
{ "hun", "hu", NULL , "Hungarian" },
|
||||
{ "hup", NULL, NULL , "Hupa" },
|
||||
{ "iba", NULL, NULL , "Iban" },
|
||||
{ "ibo", "ig", NULL , "Igbo" },
|
||||
{ "ice", "is", "isl", "Icelandic" },
|
||||
{ "ido", "io", NULL , "Ido" },
|
||||
{ "iii", "ii", NULL , "Sichuan Yi; Nuosu" },
|
||||
{ "ijo", NULL, NULL , "Ijo languages" },
|
||||
{ "iku", "iu", NULL , "Inuktitut" },
|
||||
{ "ile", "ie", NULL , "Interlingue; Occidental" },
|
||||
{ "ilo", NULL, NULL , "Iloko" },
|
||||
{ "ina", "ia", NULL , "Interlingua (International Auxiliary Language Association)" },
|
||||
{ "inc", NULL, NULL , "Indic languages" },
|
||||
{ "ind", "id", NULL , "Indonesian" },
|
||||
{ "ine", NULL, NULL , "Indo-European languages" },
|
||||
{ "inh", NULL, NULL , "Ingush" },
|
||||
{ "ipk", "ik", NULL , "Inupiaq" },
|
||||
{ "ira", NULL, NULL , "Iranian languages" },
|
||||
{ "iro", NULL, NULL , "Iroquoian languages" },
|
||||
{ "ita", "it", NULL , "Italian" },
|
||||
{ "jav", "jv", NULL , "Javanese" },
|
||||
{ "jbo", NULL, NULL , "Lojban" },
|
||||
{ "jpn", "ja", NULL , "Japanese" },
|
||||
{ "jpr", NULL, NULL , "Judeo-Persian" },
|
||||
{ "jrb", NULL, NULL , "Judeo-Arabic" },
|
||||
{ "kaa", NULL, NULL , "Kara-Kalpak" },
|
||||
{ "kab", NULL, NULL , "Kabyle" },
|
||||
{ "kac", NULL, NULL , "Kachin; Jingpho" },
|
||||
{ "kal", "kl", NULL , "Kalaallisut; Greenlandic" },
|
||||
{ "kam", NULL, NULL , "Kamba" },
|
||||
{ "kan", "kn", NULL , "Kannada" },
|
||||
{ "kar", NULL, NULL , "Karen languages" },
|
||||
{ "kas", "ks", NULL , "Kashmiri" },
|
||||
{ "kau", "kr", NULL , "Kanuri" },
|
||||
{ "kaw", NULL, NULL , "Kawi" },
|
||||
{ "kaz", "kk", NULL , "Kazakh" },
|
||||
{ "kbd", NULL, NULL , "Kabardian" },
|
||||
{ "kha", NULL, NULL , "Khasi" },
|
||||
{ "khi", NULL, NULL , "Khoisan languages" },
|
||||
{ "khm", "km", NULL , "Central Khmer" },
|
||||
{ "kho", NULL, NULL , "Khotanese; Sakan" },
|
||||
{ "kik", "ki", NULL , "Kikuyu; Gikuyu" },
|
||||
{ "kin", "rw", NULL , "Kinyarwanda" },
|
||||
{ "kir", "ky", NULL , "Kirghiz; Kyrgyz" },
|
||||
{ "kmb", NULL, NULL , "Kimbundu" },
|
||||
{ "kok", NULL, NULL , "Konkani" },
|
||||
{ "kom", "kv", NULL , "Komi" },
|
||||
{ "kon", "kg", NULL , "Kongo" },
|
||||
{ "kor", "ko", NULL , "Korean" },
|
||||
{ "kos", NULL, NULL , "Kosraean" },
|
||||
{ "kpe", NULL, NULL , "Kpelle" },
|
||||
{ "krc", NULL, NULL , "Karachay-Balkar" },
|
||||
{ "krl", NULL, NULL , "Karelian" },
|
||||
{ "kro", NULL, NULL , "Kru languages" },
|
||||
{ "kru", NULL, NULL , "Kurukh" },
|
||||
{ "kua", "kj", NULL , "Kuanyama; Kwanyama" },
|
||||
{ "kum", NULL, NULL , "Kumyk" },
|
||||
{ "kur", "ku", NULL , "Kurdish" },
|
||||
{ "kut", NULL, NULL , "Kutenai" },
|
||||
{ "lad", NULL, NULL , "Ladino" },
|
||||
{ "lah", NULL, NULL , "Lahnda" },
|
||||
{ "lam", NULL, NULL , "Lamba" },
|
||||
{ "lao", "lo", NULL , "Lao" },
|
||||
{ "lat", "la", NULL , "Latin" },
|
||||
{ "lav", "lv", NULL , "Latvian" },
|
||||
{ "lez", NULL, NULL , "Lezghian" },
|
||||
{ "lim", "li", NULL , "Limburgan; Limburger; Limburgish" },
|
||||
{ "lin", "ln", NULL , "Lingala" },
|
||||
{ "lit", "lt", NULL , "Lithuanian" },
|
||||
{ "lol", NULL, NULL , "Mongo" },
|
||||
{ "loz", NULL, NULL , "Lozi" },
|
||||
{ "ltz", "lb", NULL , "Luxembourgish; Letzeburgesch" },
|
||||
{ "lua", NULL, NULL , "Luba-Lulua" },
|
||||
{ "lub", "lu", NULL , "Luba-Katanga" },
|
||||
{ "lug", "lg", NULL , "Ganda" },
|
||||
{ "lui", NULL, NULL , "Luiseno" },
|
||||
{ "lun", NULL, NULL , "Lunda" },
|
||||
{ "luo", NULL, NULL , "Luo (Kenya and Tanzania)" },
|
||||
{ "lus", NULL, NULL , "Lushai" },
|
||||
{ "mac", "mk", "mkd", "Macedonian" },
|
||||
{ "mad", NULL, NULL , "Madurese" },
|
||||
{ "mag", NULL, NULL , "Magahi" },
|
||||
{ "mah", "mh", NULL , "Marshallese" },
|
||||
{ "mai", NULL, NULL , "Maithili" },
|
||||
{ "mak", NULL, NULL , "Makasar" },
|
||||
{ "mal", "ml", NULL , "Malayalam" },
|
||||
{ "man", NULL, NULL , "Mandingo" },
|
||||
{ "mao", "mi", "mri", "Maori" },
|
||||
{ "map", NULL, NULL , "Austronesian languages" },
|
||||
{ "mar", "mr", NULL , "Marathi" },
|
||||
{ "mas", NULL, NULL , "Masai" },
|
||||
{ "may", "ms", "msa", "Malay" },
|
||||
{ "mdf", NULL, NULL , "Moksha" },
|
||||
{ "mdr", NULL, NULL , "Mandar" },
|
||||
{ "men", NULL, NULL , "Mende" },
|
||||
{ "mic", NULL, NULL , "Mi'kmaq; Micmac" },
|
||||
{ "min", NULL, NULL , "Minangkabau" },
|
||||
{ "mis", NULL, NULL , "Uncoded languages" },
|
||||
{ "mkh", NULL, NULL , "Mon-Khmer languages" },
|
||||
{ "mlg", "mg", NULL , "Malagasy" },
|
||||
{ "mlt", "mt", NULL , "Maltese" },
|
||||
{ "mnc", NULL, NULL , "Manchu" },
|
||||
{ "mni", NULL, NULL , "Manipuri" },
|
||||
{ "mno", NULL, NULL , "Manobo languages" },
|
||||
{ "moh", NULL, NULL , "Mohawk" },
|
||||
{ "mon", "mn", NULL , "Mongolian" },
|
||||
{ "mos", NULL, NULL , "Mossi" },
|
||||
{ "mul", NULL, NULL , "Multiple languages" },
|
||||
{ "mun", NULL, NULL , "Munda languages" },
|
||||
{ "mus", NULL, NULL , "Creek" },
|
||||
{ "mwl", NULL, NULL , "Mirandese" },
|
||||
{ "mwr", NULL, NULL , "Marwari" },
|
||||
{ "myn", NULL, NULL , "Mayan languages" },
|
||||
{ "myv", NULL, NULL , "Erzya" },
|
||||
{ "nah", NULL, NULL , "Nahuatl languages" },
|
||||
{ "nai", NULL, NULL , "North American Indian languages" },
|
||||
{ "nap", NULL, NULL , "Neapolitan" },
|
||||
{ "nau", "na", NULL , "Nauru" },
|
||||
{ "nav", "nv", NULL , "Navajo; Navaho" },
|
||||
{ "ndo", "ng", NULL , "Ndonga" },
|
||||
{ "nep", "ne", NULL , "Nepali" },
|
||||
{ "new", NULL, NULL , "Nepal Bhasa; Newari" },
|
||||
{ "nia", NULL, NULL , "Nias" },
|
||||
{ "nic", NULL, NULL , "Niger-Kordofanian languages" },
|
||||
{ "niu", NULL, NULL , "Niuean" },
|
||||
{ "nog", NULL, NULL , "Nogai" },
|
||||
{ "nor", "no", NULL , "Norwegian" },
|
||||
{ "nqo", NULL, NULL , "N'Ko" },
|
||||
{ "nso", NULL, NULL , "Pedi; Sepedi; Northern Sotho" },
|
||||
{ "nub", NULL, NULL , "Nubian languages" },
|
||||
{ "nwc", NULL, NULL , "Classical Newari; Old Newari; Classical Nepal Bhasa" },
|
||||
{ "nya", "ny", NULL , "Chichewa; Chewa; Nyanja" },
|
||||
{ "nym", NULL, NULL , "Nyamwezi" },
|
||||
{ "nyn", NULL, NULL , "Nyankole" },
|
||||
{ "nyo", NULL, NULL , "Nyoro" },
|
||||
{ "nzi", NULL, NULL , "Nzima" },
|
||||
{ "oci", "oc", NULL , "Occitan (post 1500)" },
|
||||
{ "oji", "oj", NULL , "Ojibwa" },
|
||||
{ "ori", "or", NULL , "Oriya" },
|
||||
{ "orm", "om", NULL , "Oromo" },
|
||||
{ "osa", NULL, NULL , "Osage" },
|
||||
{ "oss", "os", NULL , "Ossetian; Ossetic" },
|
||||
{ "oto", NULL, NULL , "Otomian languages" },
|
||||
{ "paa", NULL, NULL , "Papuan languages" },
|
||||
{ "pag", NULL, NULL , "Pangasinan" },
|
||||
{ "pal", NULL, NULL , "Pahlavi" },
|
||||
{ "pam", NULL, NULL , "Pampanga; Kapampangan" },
|
||||
{ "pan", "pa", NULL , "Panjabi; Punjabi" },
|
||||
{ "pap", NULL, NULL , "Papiamento" },
|
||||
{ "pau", NULL, NULL , "Palauan" },
|
||||
{ "per", "fa", "fas", "Persian" },
|
||||
{ "phi", NULL, NULL , "Philippine languages" },
|
||||
{ "phn", NULL, NULL , "Phoenician" },
|
||||
{ "pli", "pi", NULL , "Pali" },
|
||||
{ "pol", "pl", NULL , "Polish" },
|
||||
{ "pon", NULL, NULL , "Pohnpeian" },
|
||||
{ "por", "pt", NULL , "Portuguese" },
|
||||
{ "pra", NULL, NULL , "Prakrit languages" },
|
||||
{ "pus", "ps", NULL , "Pushto; Pashto" },
|
||||
{ "que", "qu", NULL , "Quechua" },
|
||||
{ "raj", NULL, NULL , "Rajasthani" },
|
||||
{ "rap", NULL, NULL , "Rapanui" },
|
||||
{ "rar", NULL, NULL , "Rarotongan; Cook Islands Maori" },
|
||||
{ "roa", NULL, NULL , "Romance languages" },
|
||||
{ "roh", "rm", NULL , "Romansh" },
|
||||
{ "rom", NULL, NULL , "Romany" },
|
||||
{ "rum", "ro", "ron", "Romanian; Moldavian; Moldovan" },
|
||||
{ "run", "rn", NULL , "Rundi" },
|
||||
{ "rup", NULL, NULL , "Aromanian; Arumanian; Macedo-Romanian" },
|
||||
{ "rus", "ru", NULL , "Russian" },
|
||||
{ "sad", NULL, NULL , "Sandawe" },
|
||||
{ "sag", "sg", NULL , "Sango" },
|
||||
{ "sah", NULL, NULL , "Yakut" },
|
||||
{ "sai", NULL, NULL , "South American Indian languages" },
|
||||
{ "sal", NULL, NULL , "Salishan languages" },
|
||||
{ "sam", NULL, NULL , "Samaritan Aramaic" },
|
||||
{ "san", "sa", NULL , "Sanskrit" },
|
||||
{ "sas", NULL, NULL , "Sasak" },
|
||||
{ "sat", NULL, NULL , "Santali" },
|
||||
{ "scn", NULL, NULL , "Sicilian" },
|
||||
{ "sco", NULL, NULL , "Scots" },
|
||||
{ "sel", NULL, NULL , "Selkup" },
|
||||
{ "sem", NULL, NULL , "Semitic languages" },
|
||||
{ "sgn", NULL, NULL , "Sign Languages" },
|
||||
{ "shn", NULL, NULL , "Shan" },
|
||||
{ "sid", NULL, NULL , "Sidamo" },
|
||||
{ "sin", "si", NULL , "Sinhala; Sinhalese" },
|
||||
{ "sio", NULL, NULL , "Siouan languages" },
|
||||
{ "sit", NULL, NULL , "Sino-Tibetan languages" },
|
||||
{ "sla", NULL, NULL , "Slavic languages" },
|
||||
{ "slo", "sk", "slk", "Slovak" },
|
||||
{ "slv", "sl", NULL , "Slovenian" },
|
||||
{ "sma", NULL, NULL , "Southern Sami" },
|
||||
{ "sme", "se", NULL , "Northern Sami" },
|
||||
{ "smi", NULL, NULL , "Sami languages" },
|
||||
{ "smj", NULL, NULL , "Lule Sami" },
|
||||
{ "smn", NULL, NULL , "Inari Sami" },
|
||||
{ "smo", "sm", NULL , "Samoan" },
|
||||
{ "sms", NULL, NULL , "Skolt Sami" },
|
||||
{ "sna", "sn", NULL , "Shona" },
|
||||
{ "snd", "sd", NULL , "Sindhi" },
|
||||
{ "snk", NULL, NULL , "Soninke" },
|
||||
{ "sog", NULL, NULL , "Sogdian" },
|
||||
{ "som", "so", NULL , "Somali" },
|
||||
{ "son", NULL, NULL , "Songhai languages" },
|
||||
{ "spa", "es", NULL , "Spanish; Castilian" },
|
||||
{ "srd", "sc", NULL , "Sardinian" },
|
||||
{ "srn", NULL, NULL , "Sranan Tongo" },
|
||||
{ "srp", "sr", NULL , "Serbian" },
|
||||
{ "srr", NULL, NULL , "Serer" },
|
||||
{ "ssa", NULL, NULL , "Nilo-Saharan languages" },
|
||||
{ "ssw", "ss", NULL , "Swati" },
|
||||
{ "suk", NULL, NULL , "Sukuma" },
|
||||
{ "sun", "su", NULL , "Sundanese" },
|
||||
{ "sus", NULL, NULL , "Susu" },
|
||||
{ "sux", NULL, NULL , "Sumerian" },
|
||||
{ "swa", "sw", NULL , "Swahili" },
|
||||
{ "swe", "sv", NULL , "Swedish" },
|
||||
{ "syc", NULL, NULL , "Classical Syriac" },
|
||||
{ "syr", NULL, NULL , "Syriac" },
|
||||
{ "tah", "ty", NULL , "Tahitian" },
|
||||
{ "tai", NULL, NULL , "Tai languages" },
|
||||
{ "tam", "ta", NULL , "Tamil" },
|
||||
{ "tat", "tt", NULL , "Tatar" },
|
||||
{ "tel", "te", NULL , "Telugu" },
|
||||
{ "tem", NULL, NULL , "Timne" },
|
||||
{ "ter", NULL, NULL , "Tereno" },
|
||||
{ "tet", NULL, NULL , "Tetum" },
|
||||
{ "tgk", "tg", NULL , "Tajik" },
|
||||
{ "tgl", "tl", NULL , "Tagalog" },
|
||||
{ "tha", "th", NULL , "Thai" },
|
||||
{ "tib", "bo", "bod", "Tibetan" },
|
||||
{ "tig", NULL, NULL , "Tigre" },
|
||||
{ "tir", "ti", NULL , "Tigrinya" },
|
||||
{ "tiv", NULL, NULL , "Tiv" },
|
||||
{ "tkl", NULL, NULL , "Tokelau" },
|
||||
{ "tlh", NULL, NULL , "Klingon; tlhIngan-Hol" },
|
||||
{ "tli", NULL, NULL , "Tlingit" },
|
||||
{ "tmh", NULL, NULL , "Tamashek" },
|
||||
{ "tog", NULL, NULL , "Tonga (Nyasa)" },
|
||||
{ "ton", "to", NULL , "Tonga (Tonga Islands)" },
|
||||
{ "tpi", NULL, NULL , "Tok Pisin" },
|
||||
{ "tsi", NULL, NULL , "Tsimshian" },
|
||||
{ "tsn", "tn", NULL , "Tswana" },
|
||||
{ "tso", "ts", NULL , "Tsonga" },
|
||||
{ "tuk", "tk", NULL , "Turkmen" },
|
||||
{ "tum", NULL, NULL , "Tumbuka" },
|
||||
{ "tup", NULL, NULL , "Tupi languages" },
|
||||
{ "tur", "tr", NULL , "Turkish" },
|
||||
{ "tut", NULL, NULL , "Altaic languages" },
|
||||
{ "tvl", NULL, NULL , "Tuvalu" },
|
||||
{ "twi", "tw", NULL , "Twi" },
|
||||
{ "tyv", NULL, NULL , "Tuvinian" },
|
||||
{ "udm", NULL, NULL , "Udmurt" },
|
||||
{ "uga", NULL, NULL , "Ugaritic" },
|
||||
{ "uig", "ug", NULL , "Uighur; Uyghur" },
|
||||
{ "ukr", "uk", NULL , "Ukrainian" },
|
||||
{ "umb", NULL, NULL , "Umbundu" },
|
||||
{ "urd", "ur", NULL , "Urdu" },
|
||||
{ "uzb", "uz", NULL , "Uzbek" },
|
||||
{ "vai", NULL, NULL , "Vai" },
|
||||
{ "ven", "ve", NULL , "Venda" },
|
||||
{ "vie", "vi", NULL , "Vietnamese" },
|
||||
{ "vol", "vo", NULL , "Volapük" },
|
||||
{ "vot", NULL, NULL , "Votic" },
|
||||
{ "wak", NULL, NULL , "Wakashan languages" },
|
||||
{ "wal", NULL, NULL , "Wolaitta; Wolaytta" },
|
||||
{ "war", NULL, NULL , "Waray" },
|
||||
{ "was", NULL, NULL , "Washo" },
|
||||
{ "wel", "cy", "cym", "Welsh" },
|
||||
{ "wen", NULL, NULL , "Sorbian languages" },
|
||||
{ "wln", "wa", NULL , "Walloon" },
|
||||
{ "wol", "wo", NULL , "Wolof" },
|
||||
{ "xal", NULL, NULL , "Kalmyk; Oirat" },
|
||||
{ "xho", "xh", NULL , "Xhosa" },
|
||||
{ "yao", NULL, NULL , "Yao" },
|
||||
{ "yap", NULL, NULL , "Yapese" },
|
||||
{ "yid", "yi", NULL , "Yiddish" },
|
||||
{ "yor", "yo", NULL , "Yoruba" },
|
||||
{ "ypk", NULL, NULL , "Yupik languages" },
|
||||
{ "zap", NULL, NULL , "Zapotec" },
|
||||
{ "zbl", NULL, NULL , "Blissymbols; Blissymbolics; Bliss" },
|
||||
{ "zen", NULL, NULL , "Zenaga" },
|
||||
{ "zha", "za", NULL , "Zhuang; Chuang" },
|
||||
{ "znd", NULL, NULL , "Zande languages" },
|
||||
{ "zul", "zu", NULL , "Zulu" },
|
||||
{ "zun", NULL, NULL , "Zuni" },
|
||||
{ "zza", NULL, NULL , "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki" },
|
||||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
/* **************************************************************************
|
||||
* Functions
|
||||
* *************************************************************************/
|
||||
|
||||
const char *lang_code_get ( const char *code )
|
||||
{
|
||||
int i;
|
||||
char tmp[4];
|
||||
|
||||
if (code && *code) {
|
||||
|
||||
/* Extract the code (lowercase) */
|
||||
i = 0;
|
||||
while (i < 3 && *code) {
|
||||
if (*code == ';' || *code == ',' || *code == '-') break;
|
||||
if (*code != ' ')
|
||||
tmp[i++] = *code | 0x20; // |0x20 = lower case
|
||||
code++;
|
||||
}
|
||||
tmp[i] = '\0';
|
||||
|
||||
/* Search */
|
||||
if (i) {
|
||||
const lang_code_t *c = lang_codes;
|
||||
while (c->code2b) {
|
||||
if ( !strcmp(tmp, c->code2b) ) return c->code2b;
|
||||
if ( c->code1 && !strcmp(tmp, c->code1) ) return c->code2b;
|
||||
if ( c->code2t && !strcmp(tmp, c->code2t) ) return c->code2b;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return lang_codes[0].code2b;
|
||||
}
|
||||
|
||||
const char **lang_code_split ( const char *codes )
|
||||
{
|
||||
int n;
|
||||
const char *c, *p, **ret;
|
||||
|
||||
/* Defaults */
|
||||
if (!codes) codes = config_get_language();
|
||||
|
||||
/* No config */
|
||||
if (!codes) return NULL;
|
||||
|
||||
/* Count entries */
|
||||
n = 0;
|
||||
c = codes;
|
||||
while (*c) {
|
||||
if (*c == ',') n++;
|
||||
c++;
|
||||
}
|
||||
ret = calloc(2+n, sizeof(char*));
|
||||
|
||||
/* Create list */
|
||||
n = 0;
|
||||
p = c = codes;
|
||||
while (*c) {
|
||||
if (*c == ',') {
|
||||
ret[n++] = lang_code_get(p);
|
||||
p = c + 1;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
if (*p) ret[n++] = lang_code_get(p);
|
||||
ret[n] = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
38
src/lang_codes.h
Normal file
38
src/lang_codes.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Multi-language Support - language codes
|
||||
* Copyright (C) 2012 Adam Sutton
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TVH_LANG_CODES_H__
|
||||
#define __TVH_LANG_CODES_H__
|
||||
|
||||
typedef struct lang_code
|
||||
{
|
||||
const char *code2b; ///< ISO 639-2 B
|
||||
const char *code1; ///< ISO 639-1
|
||||
const char *code2t; ///< ISO 639-2 T
|
||||
const char *desc; ///< Description
|
||||
} lang_code_t;
|
||||
|
||||
extern const lang_code_t lang_codes[];
|
||||
|
||||
/* Convert code to preferred internal code */
|
||||
const char *lang_code_get ( const char *code );
|
||||
|
||||
/* Split list of codes as per HTTP Language-Accept spec */
|
||||
const char **lang_code_split ( const char *codes );
|
||||
|
||||
#endif /* __TVH_LANG_CODES_H__ */
|
191
src/lang_str.c
Normal file
191
src/lang_str.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Multi-language String support
|
||||
* Copyright (C) 2012 Adam Sutton
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "redblack.h"
|
||||
#include "lang_codes.h"
|
||||
#include "lang_str.h"
|
||||
|
||||
/* ************************************************************************
|
||||
* Support
|
||||
* ***********************************************************************/
|
||||
|
||||
/* Compare language codes */
|
||||
static int _lang_cmp ( void *a, void *b )
|
||||
{
|
||||
return strcmp(((lang_str_ele_t*)a)->lang, ((lang_str_ele_t*)b)->lang);
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Language String
|
||||
* ***********************************************************************/
|
||||
|
||||
/* Create new instance */
|
||||
lang_str_t *lang_str_create ( void )
|
||||
{
|
||||
return calloc(1, sizeof(lang_str_t));
|
||||
}
|
||||
|
||||
/* Destroy (free memory) */
|
||||
void lang_str_destroy ( lang_str_t *ls )
|
||||
{
|
||||
lang_str_ele_t *e;
|
||||
while ((e = RB_FIRST(ls))) {
|
||||
if (e->str) free(e->str);
|
||||
RB_REMOVE(ls, e, link);
|
||||
free(e);
|
||||
}
|
||||
free(ls);
|
||||
}
|
||||
|
||||
/* Copy the lang_str instance */
|
||||
lang_str_t *lang_str_copy ( const lang_str_t *ls )
|
||||
{
|
||||
lang_str_t *ret = lang_str_create();
|
||||
lang_str_ele_t *e;
|
||||
RB_FOREACH(e, ls, link)
|
||||
lang_str_add(ret, e->str, e->lang, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get language element */
|
||||
lang_str_ele_t *lang_str_get2
|
||||
( lang_str_t *ls, const char *lang )
|
||||
{
|
||||
int i;
|
||||
const char **langs;
|
||||
lang_str_ele_t skel, *e = NULL;
|
||||
|
||||
if (!ls) return NULL;
|
||||
|
||||
/* Check config/requested langs */
|
||||
if ((langs = lang_code_split(lang))) {
|
||||
i = 0;
|
||||
while (langs[i]) {
|
||||
skel.lang = langs[i];
|
||||
if ((e = RB_FIND(ls, &skel, link, _lang_cmp)))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
free(langs);
|
||||
}
|
||||
|
||||
/* Use first available */
|
||||
if (!e) e = RB_FIRST(ls);
|
||||
|
||||
/* Return */
|
||||
return e;
|
||||
}
|
||||
|
||||
/* Get string */
|
||||
const char *lang_str_get
|
||||
( lang_str_t *ls, const char *lang )
|
||||
{
|
||||
lang_str_ele_t *e = lang_str_get2(ls, lang);
|
||||
return e ? e->str : NULL;
|
||||
}
|
||||
|
||||
/* Internal insertion routine */
|
||||
static int _lang_str_add
|
||||
( lang_str_t *ls, const char *str, const char *lang, int update, int append )
|
||||
{
|
||||
int save = 0;
|
||||
static lang_str_ele_t *skel = NULL;
|
||||
lang_str_ele_t *e;
|
||||
|
||||
if (!str) return 0;
|
||||
|
||||
/* Get proper code */
|
||||
if (!(lang = lang_code_get(lang))) return 0;
|
||||
|
||||
/* Create skel */
|
||||
if (!skel) skel = calloc(1, sizeof(lang_str_ele_t));
|
||||
skel->lang = lang;
|
||||
|
||||
/* Create */
|
||||
e = RB_INSERT_SORTED(ls, skel, link, _lang_cmp);
|
||||
if (!e) {
|
||||
skel->str = strdup(str);
|
||||
skel = NULL;
|
||||
save = 1;
|
||||
|
||||
/* Append */
|
||||
} else if (append) {
|
||||
e->str = realloc(e->str, strlen(e->str) + strlen(str) + 1);
|
||||
strcat(e->str, str);
|
||||
save = 1;
|
||||
|
||||
/* Update */
|
||||
} else if (update && strcmp(str, e->str)) {
|
||||
free(e->str);
|
||||
e->str = strdup(str);
|
||||
save = 1;
|
||||
}
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
/* Add new string (or replace existing one) */
|
||||
int lang_str_add
|
||||
( lang_str_t *ls, const char *str, const char *lang, int update )
|
||||
{
|
||||
return _lang_str_add(ls, str, lang, update, 0);
|
||||
}
|
||||
|
||||
/* Append to existing string (or add new one) */
|
||||
int lang_str_append
|
||||
( lang_str_t *ls, const char *str, const char *lang )
|
||||
{
|
||||
return _lang_str_add(ls, str, lang, 0, 1);
|
||||
}
|
||||
|
||||
/* Serialize */
|
||||
void lang_str_serialize ( lang_str_t *ls, htsmsg_t *m, const char *f )
|
||||
{
|
||||
lang_str_ele_t *e;
|
||||
if (!ls) return;
|
||||
htsmsg_t *a = htsmsg_create_map();
|
||||
RB_FOREACH(e, ls, link) {
|
||||
htsmsg_add_str(a, e->lang, e->str);
|
||||
}
|
||||
htsmsg_add_msg(m, f, a);
|
||||
}
|
||||
|
||||
/* De-serialize */
|
||||
lang_str_t *lang_str_deserialize ( htsmsg_t *m, const char *n )
|
||||
{
|
||||
lang_str_t *ret = NULL;
|
||||
htsmsg_t *a;
|
||||
htsmsg_field_t *f;
|
||||
const char *str;
|
||||
|
||||
if ((a = htsmsg_get_map(m, n))) {
|
||||
ret = lang_str_create();
|
||||
HTSMSG_FOREACH(f, a) {
|
||||
if ((str = htsmsg_field_get_string(f))) {
|
||||
lang_str_add(ret, str, f->hmf_name, 0);
|
||||
}
|
||||
}
|
||||
} else if ((str = htsmsg_get_str(m, n))) {
|
||||
ret = lang_str_create();
|
||||
lang_str_add(ret, str, NULL, 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
55
src/lang_str.h
Normal file
55
src/lang_str.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Multi-language String support
|
||||
* Copyright (C) 2012 Adam Sutton
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TVH_LANG_STR_H__
|
||||
#define __TVH_LANG_STR_H__
|
||||
|
||||
#include "redblack.h"
|
||||
#include "htsmsg.h"
|
||||
|
||||
typedef struct lang_str_ele
|
||||
{
|
||||
RB_ENTRY(lang_str_ele) link;
|
||||
const char *lang;
|
||||
char *str;
|
||||
} lang_str_ele_t;
|
||||
|
||||
typedef RB_HEAD(lang_str, lang_str_ele) lang_str_t;
|
||||
|
||||
/* Create/Destroy */
|
||||
void lang_str_destroy ( lang_str_t *ls );
|
||||
lang_str_t *lang_str_create ( void );
|
||||
lang_str_t *lang_str_copy ( const lang_str_t *ls );
|
||||
|
||||
/* Get elements */
|
||||
const char *lang_str_get ( lang_str_t *ls, const char *lang );
|
||||
lang_str_ele_t *lang_str_get2 ( lang_str_t *ls, const char *lang );
|
||||
|
||||
/* Add/Update elements */
|
||||
int lang_str_add
|
||||
( lang_str_t *ls, const char *str, const char *lang, int update );
|
||||
int lang_str_append
|
||||
( lang_str_t *ls, const char *str, const char *lang );
|
||||
|
||||
/* Serialize/Deserialize */
|
||||
void lang_str_serialize
|
||||
( lang_str_t *ls, htsmsg_t *msg, const char *f );
|
||||
lang_str_t *lang_str_deserialize
|
||||
( htsmsg_t *m, const char *f );
|
||||
|
||||
#endif /* __TVH_LANG_STR_H__ */
|
|
@ -718,6 +718,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
|||
const char *channel = http_arg_get(&hc->hc_req_args, "channel");
|
||||
const char *tag = http_arg_get(&hc->hc_req_args, "tag");
|
||||
const char *title = http_arg_get(&hc->hc_req_args, "title");
|
||||
const char *lang = http_arg_get(&hc->hc_args, "Accept-Language");
|
||||
|
||||
if(channel && !channel[0]) channel = NULL;
|
||||
if(tag && !tag[0]) tag = NULL;
|
||||
|
@ -740,7 +741,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
|||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
epg_query(&eqr, channel, tag, eg, title);
|
||||
epg_query(&eqr, channel, tag, eg, title, lang);
|
||||
|
||||
epg_query_sort(&eqr);
|
||||
|
||||
|
@ -763,17 +764,18 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
|||
if(ch->ch_icon != NULL)
|
||||
htsmsg_add_str(m, "chicon", ch->ch_icon);
|
||||
|
||||
if(ee->title != NULL)
|
||||
htsmsg_add_str(m, "title", ee->title);
|
||||
if(ee->subtitle)
|
||||
htsmsg_add_str(m, "subtitle", ee->subtitle);
|
||||
if((s = epg_episode_get_title(ee, lang)))
|
||||
htsmsg_add_str(m, "title", s);
|
||||
if((s = epg_episode_get_subtitle(ee, lang)))
|
||||
htsmsg_add_str(m, "subtitle", s);
|
||||
|
||||
if(ee->description != NULL)
|
||||
htsmsg_add_str(m, "description", ee->description);
|
||||
else if(ee->summary != NULL)
|
||||
htsmsg_add_str(m, "description", ee->summary);
|
||||
if((s = epg_episode_get_description(ee, lang)))
|
||||
htsmsg_add_str(m, "description", s);
|
||||
else if((s = epg_episode_get_summary(ee, lang)))
|
||||
htsmsg_add_str(m, "description", s);
|
||||
|
||||
if (epg_episode_number_format(ee, buf, 100, NULL, "Season %d", ".", "Episode %d", "/%d"))
|
||||
if (epg_episode_number_format(ee, buf, 100, NULL, "Season %d", ".",
|
||||
"Episode %d", "/%d"))
|
||||
htsmsg_add_str(m, "episode", buf);
|
||||
|
||||
htsmsg_add_u32(m, "id", e->id);
|
||||
|
@ -813,11 +815,12 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque)
|
|||
epg_episode_t *ee, *ee2;
|
||||
channel_t *ch;
|
||||
uint32_t count = 0;
|
||||
const char *id, *type;
|
||||
const char *s;
|
||||
char buf[100];
|
||||
|
||||
id = http_arg_get(&hc->hc_req_args, "id");
|
||||
type = http_arg_get(&hc->hc_req_args, "type");
|
||||
const char *lang = http_arg_get(&hc->hc_args, "Accept-Language");
|
||||
const char *id = http_arg_get(&hc->hc_req_args, "id");
|
||||
const char *type = http_arg_get(&hc->hc_req_args, "type");
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = htsmsg_create_list();
|
||||
|
@ -852,9 +855,12 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque)
|
|||
count++;
|
||||
m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "uri", ee2->uri);
|
||||
htsmsg_add_str(m, "title", ee2->title);
|
||||
if (ee2->subtitle) htsmsg_add_str(m, "subtitle", ee2->subtitle);
|
||||
if (epg_episode_number_format(ee2, buf, 100, NULL, "Season %d", ".", "Episode %d", "/%d"))
|
||||
if ((s = epg_episode_get_title(ee2, lang)))
|
||||
htsmsg_add_str(m, "title", s);
|
||||
if ((s = epg_episode_get_subtitle(ee2, lang)))
|
||||
htsmsg_add_str(m, "subtitle", s);
|
||||
if (epg_episode_number_format(ee2, buf, 100, NULL, "Season %d",
|
||||
".", "Episode %d", "/%d"))
|
||||
htsmsg_add_str(m, "episode", buf);
|
||||
htsmsg_add_msg(array, NULL, m);
|
||||
}
|
||||
|
@ -865,9 +871,12 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque)
|
|||
count++;
|
||||
m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "uri", ee2->uri);
|
||||
htsmsg_add_str(m, "title", ee2->title);
|
||||
if (ee2->subtitle) htsmsg_add_str(m, "subtitle", ee2->subtitle);
|
||||
if (epg_episode_number_format(ee2, buf, 100, NULL, "Season %d", ".", "Episode %d", "/%d"))
|
||||
if ((s = epg_episode_get_title(ee2, lang)))
|
||||
htsmsg_add_str(m, "title", s);
|
||||
if ((s = epg_episode_get_subtitle(ee2, lang)))
|
||||
htsmsg_add_str(m, "subtitle", s);
|
||||
if (epg_episode_number_format(ee2, buf, 100, NULL, "Season %d",
|
||||
".", "Episode %d", "/%d"))
|
||||
htsmsg_add_str(m, "episode", buf);
|
||||
htsmsg_add_msg(array, NULL, m);
|
||||
}
|
||||
|
@ -1235,10 +1244,10 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque)
|
|||
htsmsg_add_str(m, "config_name", de->de_config_name);
|
||||
|
||||
if(de->de_title != NULL)
|
||||
htsmsg_add_str(m, "title", de->de_title);
|
||||
htsmsg_add_str(m, "title", lang_str_get(de->de_title, NULL));
|
||||
|
||||
if(de->de_desc != NULL)
|
||||
htsmsg_add_str(m, "description", de->de_desc);
|
||||
htsmsg_add_str(m, "description", lang_str_get(de->de_desc, NULL));
|
||||
|
||||
if (de->de_bcast && de->de_bcast->episode)
|
||||
if (epg_episode_number_format(de->de_bcast->episode, buf, 100, NULL, "Season %d", ".", "Episode %d", "/%d"))
|
||||
|
@ -1753,6 +1762,8 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque)
|
|||
pthread_mutex_lock(&global_lock);
|
||||
if ((str = http_arg_get(&hc->hc_req_args, "muxconfpath")))
|
||||
save |= config_set_muxconfpath(str);
|
||||
if ((str = http_arg_get(&hc->hc_req_args, "language")))
|
||||
save |= config_set_language(str);
|
||||
if (save) config_save();
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
out = htsmsg_create_map();
|
||||
|
|
|
@ -67,6 +67,7 @@ page_simple(http_connection_t *hc,
|
|||
dvr_query_result_t dqr;
|
||||
const char *rstatus = NULL;
|
||||
epg_query_result_t eqr;
|
||||
const char *lang = http_arg_get(&hc->hc_args, "Accept-Language");
|
||||
|
||||
htsbuf_qprintf(hq, "<html>");
|
||||
htsbuf_qprintf(hq, "<body>");
|
||||
|
@ -86,7 +87,7 @@ page_simple(http_connection_t *hc,
|
|||
|
||||
if(s != NULL) {
|
||||
|
||||
epg_query(&eqr, NULL, NULL, NULL, s);
|
||||
epg_query(&eqr, NULL, NULL, NULL, s, lang);
|
||||
epg_query_sort(&eqr);
|
||||
|
||||
c = eqr.eqr_entries;
|
||||
|
@ -123,12 +124,13 @@ page_simple(http_connection_t *hc,
|
|||
rstatus = de != NULL ? val2str(de->de_sched_state,
|
||||
recstatustxt) : NULL;
|
||||
|
||||
s = epg_episode_get_title(e->episode, lang);
|
||||
htsbuf_qprintf(hq,
|
||||
"<a href=\"/eventinfo/%d\">"
|
||||
"%02d:%02d-%02d:%02d %s%s%s</a><br>",
|
||||
e->id,
|
||||
a.tm_hour, a.tm_min, b.tm_hour, b.tm_min,
|
||||
e->episode ? e->episode->title : "",
|
||||
s ?: "",
|
||||
rstatus ? " " : "", rstatus ?: "");
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +201,8 @@ page_einfo(http_connection_t *hc, const char *remain, void *opaque)
|
|||
dvr_entry_t *de;
|
||||
const char *rstatus;
|
||||
dvr_entry_sched_state_t dvr_status;
|
||||
const char *lang = http_arg_get(&hc->hc_args, "Accept-Language");
|
||||
const char *s;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
|
@ -227,8 +231,9 @@ page_einfo(http_connection_t *hc, const char *remain, void *opaque)
|
|||
days[a.tm_wday], a.tm_mday, a.tm_mon + 1,
|
||||
a.tm_hour, a.tm_min, b.tm_hour, b.tm_min);
|
||||
|
||||
s = epg_episode_get_title(e->episode, lang);
|
||||
htsbuf_qprintf(hq, "<hr><b>\"%s\": \"%s\"</b><br><br>",
|
||||
e->channel->ch_name, e->episode ? e->episode->title : "");
|
||||
e->channel->ch_name, s ?: "");
|
||||
|
||||
dvr_status = de != NULL ? de->de_sched_state : DVR_NOSTATE;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ tvheadend.miscconf = function() {
|
|||
var confreader = new Ext.data.JsonReader(
|
||||
{ root: 'config' },
|
||||
[
|
||||
'muxconfpath',
|
||||
'muxconfpath', 'language',
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -21,6 +21,12 @@ tvheadend.miscconf = function() {
|
|||
allowBlank : true,
|
||||
});
|
||||
|
||||
var language = new Ext.form.TextField({
|
||||
fieldLabel : 'Default Language(s)',
|
||||
name : 'language',
|
||||
allowBlank : true,
|
||||
});
|
||||
|
||||
/* ****************************************************************
|
||||
* Form
|
||||
* ***************************************************************/
|
||||
|
@ -53,7 +59,8 @@ tvheadend.miscconf = function() {
|
|||
defaultType : 'textfield',
|
||||
autoHeight : true,
|
||||
items : [
|
||||
dvbscanPath
|
||||
language,
|
||||
dvbscanPath,
|
||||
],
|
||||
tbar: [
|
||||
saveButton,
|
||||
|
|
|
@ -774,7 +774,7 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
|
|||
|
||||
if(de->de_title != NULL) {
|
||||
snprintf(disposition, sizeof(disposition),
|
||||
"attachment; filename=%s.%s", de->de_title, postfix);
|
||||
"attachment; filename=%s.%s", lang_str_get(de->de_title, NULL), postfix);
|
||||
i = 20;
|
||||
while(disposition[i]) {
|
||||
if(disposition[i] == ' ')
|
||||
|
|
Loading…
Add table
Reference in a new issue