diff --git a/tvheadend/src/dvb/dvb_fe.c b/tvheadend/src/dvb/dvb_fe.c index c68876e..4fa1f62 100644 --- a/tvheadend/src/dvb/dvb_fe.c +++ b/tvheadend/src/dvb/dvb_fe.c @@ -39,7 +39,7 @@ #include "diseqc.h" #include "notify.h" #include "transports.h" - +#include "dvr/dvr.h" /** * Front end monitor @@ -197,7 +197,6 @@ dvb_adapter_open_dump_file(th_dvb_adapter_t *tda) struct dmx_pes_filter_params dmx_param; char fullname[1000]; char path[500]; - extern char *dvr_storage; const char *fname = tda->tda_mux_current->tdmi_identifier; int fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); @@ -220,7 +219,8 @@ dvb_adapter_open_dump_file(th_dvb_adapter_t *tda) return; } - snprintf(path, sizeof(path), "%s/muxdumps", dvr_storage); + snprintf(path, sizeof(path), "%s/muxdumps", + dvr_config_find_by_name_default("")->dvr_storage); if(mkdir(path, 0777) && errno != EEXIST) { tvhlog(LOG_ERR, "dvb", "\"%s\" unable to create mux dump dir %s -- %s", diff --git a/tvheadend/src/dvr/dvr.h b/tvheadend/src/dvr/dvr.h index 918b51e..888fc98 100644 --- a/tvheadend/src/dvr/dvr.h +++ b/tvheadend/src/dvr/dvr.h @@ -24,14 +24,22 @@ #include "channels.h" #include "subscriptions.h" -extern char *dvr_storage; -extern char *dvr_format; -extern char *dvr_file_postfix; -extern uint32_t dvr_retention_days; -extern int dvr_flags; -extern char *dvr_postproc; -extern int dvr_extra_time_pre; -extern int dvr_extra_time_post; +typedef struct dvr_config { + char *dvr_config_name; + char *dvr_storage; + char *dvr_format; + char *dvr_file_postfix; + uint32_t dvr_retention_days; + int dvr_flags; + char *dvr_postproc; + int dvr_extra_time_pre; + int dvr_extra_time_post; + + LIST_ENTRY(dvr_config) config_link; +} dvr_config_t; + +extern struct dvr_config_list dvrconfigs; + extern struct dvr_entry_list dvrentries; #define DVR_DIR_PER_DAY 0x1 @@ -98,6 +106,8 @@ typedef struct dvr_entry { * These meta fields will stay valid as long as reference count > 0 */ + char *de_config_name; + time_t de_start; time_t de_stop; @@ -172,6 +182,8 @@ typedef struct dvr_autorec_entry { TAILQ_ENTRY(dvr_autorec_entry) dae_link; char *dae_id; + char *dae_config_name; + int dae_enabled; char *dae_creator; char *dae_comment; @@ -202,6 +214,14 @@ typedef struct dvr_autorec_entry { * Prototypes */ +dvr_config_t *dvr_config_find_by_name(const char *name); + +dvr_config_t *dvr_config_find_by_name_default(const char *name); + +dvr_config_t *dvr_config_create(const char *name); + +void dvr_config_delete(const char *name); + void dvr_entry_notify(dvr_entry_t *de); const char *dvr_entry_status(dvr_entry_t *de); @@ -210,11 +230,13 @@ const char *dvr_entry_schedstatus(dvr_entry_t *de); void dvr_entry_create_by_autorec(event_t *e, dvr_autorec_entry_t *dae); -dvr_entry_t *dvr_entry_create_by_event(event_t *e, const char *creator, +dvr_entry_t *dvr_entry_create_by_event(const char *dvr_config_name, + event_t *e, const char *creator, dvr_autorec_entry_t *dae, dvr_prio_t pri); -dvr_entry_t *dvr_entry_create(channel_t *ch, time_t start, time_t stop, +dvr_entry_t *dvr_entry_create(const char *dvr_config_name, + channel_t *ch, time_t start, time_t stop, const char *title, const char *description, const char *creator, dvr_autorec_entry_t *dae, epg_episode_t *ee, uint8_t content_type, @@ -240,17 +262,17 @@ dvr_entry_t *dvr_entry_cancel(dvr_entry_t *de); void dvr_entry_dec_ref(dvr_entry_t *de); -void dvr_storage_set(const char *storage); +void dvr_storage_set(dvr_config_t *cfg, const char *storage); -void dvr_postproc_set(const char *postproc); +void dvr_postproc_set(dvr_config_t *cfg, const char *postproc); -void dvr_retention_set(int days); +void dvr_retention_set(dvr_config_t *cfg, int days); -void dvr_flags_set(int flags); +void dvr_flags_set(dvr_config_t *cfg, int flags); -void dvr_extra_time_pre_set(int d); +void dvr_extra_time_pre_set(dvr_config_t *cfg, int d); -void dvr_extra_time_post_set(int d); +void dvr_extra_time_post_set(dvr_config_t *cfg, int d); /** * Query interface @@ -268,7 +290,8 @@ void dvr_query_sort(dvr_query_result_t *dqr); /** * */ -void dvr_autorec_add(const char *title, const char *channel, +void dvr_autorec_add(const char *dvr_config_name, + const char *title, const char *channel, const char *tag, uint8_t content_type, const char *creator, const char *comment); diff --git a/tvheadend/src/dvr/dvr_autorec.c b/tvheadend/src/dvr/dvr_autorec.c index eaac0da..bc75702 100644 --- a/tvheadend/src/dvr/dvr_autorec.c +++ b/tvheadend/src/dvr/dvr_autorec.c @@ -166,6 +166,7 @@ autorec_entry_destroy(dvr_autorec_entry_t *dae) free(dae->dae_id); + free(dae->dae_config_name); free(dae->dae_creator); free(dae->dae_comment); @@ -227,6 +228,8 @@ autorec_record_build(dvr_autorec_entry_t *dae) htsmsg_add_str(e, "id", dae->dae_id); htsmsg_add_u32(e, "enabled", !!dae->dae_enabled); + if (dae->dae_config_name != NULL) + htsmsg_add_str(e, "config_name", dae->dae_config_name); if(dae->dae_creator != NULL) htsmsg_add_str(e, "creator", dae->dae_creator); if(dae->dae_comment != NULL) @@ -307,6 +310,7 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values, if((dae = autorec_entry_find(id, maycreate)) == NULL) return NULL; + tvh_str_update(&dae->dae_config_name, htsmsg_get_str(values, "config_name")); tvh_str_update(&dae->dae_creator, htsmsg_get_str(values, "creator")); tvh_str_update(&dae->dae_comment, htsmsg_get_str(values, "comment")); @@ -417,7 +421,8 @@ dvr_autorec_init(void) * */ void -dvr_autorec_add(const char *title, const char *channel, +dvr_autorec_add(const char *config_name, + const char *title, const char *channel, const char *tag, uint8_t content_type, const char *creator, const char *comment) { @@ -429,6 +434,7 @@ dvr_autorec_add(const char *title, const char *channel, if((dae = autorec_entry_find(NULL, 1)) == NULL) return; + tvh_str_set(&dae->dae_config_name, config_name); tvh_str_set(&dae->dae_creator, creator); tvh_str_set(&dae->dae_comment, comment); diff --git a/tvheadend/src/dvr/dvr_db.c b/tvheadend/src/dvr/dvr_db.c index bf4e808..6752ed8 100644 --- a/tvheadend/src/dvr/dvr_db.c +++ b/tvheadend/src/dvr/dvr_db.c @@ -30,18 +30,11 @@ #include "htsp.h" #include "streaming.h" -char *dvr_storage; -char *dvr_format; -char *dvr_file_postfix; -uint32_t dvr_retention_days; -int dvr_flags; -int dvr_extra_time_pre; -int dvr_extra_time_post; -char *dvr_postproc; -int dvr_iov_max; - static int de_tally; +int dvr_iov_max; + +struct dvr_config_list dvrconfigs; struct dvr_entry_list dvrentries; static void dvr_entry_save(dvr_entry_t *de); @@ -129,6 +122,17 @@ dvrdb_changed(void) notify_by_msg("dvrdb", m); } +/** + * + */ +static void +dvrconfig_changed(void) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_u32(m, "reload", 1); + notify_by_msg("dvrconfig", m); +} + /** * @@ -155,8 +159,9 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) struct tm tm; char buf[40]; int i; + dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); - if(dvr_flags & DVR_CHANNEL_IN_TITLE) + if(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE) snprintf(output, outlen, "%s-", de->de_channel->ch_name); else output[0] = 0; @@ -166,17 +171,17 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) localtime_r(&de->de_start, &tm); - if(dvr_flags & DVR_DATE_IN_TITLE) { + if(cfg->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) { + if(cfg->dvr_flags & DVR_TIME_IN_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(cfg->dvr_flags & DVR_EPISODE_IN_TITLE) { if(de->de_episode.ee_season && de->de_episode.ee_episode) snprintf(output + strlen(output), outlen - strlen(output), @@ -189,7 +194,7 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) de->de_episode.ee_episode); } - if(dvr_flags & DVR_CLEAN_TITLE) { + if(cfg->dvr_flags & DVR_CLEAN_TITLE) { for (i=0;ide_config_name); dvr_make_title(buf, sizeof(buf), de); @@ -231,7 +237,7 @@ dvr_entry_link(dvr_entry_t *de) else de->de_sched_state = DVR_COMPLETED; gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, - de->de_stop + dvr_retention_days * 86400); + de->de_stop + cfg->dvr_retention_days * 86400); } else { de->de_sched_state = DVR_SCHEDULED; @@ -246,7 +252,8 @@ dvr_entry_link(dvr_entry_t *de) * */ dvr_entry_t * -dvr_entry_create(channel_t *ch, time_t start, time_t stop, +dvr_entry_create(const char *config_name, + channel_t *ch, time_t start, time_t stop, const char *title, const char *description, const char *creator, dvr_autorec_entry_t *dae, epg_episode_t *ee, uint8_t content_type, dvr_prio_t pri) @@ -255,6 +262,7 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop, char tbuf[30]; struct tm tm; time_t t; + dvr_config_t *cfg = dvr_config_find_by_name_default(config_name); LIST_FOREACH(de, &ch->ch_dvrs, de_channel_link) if(de->de_start == start && de->de_sched_state != DVR_COMPLETED) @@ -272,11 +280,12 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop, if (ch->ch_dvr_extra_time_pre) de->de_start_extra = ch->ch_dvr_extra_time_pre; else - de->de_start_extra = dvr_extra_time_pre; + de->de_start_extra = cfg->dvr_extra_time_pre; if (ch->ch_dvr_extra_time_post) de->de_stop_extra = ch->ch_dvr_extra_time_post; else - de->de_stop_extra = dvr_extra_time_post; + de->de_stop_extra = cfg->dvr_extra_time_post; + de->de_config_name = strdup(cfg->dvr_config_name); de->de_creator = strdup(creator); de->de_title = strdup(title); de->de_desc = description ? strdup(description) : NULL; @@ -315,13 +324,15 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop, * */ dvr_entry_t * -dvr_entry_create_by_event(event_t *e, const char *creator, +dvr_entry_create_by_event(const char *config_name, + event_t *e, const char *creator, dvr_autorec_entry_t *dae, dvr_prio_t pri) { if(e->e_channel == NULL || e->e_title == NULL) return NULL; - return dvr_entry_create(e->e_channel, e->e_start, e->e_stop, + return dvr_entry_create(config_name, + e->e_channel, e->e_start, e->e_stop, e->e_title, e->e_desc, creator, dae, &e->e_episode, e->e_content_type, pri); } @@ -340,7 +351,7 @@ dvr_entry_create_by_autorec(event_t *e, dvr_autorec_entry_t *dae) } else { snprintf(buf, sizeof(buf), "Auto recording"); } - dvr_entry_create_by_event(e, buf, dae, dae->dae_pri); + dvr_entry_create_by_event(dae->dae_config_name, e, buf, dae, dae->dae_pri); } @@ -360,6 +371,7 @@ dvr_entry_dec_ref(dvr_entry_t *de) if(de->de_autorec != NULL) LIST_REMOVE(de, de_autorec_link); + free(de->de_config_name); free(de->de_creator); free(de->de_title); free(de->de_ititle); @@ -406,6 +418,7 @@ dvr_db_load_one(htsmsg_t *c, int id) channel_t *ch; uint32_t start, stop; int d; + dvr_config_t *cfg; if(htsmsg_get_u32(c, "start", &start)) return; @@ -417,6 +430,9 @@ dvr_db_load_one(htsmsg_t *c, int id) if((ch = channel_find_by_name(s, 0, 0)) == NULL) return; + s = htsmsg_get_str(c, "config_name"); + cfg = dvr_config_find_by_name_default(s); + if((title = htsmsg_get_str(c, "title")) == NULL) return; @@ -433,17 +449,18 @@ dvr_db_load_one(htsmsg_t *c, int id) de->de_start = start; de->de_stop = stop; + de->de_config_name = strdup(cfg->dvr_config_name); de->de_creator = strdup(creator); de->de_title = strdup(title); de->de_pri = dvr_pri2val(htsmsg_get_str(c, "pri")); if(htsmsg_get_s32(c, "start_extra", &d)) - de->de_start_extra = dvr_extra_time_pre; + de->de_start_extra = cfg->dvr_extra_time_pre; else de->de_start_extra = d; if(htsmsg_get_s32(c, "stop_extra", &d)) - de->de_stop_extra = dvr_extra_time_post; + de->de_stop_extra = cfg->dvr_extra_time_post; else de->de_stop_extra = d; @@ -519,6 +536,8 @@ dvr_entry_save(dvr_entry_t *de) htsmsg_add_s32(m, "start_extra", de->de_start_extra); htsmsg_add_s32(m, "stop_extra", de->de_stop_extra); + htsmsg_add_str(m, "config_name", de->de_config_name); + htsmsg_add_str(m, "creator", de->de_creator); if(de->de_filename != NULL) @@ -577,6 +596,8 @@ dvr_timer_expire(void *aux) static void dvr_stop_recording(dvr_entry_t *de, int stopcode) { + dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); + dvr_rec_unsubscribe(de, stopcode); de->de_sched_state = DVR_COMPLETED; @@ -591,7 +612,7 @@ dvr_stop_recording(dvr_entry_t *de, int stopcode) dvr_entry_notify(de); gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, - de->de_stop + dvr_retention_days * 86400); + de->de_stop + cfg->dvr_retention_days * 86400); } @@ -715,84 +736,98 @@ dvr_destroy_by_channel(channel_t *ch) void dvr_init(void) { - htsmsg_t *m; + htsmsg_t *m, *l; + htsmsg_field_t *f; + const char *s; char buf[500]; const char *homedir; struct stat st; uint32_t u32; + dvr_config_t *cfg; dvr_iov_max = sysconf(_SC_IOV_MAX); /* Default settings */ - dvr_retention_days = 31; - dvr_format = strdup("matroska"); - dvr_file_postfix = strdup("mkv"); + LIST_INIT(&dvrconfigs); + cfg = dvr_config_create(""); /* Override settings with config */ - dvr_flags = DVR_TAG_FILES; + l = hts_settings_load("dvr"); + if(l != NULL) { + HTSMSG_FOREACH(f, l) { + m = htsmsg_get_map_by_field(f); + if(m == NULL) + continue; - if((m = hts_settings_load("dvr/config")) != NULL) { + s = htsmsg_get_str(m, "config_name"); + cfg = dvr_config_find_by_name(s); + if(cfg == NULL) + cfg = dvr_config_create(s); - htsmsg_get_s32(m, "pre-extra-time", &dvr_extra_time_pre); - htsmsg_get_s32(m, "post-extra-time", &dvr_extra_time_post); - htsmsg_get_u32(m, "retention-days", &dvr_retention_days); - tvh_str_set(&dvr_storage, htsmsg_get_str(m, "storage")); + htsmsg_get_s32(m, "pre-extra-time", &cfg->dvr_extra_time_pre); + htsmsg_get_s32(m, "post-extra-time", &cfg->dvr_extra_time_post); + htsmsg_get_u32(m, "retention-days", &cfg->dvr_retention_days); + tvh_str_set(&cfg->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, "day-dir", &u32) && u32) + cfg->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-dir", &u32) && u32) + cfg->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, "channel-in-title", &u32) && u32) + cfg->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, "date-in-title", &u32) && u32) + cfg->dvr_flags |= DVR_DATE_IN_TITLE; - if(!htsmsg_get_u32(m, "time-in-title", &u32) && u32) - dvr_flags |= DVR_TIME_IN_TITLE; - - if(!htsmsg_get_u32(m, "whitespace-in-title", &u32) && u32) - dvr_flags |= DVR_WHITESPACE_IN_TITLE; + if(!htsmsg_get_u32(m, "time-in-title", &u32) && u32) + cfg->dvr_flags |= DVR_TIME_IN_TITLE; + + if(!htsmsg_get_u32(m, "whitespace-in-title", &u32) && u32) + cfg->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, "title-dir", &u32) && u32) + cfg->dvr_flags |= DVR_DIR_PER_TITLE; - if(!htsmsg_get_u32(m, "episode-in-title", &u32) && u32) - dvr_flags |= DVR_EPISODE_IN_TITLE; + if(!htsmsg_get_u32(m, "episode-in-title", &u32) && u32) + cfg->dvr_flags |= DVR_EPISODE_IN_TITLE; - if(!htsmsg_get_u32(m, "tag-files", &u32) && !u32) - dvr_flags &= ~DVR_TAG_FILES; - - tvh_str_set(&dvr_postproc, htsmsg_get_str(m, "postproc")); + if(!htsmsg_get_u32(m, "tag-files", &u32) && !u32) + cfg->dvr_flags &= ~DVR_TAG_FILES; + + tvh_str_set(&cfg->dvr_postproc, htsmsg_get_str(m, "postproc")); + } - htsmsg_destroy(m); + htsmsg_destroy(l); } - if(dvr_storage == NULL) { - /* Try to figure out a good place to put them videos */ - - homedir = getenv("HOME"); - - if(homedir != NULL) { - snprintf(buf, sizeof(buf), "%s/Videos", homedir); - if(stat(buf, &st) == 0 && S_ISDIR(st.st_mode)) - dvr_storage = strdup(buf); - - else if(stat(homedir, &st) == 0 && S_ISDIR(st.st_mode)) - dvr_storage = strdup(homedir); - else - dvr_storage = strdup(getcwd(buf, sizeof(buf))); + LIST_FOREACH(cfg, &dvrconfigs, config_link) { + if(cfg->dvr_storage == NULL || !strlen(cfg->dvr_storage)) { + /* Try to figure out a good place to put them videos */ + + homedir = getenv("HOME"); + + if(homedir != NULL) { + snprintf(buf, sizeof(buf), "%s/Videos", homedir); + if(stat(buf, &st) == 0 && S_ISDIR(st.st_mode)) + cfg->dvr_storage = strdup(buf); + + else if(stat(homedir, &st) == 0 && S_ISDIR(st.st_mode)) + cfg->dvr_storage = strdup(homedir); + else + cfg->dvr_storage = strdup(getcwd(buf, sizeof(buf))); + } + + tvhlog(LOG_WARNING, "dvr", + "Output directory for video recording is not yet configured " + "for DVR configuration \"%s\". " + "Defaulting to to \"%s\". " + "This can be changed from the web user interface.", + cfg->dvr_config_name, cfg->dvr_storage); } - - tvhlog(LOG_WARNING, "dvr", - "Output directory for video recording is not yet configured. " - "Defaulting to to \"%s\". " - "This can be changed from the web user interface.", - dvr_storage); } dvr_autorec_init(); @@ -801,56 +836,149 @@ dvr_init(void) } /** + * find a dvr config by name, return NULL if not found + */ +dvr_config_t * +dvr_config_find_by_name(const char *name) +{ + dvr_config_t *cfg; + + if (name == NULL) + name = ""; + + LIST_FOREACH(cfg, &dvrconfigs, config_link) + if (!strcmp(name, cfg->dvr_config_name)) + return cfg; + + return NULL; +} + +/** + * find a dvr config by name, return the default config if not found + */ +dvr_config_t * +dvr_config_find_by_name_default(const char *name) +{ + dvr_config_t *cfg; + + cfg = dvr_config_find_by_name(name); + + if (cfg == NULL) { + tvhlog(LOG_WARNING, "dvr", "Configuration '%s' not found", name); + cfg = dvr_config_find_by_name(""); + } + + if (cfg == NULL) { + cfg = dvr_config_create(""); + } + + return cfg; +} + +/** + * create a new named dvr config; the caller is responsible + * to avoid duplicates + */ +dvr_config_t * +dvr_config_create(const char *name) +{ + dvr_config_t *cfg; + + if (name == NULL) + name = ""; + + tvhlog(LOG_INFO, "dvr", "Creating new configuration '%s'", name); + + cfg = calloc(1, sizeof(dvr_config_t)); + cfg->dvr_config_name = strdup(name); + cfg->dvr_retention_days = 31; + cfg->dvr_format = strdup("matroska"); + cfg->dvr_file_postfix = strdup("mkv"); + cfg->dvr_flags = DVR_TAG_FILES; + + LIST_INSERT_HEAD(&dvrconfigs, cfg, config_link); + + return LIST_FIRST(&dvrconfigs); +} + +/** + * + */ +void +dvr_config_delete(const char *name) +{ + dvr_config_t *cfg; + + if (name == NULL || strlen(name) == 0) { + tvhlog(LOG_WARNING,"dvr","Attempt to delete default config ignored"); + return; + } + + cfg = dvr_config_find_by_name(name); + if (cfg != NULL) { + tvhlog(LOG_INFO, "dvr", "Deleting configuration '%s'", + cfg->dvr_config_name); + hts_settings_remove("dvr/config%s", cfg->dvr_config_name); + LIST_REMOVE(cfg, config_link); + dvrconfig_changed(); + } +} + +/** * */ static void -dvr_save(void) +dvr_save(dvr_config_t *cfg) { htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_str(m, "storage", dvr_storage); - htsmsg_add_u32(m, "retention-days", dvr_retention_days); - htsmsg_add_u32(m, "pre-extra-time", dvr_extra_time_pre); - htsmsg_add_u32(m, "post-extra-time", dvr_extra_time_post); - 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)); - 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)); - htsmsg_add_u32(m, "tag-files", !!(dvr_flags & DVR_TAG_FILES)); - if(dvr_postproc != NULL) - htsmsg_add_str(m, "postproc", dvr_postproc); - - hts_settings_save(m, "dvr/config"); + if (cfg->dvr_config_name != NULL && strlen(cfg->dvr_config_name) != 0) + htsmsg_add_str(m, "config_name", cfg->dvr_config_name); + htsmsg_add_str(m, "storage", cfg->dvr_storage); + htsmsg_add_u32(m, "retention-days", cfg->dvr_retention_days); + htsmsg_add_u32(m, "pre-extra-time", cfg->dvr_extra_time_pre); + htsmsg_add_u32(m, "post-extra-time", cfg->dvr_extra_time_post); + htsmsg_add_u32(m, "day-dir", !!(cfg->dvr_flags & DVR_DIR_PER_DAY)); + htsmsg_add_u32(m, "channel-dir", !!(cfg->dvr_flags & DVR_DIR_PER_CHANNEL)); + htsmsg_add_u32(m, "channel-in-title", !!(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE)); + htsmsg_add_u32(m, "date-in-title", !!(cfg->dvr_flags & DVR_DATE_IN_TITLE)); + htsmsg_add_u32(m, "time-in-title", !!(cfg->dvr_flags & DVR_TIME_IN_TITLE)); + htsmsg_add_u32(m, "whitespace-in-title", !!(cfg->dvr_flags & DVR_WHITESPACE_IN_TITLE)); + htsmsg_add_u32(m, "title-dir", !!(cfg->dvr_flags & DVR_DIR_PER_TITLE)); + htsmsg_add_u32(m, "episode-in-title", !!(cfg->dvr_flags & DVR_EPISODE_IN_TITLE)); + htsmsg_add_u32(m, "tag-files", !!(cfg->dvr_flags & DVR_TAG_FILES)); + if(cfg->dvr_postproc != NULL) + htsmsg_add_str(m, "postproc", cfg->dvr_postproc); + + hts_settings_save(m, "dvr/config%s", cfg->dvr_config_name); htsmsg_destroy(m); + + dvrconfig_changed(); } /** * */ void -dvr_storage_set(const char *storage) +dvr_storage_set(dvr_config_t *cfg, const char *storage) { - if(!strcmp(dvr_storage, storage)) + if(cfg->dvr_storage != NULL && !strcmp(cfg->dvr_storage, storage)) return; - tvh_str_set(&dvr_storage, storage); - dvr_save(); + tvh_str_set(&cfg->dvr_storage, storage); + dvr_save(cfg); } /** * */ void -dvr_postproc_set(const char *postproc) +dvr_postproc_set(dvr_config_t *cfg, const char *postproc) { - if(dvr_postproc != NULL && !strcmp(dvr_postproc, postproc)) + if(cfg->dvr_postproc != NULL && !strcmp(cfg->dvr_postproc, postproc)) return; - tvh_str_set(&dvr_postproc, !strcmp(postproc, "") ? NULL : postproc); - dvr_save(); + tvh_str_set(&cfg->dvr_postproc, !strcmp(postproc, "") ? NULL : postproc); + dvr_save(cfg); } @@ -858,21 +986,21 @@ dvr_postproc_set(const char *postproc) * */ void -dvr_retention_set(int days) +dvr_retention_set(dvr_config_t *cfg, int days) { dvr_entry_t *de; - if(days < 1 || dvr_retention_days == days) + if(days < 1 || cfg->dvr_retention_days == days) return; - dvr_retention_days = days; + cfg->dvr_retention_days = days; /* Also, rearm all timres */ LIST_FOREACH(de, &dvrentries, de_global_link) if(de->de_sched_state == DVR_COMPLETED) gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, - de->de_stop + dvr_retention_days * 86400); - dvr_save(); + de->de_stop + cfg->dvr_retention_days * 86400); + dvr_save(cfg); } @@ -880,13 +1008,13 @@ dvr_retention_set(int days) * */ void -dvr_flags_set(int flags) +dvr_flags_set(dvr_config_t *cfg, int flags) { - if(dvr_flags == flags) + if(cfg->dvr_flags == flags) return; - dvr_flags = flags; - dvr_save(); + cfg->dvr_flags = flags; + dvr_save(cfg); } @@ -894,12 +1022,13 @@ dvr_flags_set(int flags) * */ void -dvr_extra_time_pre_set(int d) +dvr_extra_time_pre_set(dvr_config_t *cfg, int d) { - if(dvr_extra_time_pre == d) + if(cfg->dvr_extra_time_pre == d) return; - dvr_extra_time_pre = d; - dvr_save(); + + cfg->dvr_extra_time_pre = d; + dvr_save(cfg); } @@ -907,12 +1036,13 @@ dvr_extra_time_pre_set(int d) * */ void -dvr_extra_time_post_set(int d) +dvr_extra_time_post_set(dvr_config_t *cfg, int d) { - if(dvr_extra_time_post == d) + if(cfg->dvr_extra_time_post == d) return; - dvr_extra_time_post = d; - dvr_save(); + + cfg->dvr_extra_time_post = d; + dvr_save(cfg); } diff --git a/tvheadend/src/dvr/dvr_rec.c b/tvheadend/src/dvr/dvr_rec.c index 33235a3..af3dd46 100644 --- a/tvheadend/src/dvr/dvr_rec.c +++ b/tvheadend/src/dvr/dvr_rec.c @@ -40,7 +40,7 @@ * */ static void *dvr_thread(void *aux); -static void dvr_spawn_postproc(dvr_entry_t *de); +static void dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc); static void dvr_thread_epilog(dvr_entry_t *de); @@ -157,7 +157,7 @@ makedirs(const char *path) * Replace various chars with a dash */ static void -cleanupfilename(char *s) +cleanupfilename(char *s, int dvr_flags) { int i, len = strlen(s); for(i = 0; i < len; i++) { @@ -186,28 +186,29 @@ pvr_generate_filename(dvr_entry_t *de) struct stat st; char *filename; struct tm tm; + dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); filename = strdup(de->de_ititle); - cleanupfilename(filename); + cleanupfilename(filename,cfg->dvr_flags); - snprintf(path, sizeof(path), "%s", dvr_storage); + snprintf(path, sizeof(path), "%s", cfg->dvr_storage); /* Append per-day directory */ - if(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); + cleanupfilename(fullname,cfg->dvr_flags); snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", fullname); } /* Append per-channel directory */ - if(dvr_flags & DVR_DIR_PER_CHANNEL) { + if(cfg->dvr_flags & DVR_DIR_PER_CHANNEL) { char *chname = strdup(de->de_channel->ch_name); - cleanupfilename(chname); + cleanupfilename(chname,cfg->dvr_flags); snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", chname); free(chname); @@ -215,10 +216,10 @@ pvr_generate_filename(dvr_entry_t *de) /* Append per-title directory */ - if(dvr_flags & DVR_DIR_PER_TITLE) { + if(cfg->dvr_flags & DVR_DIR_PER_TITLE) { char *title = strdup(de->de_title); - cleanupfilename(title); + cleanupfilename(title,cfg->dvr_flags); snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", title); free(title); @@ -233,7 +234,7 @@ pvr_generate_filename(dvr_entry_t *de) /* Construct final name */ snprintf(fullname, sizeof(fullname), "%s/%s.%s", - path, filename, dvr_file_postfix); + path, filename, cfg->dvr_file_postfix); while(1) { if(stat(fullname, &st) == -1) { @@ -248,7 +249,7 @@ pvr_generate_filename(dvr_entry_t *de) tally++; snprintf(fullname, sizeof(fullname), "%s/%s-%d.%s", - path, filename, tally, dvr_file_postfix); + path, filename, tally, cfg->dvr_file_postfix); } tvh_str_set(&de->de_filename, fullname); @@ -301,6 +302,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) const source_info_t *si = &ss->ss_si; const streaming_start_component_t *ssc; int i; + dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); if(pvr_generate_filename(de) != 0) { dvr_rec_fatal_error(de, "Unable to create directories"); @@ -308,7 +310,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) } de->de_mkmux = mk_mux_create(de->de_filename, ss, de, - !!(dvr_flags & DVR_TAG_FILES)); + !!(cfg->dvr_flags & DVR_TAG_FILES)); if(de->de_mkmux == NULL) { dvr_rec_fatal_error(de, "Unable to open file"); @@ -497,7 +499,7 @@ dvr_thread(void *aux) * */ static void -dvr_spawn_postproc(dvr_entry_t *de) +dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc) { char *fmap[256]; char **args; @@ -553,6 +555,7 @@ dvr_thread_epilog(dvr_entry_t *de) mk_mux_close(de->de_mkmux); de->de_mkmux = NULL; - if(dvr_postproc) - dvr_spawn_postproc(de); + dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); + if(cfg->dvr_postproc) + dvr_spawn_postproc(de,cfg->dvr_postproc); } diff --git a/tvheadend/src/htsp.c b/tvheadend/src/htsp.c index 7a6cd9b..d7eb68c 100644 --- a/tvheadend/src/htsp.c +++ b/tvheadend/src/htsp.c @@ -493,15 +493,19 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in) event_t *e; dvr_entry_t *de; dvr_entry_sched_state_t dvr_status; + const char *dvr_config_name; if(htsmsg_get_u32(in, "eventId", &eventid)) return htsp_error("Missing argument 'eventId'"); if((e = epg_event_find_by_id(eventid)) == NULL) return htsp_error("Event does not exist"); + + if((dvr_config_name = htsmsg_get_str(in, "configName")) == NULL) + dvr_config_name = ""; //create the dvr entry - de = dvr_entry_create_by_event(e, + de = dvr_entry_create_by_event(dvr_config_name,e, htsp->htsp_username ? htsp->htsp_username : "anonymous", NULL, DVR_PRIO_NORMAL); @@ -698,8 +702,9 @@ htsp_method_getDiskSpace(htsp_connection_t *htsp, htsmsg_t *in) { htsmsg_t *out; struct statvfs diskdata; + dvr_config_t *cfg = dvr_config_find_by_name_default(""); - if(statvfs(dvr_storage,&diskdata) == -1) + if(statvfs(cfg->dvr_storage,&diskdata) == -1) return htsp_error("Unable to stat path"); out = htsmsg_create_map(); diff --git a/tvheadend/src/tvhead.h b/tvheadend/src/tvhead.h index 3b4737e..31b087e 100644 --- a/tvheadend/src/tvhead.h +++ b/tvheadend/src/tvhead.h @@ -104,6 +104,7 @@ TAILQ_HEAD(channel_queue, channel); LIST_HEAD(channel_list, channel); LIST_HEAD(event_list, event); RB_HEAD(event_tree, event); +LIST_HEAD(dvr_config_list, dvr_config); LIST_HEAD(dvr_entry_list, dvr_entry); TAILQ_HEAD(ref_update_queue, ref_update); LIST_HEAD(th_transport_list, th_transport); diff --git a/tvheadend/src/webui/extjs.c b/tvheadend/src/webui/extjs.c index 7a174ef..b3ff976 100644 --- a/tvheadend/src/webui/extjs.c +++ b/tvheadend/src/webui/extjs.c @@ -568,7 +568,7 @@ extjs_channeltags(http_connection_t *hc, const char *remain, void *opaque) pthread_mutex_lock(&global_lock); - if(!strcmp(op, "listTags")) { + if(op != NULL && !strcmp(op, "listTags")) { out = htsmsg_create_map(); array = htsmsg_create_list(); @@ -603,6 +603,50 @@ extjs_channeltags(http_connection_t *hc, const char *remain, void *opaque) * */ static int +extjs_confignames(http_connection_t *hc, const char *remain, void *opaque) +{ + htsbuf_queue_t *hq = &hc->hc_reply; + const char *op = http_arg_get(&hc->hc_req_args, "op"); + htsmsg_t *out, *array, *e; + dvr_config_t *cfg; + + pthread_mutex_lock(&global_lock); + + if(op != NULL && !strcmp(op, "list")) { + + out = htsmsg_create_map(); + array = htsmsg_create_list(); + + LIST_FOREACH(cfg, &dvrconfigs, config_link) { + e = htsmsg_create_map(); + htsmsg_add_str(e, "identifier", cfg->dvr_config_name); + if (strlen(cfg->dvr_config_name) == 0) + htsmsg_add_str(e, "name", "(default)"); + else + htsmsg_add_str(e, "name", cfg->dvr_config_name); + htsmsg_add_msg(array, NULL, e); + } + + htsmsg_add_msg(out, "entries", array); + + } else { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_BAD_REQUEST; + } + + pthread_mutex_unlock(&global_lock); + + htsmsg_json_serialize(out, hq, 0); + htsmsg_destroy(out); + http_output_content(hc, "text/x-json; charset=UTF-8"); + return 0; + +} + +/** + * + */ +static int extjs_epg(http_connection_t *hc, const char *remain, void *opaque) { htsbuf_queue_t *hq = &hc->hc_reply; @@ -713,6 +757,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) dvr_entry_t *de; const char *s; int flags = 0; + dvr_config_t *cfg; if(op == NULL) op = "loadSettings"; @@ -725,14 +770,17 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) } if(!strcmp(op, "recordEvent")) { - s = http_arg_get(&hc->hc_req_args, "eventId"); + const char *config_name = http_arg_get(&hc->hc_req_args, "config_name"); + + s = http_arg_get(&hc->hc_req_args, "eventId"); if((e = epg_event_find_by_id(atoi(s))) == NULL) { pthread_mutex_unlock(&global_lock); return HTTP_STATUS_BAD_REQUEST; } - dvr_entry_create_by_event(e, hc->hc_representative, NULL, DVR_PRIO_NORMAL); + dvr_entry_create_by_event(config_name, + e, hc->hc_representative, NULL, DVR_PRIO_NORMAL); out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); @@ -751,6 +799,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) } else if(!strcmp(op, "createEntry")) { + const char *config_name = http_arg_get(&hc->hc_req_args, "config_name"); const char *title = http_arg_get(&hc->hc_req_args, "title"); const char *datestr = http_arg_get(&hc->hc_req_args, "date"); const char *startstr = http_arg_get(&hc->hc_req_args, "starttime"); @@ -787,7 +836,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, + dvr_entry_create(config_name, + ch, start, stop, title, NULL, hc->hc_representative, NULL, NULL, 0, dvr_pri2val(pri)); out = htsmsg_create_map(); @@ -798,7 +848,8 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) - dvr_autorec_add(http_arg_get(&hc->hc_req_args, "title"), + dvr_autorec_add(http_arg_get(&hc->hc_req_args, "config_name"), + http_arg_get(&hc->hc_req_args, "title"), http_arg_get(&hc->hc_req_args, "channel"), http_arg_get(&hc->hc_req_args, "tag"), cgrp ? epg_content_group_find_by_name(cgrp) : 0, @@ -809,42 +860,54 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) } else if(!strcmp(op, "loadSettings")) { + s = http_arg_get(&hc->hc_req_args, "config_name"); + if (s == NULL) + s = ""; + cfg = dvr_config_find_by_name_default(s); + r = htsmsg_create_map(); - htsmsg_add_str(r, "storage", dvr_storage); - if(dvr_postproc != NULL) - htsmsg_add_str(r, "postproc", dvr_postproc); - htsmsg_add_u32(r, "retention", dvr_retention_days); - htsmsg_add_u32(r, "preExtraTime", dvr_extra_time_pre); - htsmsg_add_u32(r, "postExtraTime", dvr_extra_time_post); - 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)); - 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)); - htsmsg_add_u32(r, "cleanTitle", !!(dvr_flags & DVR_CLEAN_TITLE)); - htsmsg_add_u32(r, "tagFiles", !!(dvr_flags & DVR_TAG_FILES)); + htsmsg_add_str(r, "storage", cfg->dvr_storage); + if(cfg->dvr_postproc != NULL) + htsmsg_add_str(r, "postproc", cfg->dvr_postproc); + htsmsg_add_u32(r, "retention", cfg->dvr_retention_days); + htsmsg_add_u32(r, "preExtraTime", cfg->dvr_extra_time_pre); + htsmsg_add_u32(r, "postExtraTime", cfg->dvr_extra_time_post); + htsmsg_add_u32(r, "dayDirs", !!(cfg->dvr_flags & DVR_DIR_PER_DAY)); + htsmsg_add_u32(r, "channelDirs", !!(cfg->dvr_flags & DVR_DIR_PER_CHANNEL)); + htsmsg_add_u32(r, "channelInTitle", !!(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE)); + htsmsg_add_u32(r, "dateInTitle", !!(cfg->dvr_flags & DVR_DATE_IN_TITLE)); + htsmsg_add_u32(r, "timeInTitle", !!(cfg->dvr_flags & DVR_TIME_IN_TITLE)); + htsmsg_add_u32(r, "whitespaceInTitle", !!(cfg->dvr_flags & DVR_WHITESPACE_IN_TITLE)); + htsmsg_add_u32(r, "titleDirs", !!(cfg->dvr_flags & DVR_DIR_PER_TITLE)); + htsmsg_add_u32(r, "episodeInTitle", !!(cfg->dvr_flags & DVR_EPISODE_IN_TITLE)); + htsmsg_add_u32(r, "cleanTitle", !!(cfg->dvr_flags & DVR_CLEAN_TITLE)); + htsmsg_add_u32(r, "tagFiles", !!(cfg->dvr_flags & DVR_TAG_FILES)); out = json_single_record(r, "dvrSettings"); } else if(!strcmp(op, "saveSettings")) { + s = http_arg_get(&hc->hc_req_args, "config_name"); + cfg = dvr_config_find_by_name(s); + if (cfg == NULL) + cfg = dvr_config_create(s); + + tvhlog(LOG_INFO,"dvr","Saving configuration '%s'", cfg->dvr_config_name); + if((s = http_arg_get(&hc->hc_req_args, "storage")) != NULL) - dvr_storage_set(s); + dvr_storage_set(cfg,s); if((s = http_arg_get(&hc->hc_req_args, "postproc")) != NULL) - dvr_postproc_set(s); + dvr_postproc_set(cfg,s); if((s = http_arg_get(&hc->hc_req_args, "retention")) != NULL) - dvr_retention_set(atoi(s)); + dvr_retention_set(cfg,atoi(s)); if((s = http_arg_get(&hc->hc_req_args, "preExtraTime")) != NULL) - dvr_extra_time_pre_set(atoi(s)); + dvr_extra_time_pre_set(cfg,atoi(s)); if((s = http_arg_get(&hc->hc_req_args, "postExtraTime")) != NULL) - dvr_extra_time_post_set(atoi(s)); + dvr_extra_time_post_set(cfg,atoi(s)); if(http_arg_get(&hc->hc_req_args, "dayDirs") != NULL) flags |= DVR_DIR_PER_DAY; @@ -867,7 +930,15 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) if(http_arg_get(&hc->hc_req_args, "tagFiles") != NULL) flags |= DVR_TAG_FILES; - dvr_flags_set(flags); + dvr_flags_set(cfg,flags); + + out = htsmsg_create_map(); + htsmsg_add_u32(out, "success", 1); + + } else if(!strcmp(op, "deleteSettings")) { + + s = http_arg_get(&hc->hc_req_args, "config_name"); + dvr_config_delete(s); out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); @@ -941,6 +1012,8 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_str(m, "chicon", de->de_channel->ch_icon); } + htsmsg_add_str(m, "config_name", de->de_config_name); + if(de->de_title != NULL) htsmsg_add_str(m, "title", de->de_title); @@ -1401,6 +1474,7 @@ extjs_start(void) http_path_add("/channels", NULL, extjs_channels, ACCESS_WEB_INTERFACE); http_path_add("/xmltv", NULL, extjs_xmltv, ACCESS_WEB_INTERFACE); http_path_add("/channeltags", NULL, extjs_channeltags, ACCESS_WEB_INTERFACE); + http_path_add("/confignames", NULL, extjs_confignames, ACCESS_WEB_INTERFACE); http_path_add("/epg", NULL, extjs_epg, ACCESS_WEB_INTERFACE); http_path_add("/dvr", NULL, extjs_dvr, ACCESS_WEB_INTERFACE); http_path_add("/dvrlist", NULL, extjs_dvrlist, ACCESS_WEB_INTERFACE); diff --git a/tvheadend/src/webui/simpleui.c b/tvheadend/src/webui/simpleui.c index 4fae2d8..5905275 100644 --- a/tvheadend/src/webui/simpleui.c +++ b/tvheadend/src/webui/simpleui.c @@ -210,7 +210,7 @@ page_einfo(http_connection_t *hc, const char *remain, void *opaque) de = dvr_entry_find_by_event(e); if((http_arg_get(&hc->hc_req_args, "rec")) != NULL) { - de = dvr_entry_create_by_event(e, hc->hc_username ?: "anonymous", NULL, + de = dvr_entry_create_by_event("", e, hc->hc_username ?: "anonymous", NULL, DVR_PRIO_NORMAL); } else if(de != NULL && (http_arg_get(&hc->hc_req_args, "cancel")) != NULL) { de = dvr_entry_cancel(de); diff --git a/tvheadend/src/webui/static/app/comet.js b/tvheadend/src/webui/static/app/comet.js index a24bb3b..6182b31 100644 --- a/tvheadend/src/webui/static/app/comet.js +++ b/tvheadend/src/webui/static/app/comet.js @@ -12,6 +12,7 @@ Ext.extend(tvheadend.Comet = function() { channeltags: true, autorec: true, dvrdb: true, + dvrconfig: true, channels: true }); }, Ext.util.Observable); diff --git a/tvheadend/src/webui/static/app/dvr.js b/tvheadend/src/webui/static/app/dvr.js index a255724..10fed19 100644 --- a/tvheadend/src/webui/static/app/dvr.js +++ b/tvheadend/src/webui/static/app/dvr.js @@ -28,6 +28,28 @@ tvheadend.dvrprio = new Ext.data.SimpleStore({ }); /** + * Configuration names + */ +tvheadend.configNames = new Ext.data.JsonStore({ + autoLoad:true, + root:'entries', + fields: ['identifier','name'], + id: 'identifier', + url:'confignames', + baseParams: { + op: 'list' + } +}); + +tvheadend.configNames.setDefaultSort('name', 'ASC'); + +tvheadend.comet.on('dvrconfig', function(m) { + if(m.reload != null) + tvheadend.configNames.reload(); +}); + + +/** * */ tvheadend.dvrDetails = function(entry) { @@ -190,6 +212,18 @@ tvheadend.dvrschedule = function() { hidden:true, dataIndex: 'creator' },{ + width: 200, + id:'config_name', + header: "DVR Configuration", + renderer: function(value, metadata, record, row, col, store) { + if (!value) { + return '(default)'; + } else { + return value; + } + }, + dataIndex: 'config_name' + },{ width: 200, id:'status', header: "Status", @@ -267,7 +301,19 @@ tvheadend.dvrschedule = function() { allowBlank: false, fieldLabel: 'Title', name: 'title' - } + }, + new Ext.form.ComboBox({ + store: tvheadend.configNames, + triggerAction: 'all', + mode: 'local', + fieldLabel: 'DVR Configuration', + valueField: 'identifier', + displayField: 'name', + name: 'config_name', + emptyText: '(default)', + value: '', + editable: false + }) ], buttons: [{ text: 'Create', @@ -285,6 +331,18 @@ tvheadend.dvrschedule = function() { items: panel }); win.show(); + new Ext.form.ComboBox({ + store: tvheadend.configNames, + triggerAction: 'all', + mode: 'local', + fieldLabel: 'DVR Configuration', + valueField: 'identifier', + displayField: 'name', + name: 'config_name', + emptyText: '(default)', + value: '', + editable: false + }) }; @@ -448,6 +506,26 @@ tvheadend.autoreceditor = function() { valueField: 'identifier', displayField: 'name' }) + },{ + header: "DVR Configuration", + dataIndex: 'config_name', + renderer: function(value, metadata, record, row, col, store) { + if (!value) { + return '(default)'; + } else { + return value; + } + }, + editor: new Ext.form.ComboBox({ + store: tvheadend.configNames, + triggerAction: 'all', + mode: 'local', + valueField: 'identifier', + displayField: 'name', + name: 'config_name', + emptyText: '(default)', + editable: false + }) },{ header: "Created by", dataIndex: 'creator', @@ -484,6 +562,7 @@ tvheadend.dvr = function() { {name: 'chicon'}, {name: 'start', type: 'date', dateFormat: 'U' /* unix time */}, {name: 'end', type: 'date', dateFormat: 'U' /* unix time */}, + {name: 'config_name'}, {name: 'status'}, {name: 'schedstate'}, {name: 'creator'}, @@ -522,7 +601,7 @@ tvheadend.dvr = function() { tvheadend.autorecRecord = Ext.data.Record.create([ 'enabled','title','channel','tag','creator','contentgrp','comment', - 'weekdays', 'pri', 'approx_time' + 'weekdays', 'pri', 'approx_time', 'config_name' ]); @@ -568,6 +647,25 @@ tvheadend.dvrsettings = function() { 'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs', 'episodeInTitle', 'cleanTitle', 'tagFiles']); + var confcombo = new Ext.form.ComboBox({ + store: tvheadend.configNames, + triggerAction: 'all', + mode: 'local', + displayField: 'name', + name: 'config_name', + emptyText: '(default)', + value: '', + editable: true + }); + + var delButton = new Ext.Toolbar.Button({ + tooltip: 'Delete named configuration', + iconCls:'remove', + text: "Delete configuration", + handler: deleteConfiguration, + disabled: true + }); + var confpanel = new Ext.FormPanel({ title:'Digital Video Recorder', iconCls: 'drive', @@ -633,43 +731,85 @@ tvheadend.dvrsettings = function() { fieldLabel: 'Post-processor command', name: 'postproc' }], - tbar: [{ - tooltip: 'Save changes made to channel configuration below', + tbar: [confcombo, { + tooltip: 'Save changes made to dvr configuration below', iconCls:'save', text: "Save configuration", handler: saveChanges - }, '->', { + }, delButton, '->', { text: 'Help', handler: function() { new tvheadend.help('DVR configuration', 'config_dvr.html'); } }] - }); - - confpanel.on('render', function() { + + function loadConfig() { confpanel.getForm().load({ url:'dvr', - params:{'op':'loadSettings'}, + params:{'op':'loadSettings','config_name':confcombo.getValue()}, success:function(form, action) { confpanel.enable(); } }); + } + + confcombo.on('select', function() { + if (confcombo.getValue() == '') + delButton.disable(); + else + delButton.enable(); + loadConfig(); + }); + + confpanel.on('render', function() { + loadConfig(); }); function saveChanges() { + var config_name = confcombo.getValue(); confpanel.getForm().submit({ url:'dvr', - params:{'op':'saveSettings'}, + params:{'op':'saveSettings','config_name':config_name}, waitMsg:'Saving Data...', + success: function(form, action) { + confcombo.setValue(config_name); + confcombo.fireEvent('select'); + }, failure: function(form, action) { Ext.Msg.alert('Save failed', action.result.errormsg); } }); } + function deleteConfiguration() { + if (confcombo.getValue() != "") { + Ext.MessageBox.confirm('Message', + 'Do you really want to delete DVR configuration \'' + + confcombo.getValue() + '\'?', + deleteAction); + } + } + + function deleteAction(btn) { + if (btn == 'yes') { + confpanel.getForm().submit({ + url:'dvr', + params:{'op':'deleteSettings','config_name':confcombo.getValue()}, + waitMsg:'Deleting Data...', + success: function(form, action) { + confcombo.setValue(''); + confcombo.fireEvent('select'); + }, + failure: function(form, action) { + Ext.Msg.alert('Delete failed', action.result.errormsg); + } + }); + } + } + return confpanel; } diff --git a/tvheadend/src/webui/static/app/epg.js b/tvheadend/src/webui/static/app/epg.js index 48c04bf..bc30878 100644 --- a/tvheadend/src/webui/static/app/epg.js +++ b/tvheadend/src/webui/static/app/epg.js @@ -34,6 +34,18 @@ tvheadend.epgDetails = function(event) { 'http://akas.imdb.org/find?q=' + event.title + '">Search IMDB' + var confcombo = new Ext.form.ComboBox({ + store: tvheadend.configNames, + triggerAction: 'all', + mode: 'local', + valueField: 'identifier', + displayField: 'name', + name: 'config_name', + emptyText: '(default)', + value: '', + editable: false + }); + var win = new Ext.Window({ title: event.title, bodyStyle: 'margin: 5px', @@ -42,6 +54,7 @@ tvheadend.epgDetails = function(event) { height: 300, constrainHeader: true, buttons: [ + confcombo, new Ext.Button({ handler: recordEvent, text: "Record program" @@ -56,7 +69,11 @@ tvheadend.epgDetails = function(event) { function recordEvent() { Ext.Ajax.request({ url: 'dvr', - params: {eventId: event.id, op: 'recordEvent'}, + params: { + eventId: event.id, + op: 'recordEvent', + config_name: confcombo.getValue() + }, success:function(response, options) { win.close();