dvr: add the filename charset conversion from UTF-8
This commit is contained in:
parent
d07071bf90
commit
eddc6ea844
6 changed files with 138 additions and 45 deletions
|
@ -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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue