* Add support for storing recorded events in a directory named after

the event. Ticket #150

  * Add support for appending season and episode numbers to filenames
    during recording.
This commit is contained in:
Andreas Öman 2010-03-16 22:59:10 +00:00
parent 8e5d0c68a8
commit 6876fc6afe
6 changed files with 114 additions and 20 deletions

6
debian/changelog vendored
View file

@ -23,6 +23,12 @@ hts-tvheadend (2.11-WIP) hts; urgency=low
or when a new auto recording was created from the EPG view). This
is now fixed.
* Add support for storing recorded events in a directory named after
the event. Ticket #150
* Add support for appending season and episode numbers to filenames
during recording.
hts-tvheadend (2.10) hts; urgency=high
* Fix a crash in HTSP server.

View file

@ -41,6 +41,8 @@ extern struct dvr_entry_list dvrentries;
#define DVR_DATE_IN_TITLE 0x8
#define DVR_TIME_IN_TITLE 0x10
#define DVR_WHITESPACE_IN_TITLE 0x20
#define DVR_DIR_PER_TITLE 0x40
#define DVR_EPISODE_IN_TITLE 0x80
LIST_HEAD(dvr_rec_stream_list, dvr_rec_stream);
@ -91,6 +93,8 @@ typedef struct dvr_entry {
date and time pre/post/fixed */
char *de_desc; /* Description in UTF-8 (from EPG) */
epg_episode_t de_episode;
char *de_error;
dvr_entry_sched_state_t de_sched_state;
@ -176,7 +180,8 @@ dvr_entry_t *dvr_entry_create_by_event(event_t *e, const char *creator,
dvr_entry_t *dvr_entry_create(channel_t *ch, time_t start, time_t stop,
const char *title, const char *description,
const char *creator, dvr_autorec_entry_t *dae);
const char *creator, dvr_autorec_entry_t *dae,
epg_episode_t *ee);
void dvr_init(void);

View file

@ -67,21 +67,20 @@ dvrdb_changed(void)
*
*/
static void
dvr_make_title(char *output, size_t outlen, const char *title,
const char *channel, time_t starttime)
dvr_make_title(char *output, size_t outlen, dvr_entry_t *de)
{
struct tm tm;
char buf[40];
if(dvr_flags & DVR_CHANNEL_IN_TITLE)
snprintf(output, outlen, "%s-", channel);
snprintf(output, outlen, "%s-", de->de_channel->ch_name);
else
output[0] = 0;
snprintf(output + strlen(output), outlen - strlen(output),
"%s", title);
"%s", de->de_title);
localtime_r(&starttime, &tm);
localtime_r(&de->de_start, &tm);
if(dvr_flags & DVR_DATE_IN_TITLE) {
strftime(buf, sizeof(buf), "%F", &tm);
@ -92,6 +91,19 @@ dvr_make_title(char *output, size_t outlen, const char *title,
strftime(buf, sizeof(buf), "%H-%M", &tm);
snprintf(output + strlen(output), outlen - strlen(output), "-%s", buf);
}
if(dvr_flags & DVR_EPISODE_IN_TITLE) {
if(de->de_episode.ee_season && de->de_episode.ee_episode)
snprintf(output + strlen(output), outlen - strlen(output),
".S%02dE%02d",
de->de_episode.ee_season, de->de_episode.ee_episode);
else if(de->de_episode.ee_episode)
snprintf(output + strlen(output), outlen - strlen(output),
".E%02d",
de->de_episode.ee_episode);
}
}
@ -104,8 +116,7 @@ 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);
dvr_make_title(buf, sizeof(buf), de);
de->de_ititle = strdup(buf);
@ -137,7 +148,8 @@ dvr_entry_link(dvr_entry_t *de)
dvr_entry_t *
dvr_entry_create(channel_t *ch, time_t start, time_t stop,
const char *title, const char *description,
const char *creator, dvr_autorec_entry_t *dae)
const char *creator, dvr_autorec_entry_t *dae,
epg_episode_t *ee)
{
dvr_entry_t *de;
char tbuf[30];
@ -168,14 +180,23 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop,
de->de_title = strdup(title);
de->de_desc = description ? strdup(description) : NULL;
if(ee != NULL) {
de->de_episode.ee_season = ee->ee_season;
de->de_episode.ee_episode = ee->ee_episode;
de->de_episode.ee_part = ee->ee_part;
tvh_str_set(&de->de_episode.ee_onscreen, ee->ee_onscreen);
}
dvr_entry_link(de);
t = de->de_start - de->de_start_extra * 60;
localtime_r(&t, &tm);
strftime(tbuf, sizeof(tbuf), "%c", &tm);
de->de_autorec = dae;
LIST_INSERT_HEAD(&dae->dae_spawns, de, de_autorec_link);
if(dae != NULL) {
de->de_autorec = dae;
LIST_INSERT_HEAD(&dae->dae_spawns, de, de_autorec_link);
}
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" starting at %s, "
"scheduled for recording by \"%s\"",
@ -198,7 +219,7 @@ dvr_entry_create_by_event(event_t *e, const char *creator,
return NULL;
return dvr_entry_create(e->e_channel, e->e_start, e->e_stop,
e->e_title, e->e_desc, creator, dae);
e->e_title, e->e_desc, creator, dae, &e->e_episode);
}
@ -236,6 +257,8 @@ dvr_entry_dec_ref(dvr_entry_t *de)
free(de->de_ititle);
free(de->de_desc);
free(de->de_episode.ee_onscreen);
free(de);
}
@ -332,6 +355,15 @@ dvr_db_load_one(htsmsg_t *c, int id)
}
}
if(!htsmsg_get_s32(c, "season", &d))
de->de_episode.ee_season = d;
if(!htsmsg_get_s32(c, "episode", &d))
de->de_episode.ee_episode = d;
if(!htsmsg_get_s32(c, "part", &d))
de->de_episode.ee_part = d;
tvh_str_set(&de->de_episode.ee_onscreen, htsmsg_get_str(c, "episodename"));
dvr_entry_link(de);
}
@ -393,6 +425,15 @@ dvr_entry_save(dvr_entry_t *de)
if(de->de_autorec != NULL)
htsmsg_add_str(m, "autorec", de->de_autorec->dae_id);
if(de->de_episode.ee_season)
htsmsg_add_u32(m, "season", de->de_episode.ee_season);
if(de->de_episode.ee_episode)
htsmsg_add_u32(m, "episode", de->de_episode.ee_episode);
if(de->de_episode.ee_part)
htsmsg_add_u32(m, "part", de->de_episode.ee_part);
if(de->de_episode.ee_onscreen)
htsmsg_add_str(m, "episodename", de->de_episode.ee_onscreen);
hts_settings_save(m, "dvr/log/%d", de->de_id);
htsmsg_destroy(m);
}
@ -594,6 +635,12 @@ dvr_init(void)
if(!htsmsg_get_u32(m, "whitespace-in-title", &u32) && u32)
dvr_flags |= DVR_WHITESPACE_IN_TITLE;
if(!htsmsg_get_u32(m, "title-dir", &u32) && u32)
dvr_flags |= DVR_DIR_PER_TITLE;
if(!htsmsg_get_u32(m, "episode-in-title", &u32) && u32)
dvr_flags |= DVR_EPISODE_IN_TITLE;
tvh_str_set(&dvr_postproc, htsmsg_get_str(m, "postproc"));
@ -645,6 +692,8 @@ dvr_save(void)
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));
htsmsg_add_u32(m, "whitespace-in-title", !!(dvr_flags & DVR_WHITESPACE_IN_TITLE));
htsmsg_add_u32(m, "title-dir", !!(dvr_flags & DVR_DIR_PER_TITLE));
htsmsg_add_u32(m, "episode-in-title", !!(dvr_flags & DVR_EPISODE_IN_TITLE));
if(dvr_postproc != NULL)
htsmsg_add_str(m, "postproc", dvr_postproc);

