uint32_t de_file_removed;
uint32_t de_retention;
uint32_t de_removal;
+ uint32_t de_playcount; /* Recording play count */
+ uint32_t de_playposition; /* Recording last played position in seconds */
/**
* EPG information / links
const char *desc, const char *lang,
time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
- dvr_prio_t pri, int retention, int removal );
+ dvr_prio_t pri, int retention, int removal,
+ int playcount, int playposition);
void dvr_destroy_by_channel(channel_t *ch, int delconf);
int dvr_entry_verify(dvr_entry_t *de, access_t *a, int readonly);
+void dvr_entry_changed_notify(dvr_entry_t *de);
+
void dvr_spawn_cmd(dvr_entry_t *de, const char *cmd, const char *filename, int pre);
void dvr_vfs_refresh_entry(dvr_entry_t *de);
return 0;
}
+/*
+ *
+ */
+void
+dvr_entry_changed_notify(dvr_entry_t *de)
+{
+ idnode_changed(&de->de_id);
+ htsp_dvr_entry_update(de);
+}
+
/*
*
*/
if (de->de_dont_rerecord ? 1 : 0 != dont_rerecord) {
dvr_entry_trace(de, "don't rerecord change %d", dont_rerecord);
de->de_dont_rerecord = dont_rerecord;
- idnode_changed(&de->de_id);
- idnode_notify_changed(&de->de_id);
- htsp_dvr_entry_update(de);
+ dvr_entry_changed_notify(de);
}
}
#define DVR_UPDATED_BROADCAST (1<<15)
#define DVR_UPDATED_EPISODE (1<<16)
#define DVR_UPDATED_CONFIG (1<<17)
+#define DVR_UPDATED_PLAYPOS (1<<18)
+#define DVR_UPDATED_PLAYCOUNT (1<<19)
static char *dvr_updated_str(char *buf, size_t buflen, int flags)
{
const char *title, const char *subtitle, const char *desc,
const char *lang, time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
- dvr_prio_t pri, int retention, int removal )
+ dvr_prio_t pri, int retention, int removal,
+ int playcount, int playposition)
{
char buf[40];
int save = 0, updated = 0;
updated = 1;
dvr_entry_set_timer(de);
}
+ if (de->de_sched_state == DVR_RECORDING || de->de_sched_state == DVR_COMPLETED) {
+ if (playcount >= 0 && playcount != de->de_playcount) {
+ de->de_playcount = playcount;
+ save |= DVR_UPDATED_PLAYCOUNT;
+ }
+ if (playposition >= 0 && playposition != de->de_playposition) {
+ de->de_playposition = playposition;
+ save |= DVR_UPDATED_PLAYPOS;
+ }
+ }
goto dosave;
}
/* Save changes */
dosave:
if (save) {
- idnode_changed(&de->de_id);
- htsp_dvr_entry_update(de);
+ dvr_entry_changed_notify(de);
if (tvhlog_limit(&de->de_update_limit, 60)) {
tvhinfo(LS_DVR, "\"%s\" on \"%s\": Updated%s (%s)",
lang_str_get(de->de_title, NULL), DVR_CH_NAME(de),
const char *desc, const char *lang,
time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
- dvr_prio_t pri, int retention, int removal )
+ dvr_prio_t pri, int retention, int removal, int playcount, int playposition )
{
return _dvr_entry_update(de, enabled, dvr_config_uuid,
NULL, ch, title, subtitle, desc, lang,
start, stop, start_extra, stop_extra,
- pri, retention, removal);
+ pri, retention, removal, playcount, playposition);
}
/**
gmtime2local(e2->start, t1buf, sizeof(t1buf)),
gmtime2local(e2->stop, t2buf, sizeof(t2buf)));
_dvr_entry_update(de, -1, NULL, e2, NULL, NULL, NULL, NULL, NULL,
- 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0);
+ 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
return;
}
}
if (de->de_bcast != e)
continue;
_dvr_entry_update(de, -1, NULL, e, NULL, NULL, NULL, NULL,
- NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0);
+ NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
found++;
}
if (found == 0) {
epg_broadcast_get_title(e, NULL),
channel_get_name(e->channel));
_dvr_entry_update(de, -1, NULL, e, NULL, NULL, NULL, NULL,
- NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0);
+ NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
break;
}
}
.list = dvr_entry_class_removal_list,
.opts = PO_HIDDEN | PO_ADVANCED | PO_DOC_NLIST,
},
+ {
+ .type = PT_U32,
+ .id = "playposition",
+ .name = N_("Last played position"),
+ .desc = N_("Last played position when the recording isn't fully watched yet."),
+ .off = offsetof(dvr_entry_t, de_playposition),
+ .def.i = 0,
+ .opts = PO_HIDDEN | PO_NOUI | PO_DOC_NLIST,
+ },
+ {
+ .type = PT_U32,
+ .id = "playcount",
+ .name = N_("Recording play count"),
+ .desc = N_("Number of times this recording was played."),
+ .off = offsetof(dvr_entry_t, de_playcount),
+ .def.i = 0,
+ .opts = PO_HIDDEN | PO_EXPERT | PO_DOC_NLIST,
+ },
{
.type = PT_STR,
.id = "config_name",
htsmsg_add_u32(out, "priority", de->de_pri);
htsmsg_add_u32(out, "contentType", de->de_content_type);
+ if (de->de_sched_state == DVR_RECORDING || de->de_sched_state == DVR_COMPLETED) {
+ htsmsg_add_u32(out, "playcount", de->de_playcount);
+ htsmsg_add_u32(out, "playposition", de->de_playposition);
+ }
+
if(de->de_title && (s = lang_str_get(de->de_title, lang)))
htsmsg_add_str(out, "title", s);
if(de->de_subtitle && (s = lang_str_get(de->de_subtitle, lang)))
htsmsg_t *out = NULL;
uint32_t u32;
dvr_entry_t *de;
- time_t start, stop, start_extra, stop_extra, priority, retention, removal;
+ time_t start, stop, start_extra, stop_extra, priority;
const char *dvr_config_name, *title, *subtitle, *desc, *lang;
channel_t *channel = NULL;
- int enabled;
+ int enabled, retention, removal, playcount = -1, playposition = -1;
de = htsp_findDvrEntry(htsp, in, &out, 0);
if (de == NULL)
desc = htsmsg_get_str(in, "description");
lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
+ if(!htsmsg_get_u32(in, "playcount", &u32))
+ playcount = u32 > INT_MAX ? INT_MAX : u32;
+ if(!htsmsg_get_u32(in, "playposition", &u32))
+ playposition = u32 > INT_MAX ? INT_MAX : u32;
+
de = dvr_entry_update(de, enabled, dvr_config_name, channel, title, subtitle,
desc, lang, start, stop, start_extra, stop_extra,
- priority, retention, removal);
+ priority, retention, removal, playcount, playposition);
return htsp_success();
}
titleP: _('Finished Recordings'),
iconCls: 'finishedRec',
tabIndex: index,
- edit: { params: { list: tvheadend.admin ? "retention,removal,owner,comment" : "retention,removal,comment" } },
+ edit: { params: { list: tvheadend.admin ? "playcount,retention,removal,owner,comment" : "retention,removal,comment" } },
del: false,
list: 'disp_title,disp_subtitle,episode,channelname,' +
'start_real,stop_real,duration,filesize,' +
- 'sched_status,errors,data_errors,url,config_name,owner,creator,comment',
+ 'sched_status,errors,data_errors,playcount,url,config_name,owner,creator,comment',
columns: {
filesize: {
renderer: tvheadend.filesizeRenderer()
titleP: _('Failed Recordings'),
iconCls: 'exclamation',
tabIndex: index,
- edit: { params: { list: tvheadend.admin ? "retention,removal,owner,comment" : "retention,removal,comment" } },
+ edit: { params: { list: tvheadend.admin ? "playcount,retention,removal,owner,comment" : "retention,removal,comment" } },
del: true,
delquestion: _('Do you really want to delete the selected recordings?') + '<br/><br/>' +
_('The associated file will be removed from storage.'),
list: 'disp_title,disp_subtitle,episode,channelname,' +
'start_real,stop_real,duration,filesize,status,' +
- 'sched_status,errors,data_errors,url,config_name,owner,creator,comment',
+ 'sched_status,errors,data_errors,playcount,url,config_name,owner,creator,comment',
columns: {
filesize: {
renderer: tvheadend.filesizeRenderer()
return HTTP_STATUS_UNAUTHORIZED;
}
+ /* Play count + 1 when write access */
+ if (!dvr_entry_verify(de, hc->hc_access, 0)) {
+ de->de_playcount = de->de_playcount + 1;
+ dvr_entry_changed_notify(de);
+ }
+
fname = tvh_strdupa(filename);
content = muxer_container_filename2mime(fname, 1);
charset = de->de_config ? de->de_config->dvr_charset_id : NULL;