<dt>Media container
<dd>Select the container format used to store recordings.
- <dt>DVR Log retention time (days)
- <dd>Time that Tvheadend will keep information about the recording in
- its internal database. Notice that the actual recorded file will not
- be deleted when the log entry is deleted.
-
- <dt>Extra time before recordings (minutes)
- <dd>Specify the number of minutes to record before the events scheduled
- start time. Used to cope with small scheduling errors.
-
- <dt>Extra time after recordings (minutes)
- <dd>Specify the number of minutes to record after the events scheduled
- stop time. Used to cope with small scheduling errors.
+ <dt>Cache scheme
+ <dd>Select the cache scheme used to store recordings. Leave as "system" unless you have a special case for one of the others.
+ <br><br>
+ <dd>Whenever you read or write data to the filesystems, the information is kept (cached) in memory for a while. This means that regularly-access files are available quickly without going back to the disc; it also means that there's a disconnect when writing between the write request (from the application) and the actual write itself (to the disc/storage) as changes are buffered to be written in one go.</dd>
- <dt>Make sub-directories per day
- <dd>If checked, Tvheadend will create a new directory per day in the
- recording system path. Only days when anything is recorded will be
- created. The format of the directory will be 'YYYY-MM-DD' (ISO standard)
+ <dl>
- <dt>Make sub-directories per channel
- <dd>If checked, Tvheadend will create a directory per channel when storing
- events. If both this and the 'directory per day' checkbox is enabled,
- the date-directory will be parent to the per-channel directory.
+ <dt>Unknown</dt>
+ <dd>A placeholder status, meaning that the configuration isn't properly set.</dd>
- <dt>Make sub-directories per title
- <dd>If checked, Tvheadend will create a directory per title when storing
- events. If the day/channel directory checkboxes are also enabled, those
- directories will be parents of this directory.
+ <dt>System</dt>
+ <dd>Change nothing and rely on standard (default) system caching to behave as it normally would.</dd>
- <dt>Include channel name in title
- <dd>If checked, Tvheadend will include the name of the channel in the
- event title. This applies to both the titled stored in the file
- and to the file name itself.
+ <dt>Do not keep</dt>
+ <dd>Tell the system that you're not expecting to re-use the data soon, so don't keep it in cache. The data will still be buffered for writing. <i>Useful e.g. in a RAM-limited system like a Pi (given that you're unlikely to be watching while recording, so data can be discarded now and read back from disc later).</i></dd>
- <dt>Include date in title
- <dd>If checked, Tvheadend will include the date for the recording in the
- event title. This applies to both the titled stored in the file
- and to the file name itself.
+ <dt>Sync</dt>
+ <dd>Tell the system to write the data immediately. This doesn't affect whether or not it's cached. <i>Useful e.g. if you've a particular problem with data loss due to delayed write (such as if you get frequent transient power problems).</i></dd>
- <dt>Include time in title
- <dd>If checked, Tvheadend will include the time for the recording in the
- event title. This applies to both the titled stored in the file
- and to the file name itself.
+ <dt>Sync + Do not keep</dt>
+ <dd>A combination of last two variants above - data is written immediately and then discarded from cache.</dd>
- <dt>Include episode in title
- <dd>If checked, Tvheadend will include the season and episode in the
- title (if such info is available).
+ </dl>
+
+ <dt>DVR Log retention time (days)
+ <dd>Time that Tvheadend will keep information about the recording in its internal database. Notice that the actual recorded file will not be deleted when the log entry is deleted.
- <dt>Remove all unsafe characters from filename
- <dd>If checked, all characters that could possibly cause problems for
- filenaming will be removed.
-
- <dt>Replace whitespace in title with '-'
- <dd>If checked, all whitespace characters will be replaced with '-'.
-
- <dt>Tag files with metadata
- <dd>If checked, media containers that support metadata will be tagged with
- the metadata associated with the event being recorded.
+ <dt>Extra time before recordings (minutes)
+ <dd>Specify the number of minutes to record before the events scheduled start time. Used to cope with small scheduling errors.
- <dt>Skip commercials
- <dd>If checked, commercials will be dropped from the recordings. At the
- moment, commercial detection only works for the swedish channel TV4.
+ <dt>Extra time after recordings (minutes)
+ <dd>Specify the number of minutes to record after the events scheduled stop time. Used to cope with small scheduling errors.
+ <dt>Episode duplicate detection
+ <dd>If checked, broadcasts with matching title and matching non-zero episode number
+ are considered duplicates.
+
<dt>Post-processor command
- <dd>Command to run after finishing a recording. The command will be
- run in background and is executed even if a recording is aborted
- or an error occurred. Use the %e error formatting string to check
- for errors, the error string is empty if recording finished
- successfully.
+ <dd>Command to run after finishing a recording. The command will be run in background and is executed even if a recording is aborted or an error occurred. Use the %e error formatting string to check for errors, the error string is empty if recording finished successfully.
<br><br>
Support format strings:<br>
<table class="hts-doc-text" border="0">
*/
tvheadend.dvrsettings = function() {
- var confreader = new Ext.data.JsonReader({
- root : 'dvrSettings'
- }, [ 'storage', 'postproc', 'retention', 'dayDirs', 'channelDirs',
- 'channelInTitle', 'container', 'dateInTitle', 'timeInTitle',
- 'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs',
- 'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'episodeDuplicateDetection' ]);
-
- 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',
- border : false,
- bodyStyle : 'padding:15px',
- anchor : '100% 50%',
- labelAlign : 'right',
- labelWidth : 250,
- waitMsgTarget : true,
- reader : confreader,
- defaultType : 'textfield',
- layout : 'form',
- items : [ {
- width : 300,
- fieldLabel : 'Recording system path',
- name : 'storage'
- }, new Ext.form.ComboBox({
- store : tvheadend.containers,
- fieldLabel : 'Media container',
- triggerAction : 'all',
- displayField : 'description',
- valueField : 'name',
- editable : false,
- width : 200,
- hiddenName : 'container'
- }), new Ext.form.NumberField({
- allowNegative : false,
- allowDecimals : false,
- minValue : 1,
- fieldLabel : 'DVR Log retention time (days)',
- name : 'retention'
- }), new Ext.form.NumberField({
- allowDecimals : false,
- fieldLabel : 'Extra time before recordings (minutes)',
- name : 'preExtraTime'
- }), new Ext.form.NumberField({
- allowDecimals : false,
- fieldLabel : 'Extra time after recordings (minutes)',
- name : 'postExtraTime'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Make subdirectories per day',
- name : 'dayDirs'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Make subdirectories per channel',
- name : 'channelDirs'
- }), new Ext.form.Checkbox({
- 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 filename',
- name : 'dateInTitle'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Include time in filename',
- name : 'timeInTitle'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Include episode in filename',
- name : 'episodeInTitle'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Remove all unsafe characters from filename',
- name : 'cleanTitle'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Replace whitespace in title with \'-\'',
- name : 'whitespaceInTitle'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Tag files with metadata',
- name : 'tagFiles'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Skip commercials',
- name : 'commSkip'
- }), new Ext.form.Checkbox({
- fieldLabel : 'Episode duplicate detection',
- name : 'episodeDuplicateDetection'
- }), {
- width : 300,
- fieldLabel : 'Post-processor command',
- name : 'postproc'
- } ],
- 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');
- }
- } ]
- });
-
- function loadConfig() {
- confpanel.getForm().load({
- url : 'dvr',
- 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',
- '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;
-}
+ var confreader = new Ext.data.JsonReader({
+ root: 'dvrSettings'
+ }, ['storage', 'filePermissions', 'dirPermissions', 'postproc', 'retention', 'dayDirs', 'channelDirs',
+ 'channelInTitle', 'container', 'cache', 'charset', 'dateInTitle', 'timeInTitle',
+ 'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs',
+ 'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle',
- 'episodeBeforeDate', 'rewritePAT', 'rewritePMT']);
++ 'episodeBeforeDate', 'rewritePAT', 'rewritePMT', 'episodeDuplicateDetection']);
+
+ 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
+ });
+
+ /* Config panel variables */
+
+ /* DVR Behaviour */
+
+ var recordingContainer = new Ext.form.ComboBox({
+ store: tvheadend.containers,
+ fieldLabel: 'Media container',
+ triggerAction: 'all',
+ displayField: 'description',
+ valueField: 'name',
+ editable: false,
+ width: 350,
+ hiddenName: 'container'
+ });
+
+ var cacheScheme = new Ext.form.ComboBox({
+ store: tvheadend.caches,
+ fieldLabel: 'Cache scheme',
+ triggerAction: 'all',
+ displayField: 'description',
+ valueField: 'index',
+ editable: false,
+ width: 350,
+ hiddenName: 'cache'
+ });
+
+ var logRetention = new Ext.form.NumberField({
+ allowNegative: false,
+ allowDecimals: false,
+ minValue: 1,
+ fieldLabel: 'DVR Log retention time (days)',
+ name: 'retention'
+ });
+
+ var timeBefore = new Ext.form.NumberField({
+ allowDecimals: false,
+ fieldLabel: 'Extra time before recordings (minutes)',
+ name: 'preExtraTime'
+ });
+
+ var timeAfter = new Ext.form.NumberField({
+ allowDecimals: false,
+ fieldLabel: 'Extra time after recordings (minutes)',
+ name: 'postExtraTime'
+ });
+
+ var postProcessing = new Ext.form.TextField({
+ width: 350,
+ fieldLabel: 'Post-processor command',
+ name: 'postproc'
+ });
+
+ /* Recording File Options */
+
+ var recordingPath = new Ext.form.TextField({
+ width: 350,
+ fieldLabel: 'Recording system path',
+ name: 'storage'
+ });
+
+ /* NB: recordingPermissions is defined as a TextField for validation purposes (leading zeros), but is ultimately a number */
+
+ var recordingPermissions = new Ext.form.TextField({
+ regex: /^[0][0-7]{3}$/,
+ maskRe: /[0-7]/,
+ width: 125,
+ allowBlank: false,
+ blankText: 'You must provide a value - use octal chmod notation, e.g. 0664',
+ fieldLabel: 'File permissions (octal, e.g. 0664)',
+ name: 'filePermissions'
+ });
+
+ var charset = new Ext.form.ComboBox({
+ store: tvheadend.charsets,
+ fieldLabel: 'Filename charset',
+ triggerAction: 'all',
+ displayField: 'val',
+ valueField: 'key',
+ editable: false,
+ width: 200,
+ hiddenName: 'charset'
+ });
+
+ /* TO DO - Add 'override user umask?' option, then trigger fchmod in mkmux.c, muxer_pass.c after file created */
+
+ var PATrewrite = new Ext.form.Checkbox({
+ fieldLabel: 'Rewrite PAT in passthrough mode',
+ name: 'rewritePAT'
+ });
+
+ var PMTrewrite = new Ext.form.Checkbox({
+ fieldLabel: 'Rewrite PMT in passthrough mode',
+ name: 'rewritePMT'
+ });
+
+ var tagMetadata = new Ext.form.Checkbox({
+ fieldLabel: 'Tag files with metadata',
+ name: 'tagFiles'
+ });
+
+ var skipCommercials = new Ext.form.Checkbox({
+ fieldLabel: 'Skip commercials',
+ name: 'commSkip'
+ });
+
++ var episodeDuplicateDetection = new Ext.form.Checkbox({
++ fieldLabel: 'Episode Duplicate Detect',
++ name: 'episodeDuplicateDetection'
++ });
++
+ /* Subdirectories and filename handling */
+
+ /* NB: directoryPermissions is defined as a TextField for validation purposes (leading zeros), but is ultimately a number */
+
+ var directoryPermissions = new Ext.form.TextField({
+ regex: /^[0][0-7]{3}$/,
+ maskRe: /[0-7]/,
+ width: 125,
+ allowBlank: false,
+ blankText: 'You must provide a value - use octal chmod notation, e.g. 0775',
+ fieldLabel: 'Directory permissions (octal, e.g. 0775)',
+ name: 'dirPermissions'
+ });
+
+ /* TO DO - Add 'override user umask?' option, then trigger fchmod in utils.c after directory created */
+
+ var dirsPerDay = new Ext.form.Checkbox({
+ fieldLabel: 'Make subdirectories per day',
+ name: 'dayDirs'
+ });
+
+ var dirsPerChannel = new Ext.form.Checkbox({
+ fieldLabel: 'Make subdirectories per channel',
+ name: 'channelDirs'
+ });
+
+ var dirsPerTitle = new Ext.form.Checkbox({
+ fieldLabel: 'Make subdirectories per title',
+ name: 'titleDirs'
+ });
+
+ var incChannelInTitle = new Ext.form.Checkbox({
+ fieldLabel: 'Include channel name in filename',
+ name: 'channelInTitle'
+ });
+
+ var incDateInTitle = new Ext.form.Checkbox({
+ fieldLabel: 'Include date in filename',
+ name: 'dateInTitle'
+ });
+
+ var incTimeInTitle = new Ext.form.Checkbox({
+ fieldLabel: 'Include time in filename',
+ name: 'timeInTitle'
+ });
+
+ var incEpisodeInTitle = new Ext.form.Checkbox({
+ fieldLabel: 'Include episode in filename',
+ name: 'episodeInTitle'
+ });
+
+ var incSubtitleInTitle = new Ext.form.Checkbox({
+ fieldLabel: 'Include subtitle in filename',
+ name: 'subtitleInTitle'
+ });
+
+ var episodeFirst = new Ext.form.Checkbox({
+ fieldLabel: 'Put episode in filename before date and time',
+ name: 'episodeBeforeDate'
+ });
+
+ var stripUnsafeChars = new Ext.form.Checkbox({
+ fieldLabel: 'Remove all unsafe characters from filename',
+ name: 'cleanTitle'
+ });
+
+ var stripWhitespace = new Ext.form.Checkbox({
+ fieldLabel: 'Replace whitespace in title with \'-\'',
+ name: 'whitespaceInTitle'
+ });
+
+ /* Sub-Panel - DVR behaviour */
+
+ var DVRBehaviour = new Ext.form.FieldSet({
+ title: 'DVR Behaviour',
+ width: 700,
+ autoHeight: true,
+ collapsible: true,
+ animCollapse: true,
+ items: [recordingContainer, cacheScheme, logRetention, timeBefore, timeAfter, postProcessing]
+ });
+
+ /* Sub-Panel - File Output */
+
+ var FileOutputPanel = new Ext.form.FieldSet({
+ title: 'Recording File Options',
+ width: 700,
+ autoHeight: true,
+ collapsible: true,
+ animCollapse: true,
- items: [recordingPath, recordingPermissions, charset, PATrewrite, PMTrewrite, tagMetadata, skipCommercials]
++ items: [recordingPath, recordingPermissions, charset, PATrewrite, PMTrewrite, tagMetadata, skipCommercials, episodeDuplicateDetection]
+ });
+
+ /* Sub-Panel - Directory operations */
+
+ var DirHandlingPanel = new Ext.form.FieldSet({
+ title: 'Subdirectory Options',
+ width: 700,
+ autoHeight: true,
+ collapsible: true,
+ animCollapse: true,
+ items: [directoryPermissions, dirsPerDay, dirsPerChannel, dirsPerTitle]
+ });
+
+ /* Sub-Panel - File operations - Break into two 4-item panels */
+
+ var FileHandlingPanelA = new Ext.form.FieldSet({
+ width: 350,
+ border: false,
+ autoHeight: true,
+ items : [incChannelInTitle, incDateInTitle, incTimeInTitle, incEpisodeInTitle]
+ });
+
+ var FileHandlingPanelB = new Ext.form.FieldSet({
+ width: 350,
+ border: false,
+ autoHeight: true,
+ items : [incSubtitleInTitle, episodeFirst, stripUnsafeChars, stripWhitespace]
+ });
+
+ var FileHandlingPanel = new Ext.form.FieldSet({
+ title: 'Filename Options',
+ width: 700,
+ autoHeight: true,
+ collapsible: true,
+ animCollapse : true,
+ items : [{
+ layout: 'column',
+ border: false,
+ items : [FileHandlingPanelA, FileHandlingPanelB]
+ }]
+ });
+
+ /* Main (form) panel */
+
+ var confpanel = new Ext.FormPanel({
+ title: 'Digital Video Recorder',
+ iconCls: 'drive',
+ border: false,
+ bodyStyle: 'padding:15px',
+ anchor: '100% 50%',
+ labelAlign: 'right',
+ labelWidth: 300,
+ autoScroll: true,
+ waitMsgTarget: true,
+ reader: confreader,
+ defaultType: 'textfield',
+ layout: 'form',
+ items: [DVRBehaviour, FileOutputPanel, DirHandlingPanel, FileHandlingPanel],
+ 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');
+ }
+ }]
+ });
+
+ function loadConfig() {
+ confpanel.getForm().load({
+ url: 'dvr',
+ 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',
+ '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;
+};