* 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:
parent
8e5d0c68a8
commit
6876fc6afe
6 changed files with 114 additions and 20 deletions
6
debian/changelog
vendored
6
debian/changelog
vendored
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Reference in a new issue