*/
#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <assert.h>
#include <string.h>
char *dvr_storage;
char *dvr_format;
char *dvr_file_postfix;
+uint32_t dvr_retention_days;
static int de_tally;
-static int dvr_retention_time = 86400 * 31;
struct dvr_entry_list dvrentries;
-static void dvr_save(dvr_entry_t *de);
+static void dvr_entry_save(dvr_entry_t *de);
static void dvr_timer_expire(void *aux);
static void dvr_timer_start_recording(void *aux);
if(now >= de->de_stop || de->de_dont_reschedule) {
de->de_sched_state = DVR_COMPLETED;
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
- de->de_stop + dvr_retention_time);
+ de->de_stop + dvr_retention_days * 86400);
} else {
de->de_sched_state = DVR_SCHEDULED;
de->de_title, de->de_channel->ch_name, tbuf, creator);
dvrdb_changed();
- dvr_save(de);
+ dvr_entry_save(de);
}
*
*/
static void
-dvr_save(dvr_entry_t *de)
+dvr_entry_save(dvr_entry_t *de)
{
htsmsg_t *m = htsmsg_create();
de->de_error ?: "Program ended");
dvrdb_changed();
- dvr_save(de);
+ dvr_entry_save(de);
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
- de->de_stop + dvr_retention_time);
+ de->de_stop + dvr_retention_days * 86400);
}
void
dvr_init(void)
{
- dvr_storage = strdup("/home/andoma/media/dvr");
+ htsmsg_t *m;
+ char buf[500];
+ const char *homedir;
+ struct stat st;
+
+ /* Default settings */
+
+ dvr_retention_days = 31;
dvr_format = strdup("matroska");
dvr_file_postfix = strdup("mkv");
+ /* Override settings with config */
+
+ if((m = hts_settings_load("dvr")) != NULL) {
+ htsmsg_get_u32(m, "retention-days", &dvr_retention_days);
+ tvh_str_set(&dvr_storage, htsmsg_get_str(m, "storage"));
+
+ htsmsg_destroy(m);
+ }
+
+ 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)));
+ }
+
+ fprintf(stderr,
+ "\nNotice: Digital Video Recorder\n");
+ fprintf(stderr,
+ " Output directory for video recording is not yet configured.\n");
+ fprintf(stderr,
+ " Defaulting to to \"%s\".\n", dvr_storage);
+ fprintf(stderr,
+ " This can be changed from the web user interface.\n");
+ }
+
dvr_db_load();
}
+/**
+ *
+ */
+static void
+dvr_save(void)
+{
+ htsmsg_t *m = htsmsg_create();
+ htsmsg_add_str(m, "storage", dvr_storage);
+ htsmsg_add_u32(m, "retention-days", dvr_retention_days);
+
+ hts_settings_save(m, "dvr");
+ htsmsg_destroy(m);
+}
+
+/**
+ *
+ */
+void
+dvr_storage_set(const char *storage)
+{
+ if(!strcmp(dvr_storage, storage))
+ return;
+
+ tvh_str_set(&dvr_storage, storage);
+ dvr_save();
+}
+
+
+/**
+ *
+ */
+void
+dvr_retention_set(int days)
+{
+ dvr_entry_t *de;
+ if(days < 1 || dvr_retention_days == days)
+ return;
+
+ 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();
+}
+
/**
*
{
htsbuf_queue_t *hq = &hc->hc_reply;
const char *op = http_arg_get(&hc->hc_req_args, "op");
- htsmsg_t *out;
+ htsmsg_t *out, *r;
event_t *e;
dvr_entry_t *de;
const char *s;
+ if(op == NULL)
+ op = "loadSettings";
+
pthread_mutex_lock(&global_lock);
if(!strcmp(op, "recordEvent")) {
out = htsmsg_create();
htsmsg_add_u32(out, "success", 1);
+
+ } else if(!strcmp(op, "loadSettings")) {
+
+ r = htsmsg_create();
+ htsmsg_add_str(r, "storage", dvr_storage);
+ htsmsg_add_u32(r, "retention", dvr_retention_days);
+ out = json_single_record(r, "dvrSettings");
+
+ } else if(!strcmp(op, "saveSettings")) {
+
+ if((s = http_arg_get(&hc->hc_req_args, "storage")) != NULL)
+ dvr_storage_set(s);
+
+ if((s = http_arg_get(&hc->hc_req_args, "retention")) != NULL)
+ dvr_retention_set(atoi(s));
+
+
+ out = htsmsg_create();
+ htsmsg_add_u32(out, "success", 1);
+
} else {
pthread_mutex_unlock(&global_lock);
return panel;
}
+
+
+/**
+ * Configuration panel (located under configuration)
+ */
+tvheadend.dvrsettings = function() {
+
+ var confreader = new Ext.data.JsonReader({
+ root: 'dvrSettings',
+ }, ['storage','retention']);
+
+
+ var confpanel = new Ext.FormPanel({
+ title:'Digital Video Recorder',
+ border:false,
+ bodyStyle:'padding:15px',
+ anchor: '100% 50%',
+ labelAlign: 'right',
+ labelWidth: 150,
+ waitMsgTarget: true,
+ reader: confreader,
+ defaultType: 'textfield',
+ layout: 'form',
+ items: [{
+ width: 300,
+ fieldLabel: 'Recording system path',
+ name: 'storage'
+ }, new Ext.form.NumberField({
+ allowNegative: false,
+ allowDecimals: false,
+ minValue: 1,
+ fieldLabel: 'Retention time (days)',
+ name: 'retention'
+ })],
+ tbar: [{
+ tooltip: 'Save changes made to channel configuration below',
+ iconCls:'save',
+ text: "Save configuration",
+ handler: saveChanges
+ }],
+
+ });
+
+ confpanel.on('render', function() {
+ confpanel.getForm().load({
+ url:'/dvr',
+ params:{'op':'loadSettings'},
+ success:function(form, action) {
+ confpanel.enable();
+ }
+ });
+ });
+
+
+ function saveChanges() {
+ confpanel.getForm().submit({
+ url:'/dvr',
+ params:{'op':'saveSettings'},
+ waitMsg:'Saving Data...',
+ failure: function(form, action) {
+ Ext.Msg.alert('Save failed', action.result.errormsg);
+ }
+ });
+ }
+
+ return confpanel;
+}
+