Make root directory, log retention time and DVR titles/filenames configurable.

This commit is contained in:
Andreas Öman 2008-09-21 09:37:35 +00:00
parent 026a7bbc2c
commit fb33332ebd
5 changed files with 227 additions and 30 deletions

View file

@ -28,6 +28,13 @@ extern char *dvr_storage;
extern char *dvr_format;
extern char *dvr_file_postfix;
extern uint32_t dvr_retention_days;
extern int dvr_flags;
#define DVR_DIR_PER_DAY 0x1
#define DVR_DIR_PER_CHANNEL 0x2
#define DVR_CHANNEL_IN_TITLE 0x4
#define DVR_DATE_IN_TITLE 0x8
#define DVR_TIME_IN_TITLE 0x10
LIST_HEAD(dvr_rec_stream_list, dvr_rec_stream);
@ -71,6 +78,8 @@ typedef struct dvr_entry {
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) */
char *de_error;
@ -131,6 +140,8 @@ void dvr_storage_set(const char *storage);
void dvr_retention_set(int days);
void dvr_flags_set(int flags);
/**
* Query interface
*/

View file

@ -32,6 +32,7 @@ char *dvr_storage;
char *dvr_format;
char *dvr_file_postfix;
uint32_t dvr_retention_days;
int dvr_flags;
static int de_tally;
@ -58,6 +59,39 @@ dvrdb_changed(void)
}
/**
*
*/
static void
dvr_make_title(char *output, size_t outlen, const char *title,
const char *channel, time_t starttime)
{
struct tm tm;
char buf[40];
if(dvr_flags & DVR_CHANNEL_IN_TITLE)
snprintf(output, outlen, "%s-", channel);
else
output[0] = 0;
snprintf(output + strlen(output), outlen - strlen(output),
"%s", title);
localtime_r(&starttime, &tm);
if(dvr_flags & DVR_DATE_IN_TITLE) {
strftime(buf, sizeof(buf), "%F", &tm);
snprintf(output + strlen(output), outlen - strlen(output), "-%s", buf);
}
if(dvr_flags & DVR_TIME_IN_TITLE) {
strftime(buf, sizeof(buf), "%R", &tm);
snprintf(output + strlen(output), outlen - strlen(output), "-%s", buf);
}
}
/**
*
*/
@ -65,6 +99,12 @@ static void
dvr_entry_link(dvr_entry_t *de)
{
time_t now, preamble;
char buf[100];
dvr_make_title(buf, sizeof(buf), de->de_title, de->de_channel->ch_name,
de->de_start);
de->de_ititle = strdup(buf);
de->de_refcnt = 1;
@ -87,6 +127,7 @@ dvr_entry_link(dvr_entry_t *de)
}
/**
*
*/
@ -146,6 +187,7 @@ dvr_entry_dec_ref(dvr_entry_t *de)
free(de->de_creator);
free(de->de_title);
free(de->de_ititle);
free(de->de_desc);
free(de);
@ -393,6 +435,7 @@ dvr_init(void)
char buf[500];
const char *homedir;
struct stat st;
uint32_t u32;
/* Default settings */
@ -406,6 +449,21 @@ dvr_init(void)
htsmsg_get_u32(m, "retention-days", &dvr_retention_days);
tvh_str_set(&dvr_storage, htsmsg_get_str(m, "storage"));
if(!htsmsg_get_u32(m, "day-dir", &u32) && u32)
dvr_flags |= DVR_DIR_PER_DAY;
if(!htsmsg_get_u32(m, "channel-dir", &u32) && u32)
dvr_flags |= DVR_DIR_PER_CHANNEL;
if(!htsmsg_get_u32(m, "channel-in-title", &u32) && u32)
dvr_flags |= DVR_CHANNEL_IN_TITLE;
if(!htsmsg_get_u32(m, "date-in-title", &u32) && u32)
dvr_flags |= DVR_DATE_IN_TITLE;
if(!htsmsg_get_u32(m, "time-in-title", &u32) && u32)
dvr_flags |= DVR_TIME_IN_TITLE;
htsmsg_destroy(m);
}
@ -447,6 +505,11 @@ dvr_save(void)
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "storage", dvr_storage);
htsmsg_add_u32(m, "retention-days", dvr_retention_days);
htsmsg_add_u32(m, "day-dir", !!(dvr_flags & DVR_DIR_PER_DAY));
htsmsg_add_u32(m, "channel-dir", !!(dvr_flags & DVR_DIR_PER_CHANNEL));
htsmsg_add_u32(m, "channel-in-title", !!(dvr_flags & DVR_CHANNEL_IN_TITLE));
htsmsg_add_u32(m, "date-in-title", !!(dvr_flags & DVR_DATE_IN_TITLE));
htsmsg_add_u32(m, "time-in-title", !!(dvr_flags & DVR_TIME_IN_TITLE));
hts_settings_save(m, "dvr");
htsmsg_destroy(m);
@ -488,6 +551,20 @@ dvr_retention_set(int days)
}
/**
*
*/
void
dvr_flags_set(int flags)
{
if(dvr_flags == flags)
return;
dvr_flags = flags;
dvr_save();
}
/**
*
*/

