From 6876fc6afe48aebbf78b3a78ab078b4e66be67fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 16 Mar 2010 22:59:10 +0000 Subject: [PATCH] * 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. --- debian/changelog | 6 ++++ src/dvr/dvr.h | 7 +++- src/dvr/dvr_db.c | 71 +++++++++++++++++++++++++++++++------ src/dvr/dvr_rec.c | 15 ++++++-- src/webui/extjs.c | 14 ++++++-- src/webui/static/app/dvr.js | 21 ++++++++--- 6 files changed, 114 insertions(+), 20 deletions(-) diff --git a/debian/changelog b/debian/changelog index 8256c1a0..a4c240c7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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. diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index f3699a0c..9b1fe306 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -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); diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 850bee6f..d14651df 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -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); diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index ac8a05f4..d41ebad6 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -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; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index c7de9614..347eb924 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -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); diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index ad26f85f..b307277d 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -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'