View file

@ -170,7 +170,6 @@ pvr_generate_filename(dvr_entry_t *de)
int tally = 0;
struct stat st;
char *filename;
char *chname;
struct tm tm;
filename = strdup(de->de_ititle);
@ -192,13 +191,25 @@ pvr_generate_filename(dvr_entry_t *de)
if(dvr_flags & DVR_DIR_PER_CHANNEL) {
chname = strdup(de->de_channel->ch_name);
char *chname = strdup(de->de_channel->ch_name);
cleanupfilename(chname);
snprintf(path + strlen(path), sizeof(path) - strlen(path),
"/%s", chname);
free(chname);
}
/* Append per-title directory */
if(dvr_flags & DVR_DIR_PER_TITLE) {
char *title = strdup(de->de_title);
cleanupfilename(title);
snprintf(path + strlen(path), sizeof(path) - strlen(path),
"/%s", title);
free(title);
}
/* */
if(makedirs(path) != 0)
return -1;

View file

@ -780,7 +780,8 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
if(stop < start)
stop += 86400;
dvr_entry_create(ch, start, stop, title, NULL, hc->hc_representative, NULL);
dvr_entry_create(ch, start, stop, title, NULL, hc->hc_representative,
NULL, NULL);
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
@ -810,7 +811,9 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
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));
htsmsg_add_u32(r, "whitespaceInTitle", !!(dvr_flags & DVR_WHITESPACE_IN_TITLE));
htsmsg_add_u32(r, "whitespaceInTitle", !!(dvr_flags & DVR_WHITESPACE_IN_TITLE));
htsmsg_add_u32(r, "titleDirs", !!(dvr_flags & DVR_DIR_PER_TITLE));
htsmsg_add_u32(r, "episodeInTitle", !!(dvr_flags & DVR_EPISODE_IN_TITLE));
out = json_single_record(r, "dvrSettings");
@ -843,6 +846,10 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
flags |= DVR_TIME_IN_TITLE;
if(http_arg_get(&hc->hc_req_args, "whitespaceInTitle") != NULL)
flags |= DVR_WHITESPACE_IN_TITLE;
if(http_arg_get(&hc->hc_req_args, "titleDirs") != NULL)
flags |= DVR_DIR_PER_TITLE;
if(http_arg_get(&hc->hc_req_args, "episodeInTitle") != NULL)
flags |= DVR_EPISODE_IN_TITLE;
dvr_flags_set(flags);
@ -924,6 +931,9 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque)
if(de->de_desc != NULL)
htsmsg_add_str(m, "description", de->de_desc);
if(de->de_episode.ee_onscreen)
htsmsg_add_str(m, "episode", de->de_episode.ee_onscreen);
htsmsg_add_u32(m, "id", de->de_id);
htsmsg_add_u32(m, "start", de->de_start);
htsmsg_add_u32(m, "end", de->de_stop);