View file

@ -135,6 +135,53 @@ dvr_rec_unsubscribe(dvr_entry_t *de)
}
/**
*
*/
static int
makedirs(const char *path)
{
struct stat st;
char *p;
int l, r;
if(stat(path, &st) == 0 && S_ISDIR(st.st_mode))
return 0; /* Dir already there */
if(mkdir(path, 0777) == 0)
return 0; /* Dir created ok */
if(errno == ENOENT) {
/* Parent does not exist, try to create it */
/* Allocate new path buffer and strip off last directory component */
l = strlen(path);
p = alloca(l + 1);
memcpy(p, path, l);
p[l--] = 0;
for(; l >= 0; l--)
if(p[l] == '/')
break;
if(l == 0) {
return ENOENT;
}
p[l] = 0;
if((r = makedirs(p)) != 0)
return r;
/* Try again */
if(mkdir(path, 0777) == 0)
return 0; /* Dir created ok */
}
r = errno;
tvhlog(LOG_DEBUG, "dvr", "Unable to create directory \"%s\" -- %s",
path, strerror(r));
return r;
}
/**
@ -155,28 +202,52 @@ deslashify(char *s)
* - avoid duplicate filenames
*
*/
static void
static int
pvr_generate_filename(dvr_entry_t *de)
{
char fullname[1000];
char path[500];
int tally = 0;
struct stat st;
char *chname;
char *filename;
char *chname;
struct tm tm;
if(de->de_filename != NULL) {
free(de->de_filename);
de->de_filename = NULL;
}
filename = strdup(de->de_title);
filename = strdup(de->de_ititle);
deslashify(filename);
chname = strdup(de->de_channel->ch_name);
deslashify(chname);
av_strlcpy(path, dvr_storage, sizeof(path));
snprintf(fullname, sizeof(fullname), "%s/%s-%s.%s",
dvr_storage, chname, filename, dvr_file_postfix);
/* Append per-day directory */
if(dvr_flags & DVR_DIR_PER_DAY) {
localtime_r(&de->de_start, &tm);
strftime(fullname, sizeof(fullname), "%F", &tm);
deslashify(fullname);
snprintf(path + strlen(path), sizeof(path) - strlen(path),
"/%s", fullname);
}
/* Append per-channel directory */
if(dvr_flags & DVR_DIR_PER_CHANNEL) {
chname = strdup(de->de_channel->ch_name);
deslashify(chname);
snprintf(path + strlen(path), sizeof(path) - strlen(path),
"/%s", chname);
free(chname);
}
/* */
if(makedirs(path) != 0)
return -1;
/* Construct final name */
snprintf(fullname, sizeof(fullname), "%s/%s.%s",
path, filename, dvr_file_postfix);
while(1) {
if(stat(fullname, &st) == -1) {
@ -190,14 +261,14 @@ pvr_generate_filename(dvr_entry_t *de)
tally++;
snprintf(fullname, sizeof(fullname), "%s/%s-%s-%d.%s",
dvr_storage, chname, filename, tally,dvr_file_postfix);
snprintf(fullname, sizeof(fullname), "%s/%s-%d.%s",
path, filename, tally, dvr_file_postfix);
}
de->de_filename = strdup(fullname);
tvh_str_set(&de->de_filename, fullname);
free(filename);
free(chname);
return 0;
}
/**
@ -230,7 +301,10 @@ dvr_rec_start(dvr_entry_t *de, streaming_pad_t *sp)
pthread_t ptid;
pthread_attr_t attr;
pvr_generate_filename(de);
if(pvr_generate_filename(de) != 0) {
dvr_rec_fatal_error(de, "Unable to create directories");
return;
}
/* Find lavf format */
@ -245,7 +319,7 @@ dvr_rec_start(dvr_entry_t *de, streaming_pad_t *sp)
fctx = av_alloc_format_context();
av_strlcpy(fctx->title, de->de_title ?: "",
av_strlcpy(fctx->title, de->de_ititle ?: "",
sizeof(fctx->title));
av_strlcpy(fctx->comment, de->de_desc ?: "",
@ -309,7 +383,7 @@ dvr_rec_start(dvr_entry_t *de, streaming_pad_t *sp)
if(codec == NULL) {
tvhlog(LOG_ERR, "dvr",
"%s - Cannot find codec for %s, ignoring stream",
de->de_title, codec_name);
de->de_ititle, codec_name);
continue;
}
@ -325,7 +399,7 @@ dvr_rec_start(dvr_entry_t *de, streaming_pad_t *sp)
if(avcodec_open(ctx, codec) < 0) {
tvhlog(LOG_ERR, "dvr",
"%s - Cannot open codec for %s, ignoring stream",
de->de_title, codec_name);
de->de_ititle, codec_name);
free(ctx);
continue;
}
@ -503,7 +577,7 @@ dvr_thread_new_pkt(dvr_entry_t *de, th_pkt_t *pkt)
tvhlog(LOG_DEBUG, "dvr", "%s - "
"Stream #%d: \"%s\" decoded a complete audio frame: "
"%d channels in %d Hz",
de->de_title, st->index, ctx->codec->name,
de->de_ititle, st->index, ctx->codec->name,
ctx->channels, ctx->sample_rate);
drs->drs_decoded = 1;
@ -522,7 +596,7 @@ dvr_thread_new_pkt(dvr_entry_t *de, th_pkt_t *pkt)
tvhlog(LOG_DEBUG, "dvr", "%s - "
"Stream #%d: \"%s\" decoded a complete video frame: "
"%d x %d at %.2fHz",
de->de_title, st->index, ctx->codec->name,
de->de_ititle, st->index, ctx->codec->name,
ctx->width, st->codec->height,
(float)ctx->time_base.den / (float)ctx->time_base.num);
@ -541,7 +615,7 @@ dvr_thread_new_pkt(dvr_entry_t *de, th_pkt_t *pkt)
if(av_write_header(fctx)) {
tvhlog(LOG_ERR, "dvr",
"%s - Unable to write header",
de->de_title);
de->de_ititle);
break;
}
@ -549,7 +623,7 @@ dvr_thread_new_pkt(dvr_entry_t *de, th_pkt_t *pkt)
tvhlog(LOG_ERR, "dvr",
"%s - Header written to file, stream dump:",
de->de_title);
de->de_ititle);
for(i = 0; i < fctx->nb_streams; i++) {
stx = fctx->streams[i];
@ -557,7 +631,7 @@ dvr_thread_new_pkt(dvr_entry_t *de, th_pkt_t *pkt)
avcodec_string(txt, sizeof(txt), stx->codec, 1);
tvhlog(LOG_ERR, "dvr", "%s - Stream #%d: %s [%d/%d]",
de->de_title, i, txt,
de->de_ititle, i, txt,
stx->time_base.num, stx->time_base.den);
}
@ -610,7 +684,7 @@ dvr_thread_new_pkt(dvr_entry_t *de, th_pkt_t *pkt)
de->de_rec_state = DE_RS_RUNNING;
tvhlog(LOG_ERR, "dvr", "%s - Skipped %lld seconds of commercials",
de->de_title, (pkt->pkt_dts - de->de_ts_com_start) / 1000000);
de->de_ititle, (pkt->pkt_dts - de->de_ts_com_start) / 1000000);
goto outputpacket;
}
break;

