dvr: add the filename charset conversion from UTF-8

This commit is contained in:
Jaroslav Kysela 2014-05-26 21:14:48 +02:00
parent d07071bf90
commit eddc6ea844
6 changed files with 138 additions and 45 deletions

View file

@ -95,6 +95,9 @@
See also <i>Directory permissions</i> in <i>Subdirectory Options</i>.
<dt>Filename charset
<dd>Character set for the created filename. Tvheadend will try to approximate characters to similarly looking ones.
<dt>Rewrite PAT in passthrough mode
<dd>Rewrite the original Program Association Table to only include the active service. When this option is disabled, Tvheadend will write the original PAT as broadcast, which lists all services from the original multiplex.

View file

@ -31,6 +31,8 @@ typedef struct dvr_config {
char *dvr_storage;
uint32_t dvr_retention_days;
int dvr_flags;
char *dvr_charset;
char *dvr_charset_id;
char *dvr_postproc;
int dvr_extra_time_pre;
int dvr_extra_time_post;
@ -330,6 +332,8 @@ void dvr_entry_dec_ref(dvr_entry_t *de);
void dvr_storage_set(dvr_config_t *cfg, const char *storage);
void dvr_charset_set(dvr_config_t *cfg, const char *charset);
void dvr_container_set(dvr_config_t *cfg, const char *container);
void dvr_file_permissions_set(dvr_config_t *cfg, int permissions);

View file

@ -29,6 +29,7 @@
#include "notify.h"
#include "htsp_server.h"
#include "streaming.h"
#include "intlconv.h"
static int de_tally;
@ -163,6 +164,22 @@ dvr_entry_notify(dvr_entry_t *de)
}
/**
*
*/
static void
dvr_charset_update(dvr_config_t *cfg, const char *charset)
{
const char *s, *id;
free(cfg->dvr_charset);
free(cfg->dvr_charset_id);
s = charset ? charset : "ASCII";
id = intlconv_charset_id(s, 1, 1);
cfg->dvr_charset = strdup(s);
cfg->dvr_charset_id = id ? strdup(id) : NULL;
}
/**
*
*/
@ -1190,7 +1207,9 @@ dvr_init(void)
if(!htsmsg_get_u32(m, "episode-before-date", &u32) && u32)
cfg->dvr_flags |= DVR_EPISODE_BEFORE_DATE;
tvh_str_set(&cfg->dvr_postproc, htsmsg_get_str(m, "postproc"));
dvr_charset_update(cfg, htsmsg_get_str(m, "charset"));
tvh_str_set(&cfg->dvr_postproc, htsmsg_get_str(m, "postproc"));
}
htsmsg_destroy(l);
@ -1245,6 +1264,8 @@ dvr_done(void)
pthread_mutex_lock(&global_lock);
while ((cfg = LIST_FIRST(&dvrconfigs)) != NULL) {
LIST_REMOVE(cfg, config_link);
free(cfg->dvr_charset_id);
free(cfg->dvr_charset);
free(cfg->dvr_storage);
free(cfg->dvr_config_name);
free(cfg);
@ -1314,6 +1335,7 @@ dvr_config_create(const char *name)
cfg->dvr_retention_days = 31;
cfg->dvr_mc = MC_MATROSKA;
cfg->dvr_flags = DVR_TAG_FILES | DVR_SKIP_COMMERCIALS;
dvr_charset_update(cfg, "ASCII");
/* series link support */
cfg->dvr_sl_brand_lock = 1; // use brand linking
@ -1359,6 +1381,9 @@ dvr_config_delete(const char *name)
cfg->dvr_config_name);
hts_settings_remove("dvr/config%s", cfg->dvr_config_name);
LIST_REMOVE(cfg, config_link);
free(cfg->dvr_charset_id);
free(cfg->dvr_charset);
free(cfg->dvr_storage);
free(cfg->dvr_config_name);
free(cfg);
@ -1409,7 +1434,9 @@ dvr_save(dvr_config_t *cfg)
htsmsg_add_u32(m, "skip-commercials", !!(cfg->dvr_flags & DVR_SKIP_COMMERCIALS));
htsmsg_add_u32(m, "subtitle-in-title", !!(cfg->dvr_flags & DVR_SUBTITLE_IN_TITLE));
htsmsg_add_u32(m, "episode-before-date", !!(cfg->dvr_flags & DVR_EPISODE_BEFORE_DATE));
if(cfg->dvr_postproc != NULL)
if (cfg->dvr_charset != NULL)
htsmsg_add_str(m, "charset", cfg->dvr_charset);
if (cfg->dvr_postproc != NULL)
htsmsg_add_str(m, "postproc", cfg->dvr_postproc);
hts_settings_save(m, "dvr/config%s", cfg->dvr_config_name);
@ -1431,6 +1458,19 @@ dvr_storage_set(dvr_config_t *cfg, const char *storage)
dvr_save(cfg);
}
/**
*
*/
void
dvr_charset_set(dvr_config_t *cfg, const char *charset)
{
if(cfg->dvr_charset != NULL && !strcmp(cfg->dvr_charset, charset))
return;
dvr_charset_update(cfg, charset);
dvr_save(cfg);
}
/**
*
*/