View file

@ -116,6 +116,11 @@ tvheadend.dvrschedule = function() {
id:'title',
header: "Title",
dataIndex: 'title'
},{
width: 250,
id:'episode',
header: "Episode",
dataIndex: 'episode'
},{
width: 100,
id:'start',
@ -388,6 +393,7 @@ tvheadend.dvr = function() {
{name: 'id'},
{name: 'channel'},
{name: 'title'},
{name: 'episode'},
{name: 'description'},
{name: 'chicon'},
{name: 'start', type: 'date', dateFormat: 'U' /* unix time */},
@ -456,7 +462,8 @@ tvheadend.dvrsettings = function() {
}, ['storage','postproc','retention','dayDirs',
'channelDirs','channelInTitle',
'dateInTitle','timeInTitle',
'preExtraTime', 'postExtraTime', 'whitespaceInTitle']);
'preExtraTime', 'postExtraTime', 'whitespaceInTitle',
'titleDirs', 'episodeInTitle']);
var confpanel = new Ext.FormPanel({
title:'Digital Video Recorder',
@ -495,14 +502,20 @@ tvheadend.dvrsettings = function() {
fieldLabel: 'Make subdirectories per channel',
name: 'channelDirs'
}), new Ext.form.Checkbox({
fieldLabel: 'Include channel name in title',
fieldLabel: 'Make subdirectories per title',
name: 'titleDirs'
}), new Ext.form.Checkbox({
fieldLabel: 'Include channel name in filename',
name: 'channelInTitle'
}), new Ext.form.Checkbox({
fieldLabel: 'Include date in title',
fieldLabel: 'Include date in filename',
name: 'dateInTitle'
}), new Ext.form.Checkbox({
fieldLabel: 'Include time in title',
fieldLabel: 'Include time in filename',
name: 'timeInTitle'
}), new Ext.form.Checkbox({
fieldLabel: 'Include episode in filename',
name: 'episodeInTitle'
}), new Ext.form.Checkbox({
fieldLabel: 'Replace whitespace in title with \'-\'',
name: 'whitespaceInTitle'