View file

@ -891,6 +891,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
event_t *e;
dvr_entry_t *de;
const char *s;
int flags = 0;
if(op == NULL)
op = "loadSettings";
@ -927,6 +928,12 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
r = htsmsg_create();
htsmsg_add_str(r, "storage", dvr_storage);
htsmsg_add_u32(r, "retention", dvr_retention_days);
htsmsg_add_u32(r, "dayDirs", !!(dvr_flags & DVR_DIR_PER_DAY));
htsmsg_add_u32(r, "channelDirs", !!(dvr_flags & DVR_DIR_PER_CHANNEL));
htsmsg_add_u32(r, "channelInTitle", !!(dvr_flags & DVR_CHANNEL_IN_TITLE));
htsmsg_add_u32(r, "dateInTitle", !!(dvr_flags & DVR_DATE_IN_TITLE));
htsmsg_add_u32(r, "timeInTitle", !!(dvr_flags & DVR_TIME_IN_TITLE));
out = json_single_record(r, "dvrSettings");
} else if(!strcmp(op, "saveSettings")) {
@ -937,6 +944,18 @@ 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(atoi(s));
if(http_arg_get(&hc->hc_req_args, "dayDirs") != NULL)
flags |= DVR_DIR_PER_DAY;
if(http_arg_get(&hc->hc_req_args, "channelDirs") != NULL)
flags |= DVR_DIR_PER_CHANNEL;
if(http_arg_get(&hc->hc_req_args, "channelInTitle") != NULL)
flags |= DVR_CHANNEL_IN_TITLE;
if(http_arg_get(&hc->hc_req_args, "dateInTitle") != NULL)
flags |= DVR_DATE_IN_TITLE;
if(http_arg_get(&hc->hc_req_args, "timeInTitle") != NULL)
flags |= DVR_TIME_IN_TITLE;
dvr_flags_set(flags);
out = htsmsg_create();
htsmsg_add_u32(out, "success", 1);

View file

@ -197,8 +197,9 @@ tvheadend.dvrsettings = function() {
var confreader = new Ext.data.JsonReader({
root: 'dvrSettings',
}, ['storage','retention']);
}, ['storage','retention','dayDirs',
'channelDirs','channelInTitle',
'dateInTitle','timeInTitle']);
var confpanel = new Ext.FormPanel({
title:'Digital Video Recorder',
@ -206,7 +207,7 @@ tvheadend.dvrsettings = function() {
bodyStyle:'padding:15px',
anchor: '100% 50%',
labelAlign: 'right',
labelWidth: 150,
labelWidth: 200,
waitMsgTarget: true,
reader: confreader,
defaultType: 'textfield',
@ -219,8 +220,23 @@ tvheadend.dvrsettings = function() {
allowNegative: false,
allowDecimals: false,
minValue: 1,
fieldLabel: 'Retention time (days)',
fieldLabel: 'DVR Log retention time (days)',
name: 'retention'
}), new Ext.form.Checkbox({
fieldLabel: 'Make subdirectories per day',
name: 'dayDirs'
}), new Ext.form.Checkbox({
fieldLabel: 'Make subdirectories per channel',
name: 'channelDirs'
}), new Ext.form.Checkbox({
fieldLabel: 'Include channel name in title',
name: 'channelInTitle'
}), new Ext.form.Checkbox({
fieldLabel: 'Include date in title',
name: 'dateInTitle'
}), new Ext.form.Checkbox({
fieldLabel: 'Include time in title',
name: 'timeInTitle'
})],
tbar: [{
tooltip: 'Save changes made to channel configuration below',