#define DVR_FILESIZE_TOTAL (1<<1)
#define DVR_FINISHED_ALL (1<<0)
-#define DVR_FINISHED_SUCCESS (1<<1)
-#define DVR_FINISHED_FAILED (1<<2)
+#define DVR_FINISHED_SUCCESS (1<<1)
+#define DVR_FINISHED_FAILED (1<<2)
#define DVR_FINISHED_REMOVED_SUCCESS (1<<3) /* Removed recording, was succesful before */
#define DVR_FINISHED_REMOVED_FAILED (1<<4) /* Removed recording, was failed before */
*/
LIST_ENTRY(dvr_entry) de_global_link;
-
+
channel_t *de_channel;
LIST_ENTRY(dvr_entry) de_channel_link;
char *de_image; /* Programme Image */
char *de_fanart_image; /* Programme fanart image */
htsmsg_t *de_files; /* List of all used files */
- char *de_directory; /* Can be set for autorec entries, will override any
+ char *de_directory; /* Can be set for autorec entries, will override any
directory setting from the configuration */
lang_str_t *de_title; /* Title in UTF-8 (from EPG) */
lang_str_t *de_subtitle; /* Subtitle in UTF-8 (from EPG) */
uint32_t de_content_type; /* Content type (from EPG) (only code) */
uint16_t de_copyright_year; /* Copyright year (from EPG) */
uint16_t de_dvb_eid;
+ uint16_t de_age_rating; /* Age rating (from EPG) */
int de_pri;
int de_dont_reschedule;
* Last error, see SM_CODE_ defines
*/
uint32_t de_last_error;
-
+
/**
* Autorec linkage
char *dae_title;
tvh_regex_t dae_title_regex;
int dae_fulltext;
-
+
uint32_t dae_content_type;
/* These categories (mainly from xmltv) such as Cooking, Dog racing, Movie.
* This allows user to easily do filtering such as '"Movie" "Martial arts"'
time_t dae_start_extra;
time_t dae_stop_extra;
-
+
int dae_record;
-
+
} dvr_autorec_entry_t;
extern struct dvr_autorec_entry_queue autorec_entries;
time_t start, time_t stop,
time_t start_extra, time_t stop_extra,
dvr_prio_t pri, int retention, int removal,
- int playcount, int playposition);
+ int playcount, int playposition, int age_rating);
void dvr_destroy_by_channel(channel_t *ch, int delconf);
genre = LIST_FIRST(&e->genre);
if (genre)
htsmsg_add_u32(conf, "content_type", genre->code / 16);
+ if(e->age_rating)
+ htsmsg_add_u32(conf, "age_rating", e->age_rating);
}
de = dvr_entry_create(NULL, conf, 0);
if (svf == PROFILE_SVF_UHD && !old_has_svf) {
old_has_svf = channel_has_correct_service_filter(old_channel, PROFILE_SVF_FHD);
new_has_svf = channel_has_correct_service_filter(new_channel, PROFILE_SVF_FHD);
-
+
if (!old_has_svf && new_has_svf)
return 1;
#define DVR_UPDATED_CONFIG (1<<17)
#define DVR_UPDATED_PLAYPOS (1<<18)
#define DVR_UPDATED_PLAYCOUNT (1<<19)
+#define DVR_UPDATED_AGE_RATING (1<<20)
static char *dvr_updated_str(char *buf, size_t buflen, int flags)
{
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,
- int playcount, int playposition)
+ int playcount, int playposition, int age_rating)
{
char buf[40];
int save = 0, updated = 0;
updated = 1;
dvr_entry_set_timer(de);
}
+ /* Manual Age Rating */
+ if (age_rating != de->de_age_rating) {
+ de->de_age_rating = age_rating;
+ save |= DVR_UPDATED_AGE_RATING;
+ }
/* Title */
if (e && e->title) {
save |= DVR_UPDATED_EID;
}
+ /* Age Rating from EPG*/
+ if (e && e->age_rating != de->de_age_rating) {
+ de->de_age_rating = e->age_rating;
+ save |= DVR_UPDATED_AGE_RATING;
+ }
+
/* Description */
if (e && e->description) {
save |= lang_str_set2(&de->de_desc, e->description) ? DVR_UPDATED_DESCRIPTION : 0;
const char *summary, 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, int playcount, int playposition )
+ dvr_prio_t pri, int retention, int removal, int playcount, int playposition,
+ int age_rating )
{
return _dvr_entry_update(de, enabled, dvr_config_uuid,
NULL, ch, title, subtitle, summary, desc, lang,
start, stop, start_extra, stop_extra,
- pri, retention, removal, playcount, playposition);
+ pri, retention, removal, playcount, playposition,
+ age_rating);
}
/**
gmtime2local(e2->start, t1buf, sizeof(t1buf)),
gmtime2local(e2->stop, t2buf, sizeof(t2buf)));
_dvr_entry_update(de, -1, NULL, e2, NULL, NULL, NULL, NULL, NULL,
- NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
+ NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1, 0);
return;
}
}
assert(de->de_bcast == e);
if (de->de_sched_state != DVR_SCHEDULED) continue;
_dvr_entry_update(de, -1, NULL, e, NULL, NULL, NULL, NULL, NULL,
- NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
+ NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1, 0);
}
LIST_FOREACH(de, &e->channel->ch_dvrs, de_channel_link) {
if (de->de_sched_state != DVR_SCHEDULED) continue;
epg_broadcast_get_title(e, NULL),
channel_get_name(e->channel, channel_blank_name));
_dvr_entry_update(de, -1, NULL, e, NULL, NULL, NULL, NULL, NULL,
- NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1);
+ NULL, 0, 0, 0, 0, DVR_PRIO_NOTSET, 0, 0, -1, -1, 0);
}
}
}
{
.type = PT_U16,
.id = "copyright_year",
- .name = N_("The copyright year of the program."),
+ .name = N_("Copyright year"),
.desc = N_("The copyright year of the program."),
.off = offsetof(dvr_entry_t, de_copyright_year),
.opts = PO_RDONLY | PO_EXPERT,
.get = dvr_entry_class_genre_get,
.opts = PO_RDONLY | PO_NOSAVE,
},
+ {
+ .type = PT_U16,
+ .id = "age_rating",
+ .name = N_("Age Rating"),
+ .desc = N_("The age rating of the program."),
+ .off = offsetof(dvr_entry_t, de_age_rating),
+ .opts = PO_RDONLY | PO_EXPERT,
+ },
{}
}
};
if(pb != NULL)
pktbuf_ref_inc(pb);
hm->hm_payloadsize = payloadsize;
-
+
tvh_mutex_lock(&htsp->htsp_out_mutex);
assert(!hmq->hmq_dead);
htsp_send(htsp, m, NULL, hmq ?: &htsp->htsp_hmq_ctrl, 0);
}
-/**
+/**
* Simple function to respond with an error
*/
static htsmsg_t *
if((fd = tvh_open("/dev/urandom", O_RDONLY, 0)) < 0)
return -1;
-
+
n = read(fd, &htsp->htsp_challenge, 32);
close(fd);
return n != 32;
idnode_list_mapping_t *ilm;
htsmsg_t *out = htsmsg_create_map();
htsmsg_t *members = include_channels ? htsmsg_create_list() : NULL;
-
+
htsmsg_add_u32(out, "tagId", htsp_channel_tag_get_identifier(ct));
htsmsg_add_u32(out, "tagIndex", ct->ct_index);
htsmsg_add_u32(out, "priority", u32);
htsmsg_add_u32(out, "contentType", de->de_content_type);
+ //To not risk breaking older clients, only
+ //provide the 'age rating' via HTSP if the requested
+ //API version if greater than 35.
+ if (htsp->htsp_version > 35)
+ {
+ htsmsg_add_u32(out, "ageRating", de->de_age_rating);
+ }
+
+
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);
htsmsg_add_str(r, "uilanguage", htsp->htsp_granted_access->aa_lang_ui ?
htsp->htsp_granted_access->aa_lang_ui : (config.language_ui ? config.language_ui : ""));
}
-
+
return r;
}
if (dvr_get_disk_space(&bfree, &bused, &btotal))
return htsp_error(htsp, N_("Unable to stat path"));
-
+
out = htsmsg_create_map();
htsmsg_add_s64(out, "freediskspace", bfree);
htsmsg_add_s64(out, "useddiskspace", bused);
}
/* First, just OK the async request */
- htsp_reply(htsp, in, htsmsg_create_map());
+ htsp_reply(htsp, in, htsmsg_create_map());
/* Set epg */
if(epg)
TAILQ_FOREACH(ct, &channel_tags, ct_link)
if(channel_tag_access(ct, htsp->htsp_granted_access, 0))
htsp_send_message(htsp, htsp_build_tag(htsp, ct, "tagAdd", 0), NULL);
-
+
/* Send all channels */
CHANNEL_FOREACH(ch)
if (htsp_user_access_channel(htsp,ch))
htsp_send_message(htsp, htsp_build_channel(ch, "channelAdd", htsp), NULL);
-
+
/* Send all enabled and external tags (now with channel mappings) */
TAILQ_FOREACH(ct, &channel_tags, ct_link)
if(channel_tag_access(ct, htsp->htsp_granted_access, 0))
uint32_t eventId;
epg_broadcast_t *e;
const char *lang;
-
+
if(htsmsg_get_u32(in, "eventId", &eventId))
return htsp_error(htsp, N_("Invalid arguments"));
lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
}
/**
- * Get information about the given event +
+ * Get information about the given event +
* n following events
*/
static htsmsg_t *
}
}
-
+
/* Send */
out = htsmsg_create_map();
htsmsg_add_msg(out, "events", events);
if(htsmsg_get_bool_or_default(in, "fulltext", 0))
eq.fulltext = 1;
eq.stitle = strdup(query);
-
+
/* Optional */
if(!(htsmsg_get_u32(in, "channelId", &u32))) {
if (!(ch = channel_find_by_id(u32)))
}
htsmsg_add_msg(out, full ? "events" : "eventIds", array);
}
-
+
epg_query_free(&eq);
-
+
return out;
}
/**
* add a Dvrentry
*/
-static htsmsg_t *
+static htsmsg_t *
htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
{
htsmsg_t *conf, *out;
s = htsmsg_get_str(in, "description");
if (s)
lang_str_serialize_one(conf, "description", s, lang);
+ u32 = htsmsg_get_u32_or_default(in, "ageRating", 0);
+ if(u32)
+ htsmsg_add_u32(conf, "age_rating", u32);
}
/* Create the dvr entry */
htsmsg_destroy(conf);
dvr_status = de != NULL ? de->de_sched_state : DVR_NOSTATE;
-
+
/* Create response */
out = htsmsg_create_map();
-
+
switch(dvr_status) {
case DVR_SCHEDULED:
case DVR_RECORDING:
const char *dvr_config_name, *title, *subtitle, *summary, *desc, *lang;
channel_t *channel = NULL;
int enabled, retention, removal, playcount = -1, playposition = -1;
+ int age_rating;
de = htsp_findDvrEntry(htsp, in, &out, 0);
if (de == NULL)
return out;
-
+
if(!htsmsg_get_u32(in, "channelId", &u32))
channel = channel_find_by_id(u32);
if (!channel)
retention = htsmsg_get_u32_or_default(in, "retention", DVR_RET_REM_DVRCONFIG);
removal = htsmsg_get_u32_or_default(in, "removal", DVR_RET_REM_DVRCONFIG);
priority = htsmsg_get_u32_or_default(in, "priority", DVR_PRIO_NOTSET);
+ age_rating = htsmsg_get_u32_or_default(in, "ageRating", 0);
title = htsmsg_get_str(in, "title");
subtitle = htsmsg_get_str(in, "subtitle");
summary = htsmsg_get_str(in, "summary");
de = dvr_entry_update(de, enabled, dvr_config_name, channel, title, subtitle,
summary, desc, lang, start, stop, start_extra, stop_extra,
- priority, retention, removal, playcount, playposition);
+ priority, retention, removal, playcount, playposition,
+ age_rating);
return htsp_success();
}
return htsp_error(htsp, N_("User does not have access"));
autorec_destroy_by_id(daeId, 1);
-
+
return htsp_success();
}
}
/**
- * Return cutpoint data for a recording (if present).
+ * Return cutpoint data for a recording (if present).
*
* Request message fields:
* id u32 required DVR entry id
*
* Result message fields:
- * cutpoints msg[] optional List of cutpoint entries, if a file is
+ * cutpoints msg[] optional List of cutpoint entries, if a file is
* found and has some valid data.
*
* Cutpoint fields:
* start u32 required Cut start time in ms.
* end u32 required Cut end time in ms.
- * type u32 required Action type:
- * 0=Cut, 1=Mute, 2=Scene,
+ * type u32 required Action type:
+ * 0=Cut, 1=Mute, 2=Scene,
* 3=Commercial break.
**/
static htsmsg_t *
}
htsmsg_add_msg(msg, "cutpoints", cutpoint_list);
}
-
+
// Cleanup...
dvr_cutpoint_list_destroy(list);
LIST_FOREACH(s, &htsp->htsp_subscriptions, hs_link)
if(s->hs_sid == sid)
break;
-
+
/*
* We send the reply here or the user will get the 'subscriptionStop'
* async message before the reply to 'unsubscribe'.
LIST_FOREACH(hs, &htsp->htsp_subscriptions, hs_link)
if(hs->hs_sid == sid)
break;
-
+
if(hs == NULL)
return htsp_error(htsp, N_("Subscription does not exist"));
privgain = (rights->aa_rights |
htsp->htsp_granted_access->aa_rights) !=
htsp->htsp_granted_access->aa_rights;
-
+
tvhinfo(LS_HTSP, "%s: Identified as user '%s'",
htsp->htsp_logname, username);
tvh_str_update(&htsp->htsp_username, username);
uint8_t data[4];
void *buf;
- v = timeout ? tcp_read_timeout(htsp->htsp_fd, data, 4, timeout) :
+ v = timeout ? tcp_read_timeout(htsp->htsp_fd, data, 4, timeout) :
tcp_read(htsp->htsp_fd, data, 4);
if(v != 0)
if((buf = malloc(len)) == NULL)
return ENOMEM;
- v = timeout ? tcp_read_timeout(htsp->htsp_fd, buf, len, timeout) :
+ v = timeout ? tcp_read_timeout(htsp->htsp_fd, buf, len, timeout) :
tcp_read(htsp->htsp_fd, buf, len);
-
+
if(v != 0) {
free(buf);
return v;
r = tvh_write(htsp->htsp_fd, dptr, dlen);
free(dptr);
tvh_mutex_lock(&htsp->htsp_out_mutex);
-
+
if (r) {
tvhinfo(LS_HTSP, "%s: Write error -- %s",
htsp->htsp_logname, strerror(errno));
htsp_connection_t htsp;
char buf[50];
htsp_subscription_t *s;
-
+
// Note: global_lock held on entry
if (config.dscp >= 0)
htsp_file_destroy(hf);
close(fd);
-
+
/* Free memory (leave lock in place, for parent method) */
tvh_mutex_lock(&global_lock);
free(htsp.htsp_logname);
}
m = htsmsg_create_map();
-
+
htsmsg_add_str(m, "method", "muxpkt");
htsmsg_add_u32(m, "subscriptionId", hs->hs_sid);
if (video)
uint32_t dur = hs->hs_90khz ? pkt->pkt_duration : ts_rescale(pkt->pkt_duration, 1000000);
htsmsg_add_u32(m, "duration", dur);
-
+
/**
* Since we will serialize directly we use 'binptr' which is a binary
* object that just points to data, thus avoiding a copy.
htsmsg_add_u32(m, "errors", hs->hs_data_errors);
/**
- * Figure out real time queue delay
+ * Figure out real time queue delay
*/
-
+
tvh_mutex_lock(&htsp->htsp_out_mutex);
int64_t min_dts = PTS_UNSET;
continue;
if(ts == PTS_UNSET)
continue;
-
+
if(min_dts == PTS_UNSET)
min_dts = ts;
else
htsmsg_add_str(c, "type", type);
if(ssc->es_lang[0])
htsmsg_add_str(c, "language", ssc->es_lang);
-
+
if(ssc->es_type == SCT_DVBSUB) {
htsmsg_add_u32(c, "composition_id", ssc->es_composition_id);
htsmsg_add_u32(c, "ancillary_id", ssc->es_ancillary_id);
htsmsg_add_msg(streams, NULL, c);
}
-
+
htsmsg_add_msg(m, "streams", streams);
si = &ss->ss_si;
htsmsg_add_str2(sourceinfo, "service", si->si_service );
htsmsg_add_str2(sourceinfo, "satpos", si->si_satpos );
}
-
+
htsmsg_add_msg(m, "sourceinfo", sourceinfo);
-
+
htsmsg_add_str(m, "method", "subscriptionStart");
htsmsg_add_u32(m, "subscriptionId", hs->hs_sid);
htsp_send_subscription(hs->hs_htsp, m, NULL, hs, 0);
const char *lang;
const lang_code_list_t *langs;
epg_episode_num_t num;
+ int tmp_age;
if (de || ebc) {
localtime_r(de ? &de->de_start : &ebc->start, &tm);
if(eg && epg_genre_get_str(eg, 1, 0, ctype, 100, NULL))
addtag(q, build_tag_string("CONTENT_TYPE", ctype, NULL, 0, NULL));
+ //TODO - MKV The spec suggests that this tag can consist of multiple value types.
+ //https://www.matroska.org/technical/tagging.html
+ //==> Depending on the COUNTRY it’s the format of the rating of a movie
+ //==> (P, R, X in the USA, an age in other countries or a URI defining a logo).
+ tmp_age = ebc->age_rating;
+ addtag(q, build_tag_int("LAW_RATING", tmp_age, 0, NULL));
+
if(ch)
addtag(q, build_tag_string("TVCHANNEL",
channel_get_name(ch, channel_blank_name), NULL, 0, NULL));
var genre = params[21].value;
/* channelname is unused param 22 */
var fanart_image = params[23].value;
+ var age_rating = params[25].value;
var content = '<div class="dvr-details-dialog">' +
'<div class="dvr-details-dialog-background-image"></div>' +
'<div class="dvr-details-dialog-content">';
del: true,
list: 'category,enabled,duplicate,disp_title,disp_extratext,episode_disp,' +
'channel,image,copyright_year,start_real,stop_real,duration,pri,filesize,' +
- 'sched_status,errors,data_errors,config_name,owner,creator,comment,genre,broadcast',
+ 'sched_status,errors,data_errors,config_name,owner,creator,comment,genre,broadcast,age_rating',
columns: {
disp_title: {
renderer: tvheadend.displayWithYearAndDuplicateRenderer(),
buttonFcn(store, select, 'api/dvr/entry/move/failed');
}
};
-
+
var removeButton = {
name: 'remove',
builder: function() {
del: false,
list: 'disp_title,disp_extratext,episode_disp,channel,channelname,' +
'start_real,stop_real,duration,filesize,copyright_year,' +
- 'sched_status,errors,data_errors,playcount,url,config_name,owner,creator,comment,',
+ 'sched_status,errors,data_errors,playcount,url,config_name,owner,creator,comment,age_rating',
columns: {
disp_title: {
renderer: tvheadend.displayWithYearRenderer(),
_('The associated file will be removed from storage.'),
list: 'disp_title,disp_extratext,episode_disp,channel,channelname,' +
'image,copyright_year,start_real,stop_real,duration,filesize,status,' +
- 'sched_status,errors,data_errors,playcount,url,config_name,owner,creator,comment',
+ 'sched_status,errors,data_errors,playcount,url,config_name,owner,creator,comment,age_rating',
columns: {
disp_title: {
renderer: tvheadend.displayWithYearRenderer(),
del: true,
list: 'disp_title,disp_extratext,episode_disp,channel,channelname,image,' +
'copyright_year,start_real,stop_real,duration,status,' +
- 'sched_status,errors,data_errors,url,config_name,owner,creator,comment',
+ 'sched_status,errors,data_errors,url,config_name,owner,creator,comment,age_rating',
columns: {
disp_title: {
renderer: tvheadend.displayWithYearRenderer(),