View file

@ -34,6 +34,7 @@
#include "plumbing/globalheaders.h"
#include "htsp_server.h"
#include "atomic.h"
#include "intlconv.h"
#include "muxer.h"
@ -122,11 +123,27 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
/**
* Replace various chars with a dash
*/
static void
cleanupfilename(char *s, int dvr_flags)
static char *
cleanup_filename(char *s, dvr_config_t *cfg)
{
int i, len = strlen(s);
for(i = 0; i < len; i++) {
int i, len = strlen(s), dvr_flags = cfg->dvr_flags;
char *s1;
s1 = intlconv_utf8safestr(cfg->dvr_charset_id, s, len * 2);
if (s1 == NULL) {
tvherror("dvr", "Unsupported charset %s using ASCII", cfg->dvr_charset);
s1 = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
s, len * 2);
if (s1 == NULL)
return NULL;
}
s = s1;
/* Do not create hidden files */
if (s[0] == '.')
s[0] = '-';
for (i = 0, len = strlen(s); i < len; i++) {
if(s[i] == '/')
s[i] = '-';
@ -140,6 +157,8 @@ cleanupfilename(char *s, int dvr_flags)
(strchr("/:\\<>|*?'\"", s[i]) != NULL)))
s[i] = '-';
}
return s;
}
/**
@ -152,65 +171,64 @@ cleanupfilename(char *s, int dvr_flags)
static int
pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
{
char fullname[1000];
char path[500];
char fullname[PATH_MAX];
char path[PATH_MAX];
int tally = 0;
struct stat st;
char filename[1000];
char *filename, *s;
struct tm tm;
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
dvr_make_title(filename, sizeof(filename), de);
cleanupfilename(filename,cfg->dvr_flags);
snprintf(path, sizeof(path), "%s", cfg->dvr_storage);
strncpy(path, cfg->dvr_storage, sizeof(path));
path[sizeof(path)-1] = '\0';
/* Remove trailing slash */
if (path[strlen(path)-1] == '/')
path[strlen(path)-1] = '\0';
/* Append per-day directory */
if(cfg->dvr_flags & DVR_DIR_PER_DAY) {
if (cfg->dvr_flags & DVR_DIR_PER_DAY) {
localtime_r(&de->de_start, &tm);
strftime(fullname, sizeof(fullname), "%F", &tm);
cleanupfilename(fullname,cfg->dvr_flags);
snprintf(path + strlen(path), sizeof(path) - strlen(path),
"/%s", fullname);
s = cleanup_filename(fullname, cfg);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
/* Append per-channel directory */
if(cfg->dvr_flags & DVR_DIR_PER_CHANNEL) {
if (cfg->dvr_flags & DVR_DIR_PER_CHANNEL) {
char *chname = strdup(DVR_CH_NAME(de));
cleanupfilename(chname,cfg->dvr_flags);
snprintf(path + strlen(path), sizeof(path) - strlen(path),
"/%s", chname);
s = cleanup_filename(chname, cfg);
free(chname);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
// TODO: per-brand, per-season
/* Append per-title directory */
if(cfg->dvr_flags & DVR_DIR_PER_TITLE) {
if (cfg->dvr_flags & DVR_DIR_PER_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);
s = cleanup_filename(title, cfg);
free(title);
if (s == NULL)
return -1;
snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s);
free(s);
}
if(makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0) {
if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0)
return -1;
}
/* Construct final name */
dvr_make_title(fullname, sizeof(fullname), de);
filename = cleanup_filename(fullname, cfg);
if (filename == NULL)
return -1;
snprintf(fullname, sizeof(fullname), "%s/%s.%s",
path, filename, muxer_suffix(de->de_mux, ss));
@ -229,6 +247,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
snprintf(fullname, sizeof(fullname), "%s/%s-%d.%s",
path, filename, tally, muxer_suffix(de->de_mux, ss));
}
free(filename);
tvh_str_set(&de->de_filename, fullname);

View file

@ -1127,6 +1127,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
r = htsmsg_create_map();
htsmsg_add_str(r, "storage", cfg->dvr_storage);
htsmsg_add_str(r, "charset", cfg->dvr_charset);
htsmsg_add_str(r, "container", muxer_container_type2txt(cfg->dvr_mc));
/* Convert integer permissions to an octal-format 0xxx string and store it in the config file */
@ -1174,7 +1175,10 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
if((s = http_arg_get(&hc->hc_req_args, "storage")) != NULL)
dvr_storage_set(cfg,s);
if((s = http_arg_get(&hc->hc_req_args, "container")) != NULL)
if((s = http_arg_get(&hc->hc_req_args, "charset")) != NULL)
dvr_charset_set(cfg,s);
if((s = http_arg_get(&hc->hc_req_args, "container")) != NULL)
dvr_container_set(cfg,s);
/*
@ -1182,13 +1186,13 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
* Note no checking that strtol won't overflow int - this should never happen with three-digit numbers
*/
if((s = http_arg_get(&hc->hc_req_args, "filePermissions")) != NULL)
if((s = http_arg_get(&hc->hc_req_args, "filePermissions")) != NULL)
dvr_file_permissions_set(cfg,(int)strtol(s,NULL,0));
if((s = http_arg_get(&hc->hc_req_args, "dirPermissions")) != NULL)
if((s = http_arg_get(&hc->hc_req_args, "dirPermissions")) != NULL)
dvr_directory_permissions_set(cfg,(int)strtol(s,NULL,0));
if((s = http_arg_get(&hc->hc_req_args, "cache")) != NULL)
if((s = http_arg_get(&hc->hc_req_args, "cache")) != NULL)
dvr_mux_cache_set(cfg,atoi(s));
if((s = http_arg_get(&hc->hc_req_args, "postproc")) != NULL)
@ -1197,11 +1201,11 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
if((s = http_arg_get(&hc->hc_req_args, "retention")) != NULL)
dvr_retention_set(cfg,atoi(s));
if((s = http_arg_get(&hc->hc_req_args, "preExtraTime")) != NULL)
dvr_extra_time_pre_set(cfg,atoi(s));
if((s = http_arg_get(&hc->hc_req_args, "preExtraTime")) != NULL)
dvr_extra_time_pre_set(cfg,atoi(s));
if((s = http_arg_get(&hc->hc_req_args, "postExtraTime")) != NULL)
dvr_extra_time_post_set(cfg,atoi(s));
if((s = http_arg_get(&hc->hc_req_args, "postExtraTime")) != NULL)
dvr_extra_time_post_set(cfg,atoi(s));
if(http_arg_get(&hc->hc_req_args, "dayDirs") != NULL)
flags |= DVR_DIR_PER_DAY;

View file

@ -27,6 +27,18 @@ tvheadend.containers = new Ext.data.JsonStore({
});
//For the cache configuration
tvheadend.charsets = new Ext.data.JsonStore({
autoLoad: true,
root: 'entries',
fields: ['key', 'val'],
id: 'key',
url: 'api/intlconv/charsets',
baseParams: {
enum: 1
}
});
//For the charset configuration
tvheadend.caches = new Ext.data.JsonStore({
autoLoad: true,
root: 'entries',
@ -784,7 +796,7 @@ tvheadend.dvrsettings = function() {
var confreader = new Ext.data.JsonReader({
root: 'dvrSettings'
}, ['storage', 'filePermissions', 'dirPermissions', 'postproc', 'retention', 'dayDirs', 'channelDirs',
'channelInTitle', 'container', 'cache', 'dateInTitle', 'timeInTitle',
'channelInTitle', 'container', 'cache', 'charset', 'dateInTitle', 'timeInTitle',
'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs',
'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle',
'episodeBeforeDate', 'rewritePAT', 'rewritePMT']);
@ -880,6 +892,17 @@ tvheadend.dvrsettings = function() {
name: 'filePermissions'
});
var charset = new Ext.form.ComboBox({
store: tvheadend.charsets,
fieldLabel: 'Filename charset',
triggerAction: 'all',
displayField: 'val',
valueField: 'key',
editable: false,
width: 200,
hiddenName: 'charset'
});
/* TO DO - Add 'override user umask?' option, then trigger fchmod in mkmux.c, muxer_pass.c after file created */
var PATrewrite = new Ext.form.Checkbox({
@ -992,7 +1015,7 @@ tvheadend.dvrsettings = function() {
autoHeight: true,
collapsible: true,
animCollapse: true,
items: [recordingPath, recordingPermissions, PATrewrite, PMTrewrite, tagMetadata, skipCommercials]
items: [recordingPath, recordingPermissions, charset, PATrewrite, PMTrewrite, tagMetadata, skipCommercials]
});
/* Sub-Panel - Directory operations */