4343#include "parsers/parser_hevc.h"
4444#include "muxer_mkv.h"
4545
46+ #include "epggrab.h" //Needed to see if global processing of parental rating labels is enabled.
4647
4748extern int dvr_iov_max ;
4849
@@ -229,17 +230,17 @@ mk_build_segment_info(mk_muxer_t *mk)
229230 snprintf (app , sizeof (app ), "Tvheadend %s" , tvheadend_version );
230231
231232 if (!mk -> webm )
232- ebml_append_bin (q , 0x73a4 , mk -> uuid , sizeof (mk -> uuid ));
233+ ebml_append_bin (q , 0x73a4 , mk -> uuid , sizeof (mk -> uuid )); //0x73a4 = SegmentUUID
233234
234235 if (!mk -> webm )
235- ebml_append_string (q , 0x7ba9 , mk -> title );
236+ ebml_append_string (q , 0x7ba9 , mk -> title ); //0x7ba9 = Title
236237
237- ebml_append_string (q , 0x4d80 , "Tvheadend Matroska muxer" );
238- ebml_append_string (q , 0x5741 , app );
239- ebml_append_uint (q , 0x2ad7b1 , MATROSKA_TIMESCALE );
238+ ebml_append_string (q , 0x4d80 , "Tvheadend Matroska muxer" ); //0x4d80 = MuxingApp
239+ ebml_append_string (q , 0x5741 , app ); //0x5741 = WritingApp
240+ ebml_append_uint (q , 0x2ad7b1 , MATROSKA_TIMESCALE ); //0x2ad7b1 = TimestampScale
240241
241242 if (mk -> totduration )
242- ebml_append_float (q , 0x4489 , (float )mk -> totduration );
243+ ebml_append_float (q , 0x4489 , (float )mk -> totduration ); //0x4489 = Duration
243244 else
244245 ebml_append_pad (q , 7 ); // Must be equal to floatingpoint duration
245246 return q ;
@@ -677,7 +678,7 @@ mk_write_chapters(mk_muxer_t *mk)
677678 return ;
678679
679680 mk -> chapters_pos = mk -> fdpos ;
680- mk_write_master (mk , 0x1043a770 , mk_build_chapters (mk ));
681+ mk_write_master (mk , 0x1043a770 , mk_build_chapters (mk )); //0x1043a770 = Chapters
681682}
682683
683684
@@ -692,18 +693,18 @@ build_tag_string(const char *name, const char *value, const char *lang,
692693 htsbuf_queue_t * st = htsbuf_queue_alloc (0 );
693694
694695 htsbuf_queue_t * t = htsbuf_queue_alloc (0 );
695- ebml_append_uint (t , 0x68ca , targettype ?: 50 );
696+ ebml_append_uint (t , 0x68ca , targettype ?: 50 ); //0x68ca = TargetTypeValue
696697
697698 if (targettypename )
698- ebml_append_string (t , 0x63ca , targettypename );
699- ebml_append_master (q , 0x63c0 , t );
699+ ebml_append_string (t , 0x63ca , targettypename ); //0x63ca = TargetType
700+ ebml_append_master (q , 0x63c0 , t ); //0x63c0 = Targets
700701
701- ebml_append_string (st , 0x45a3 , name );
702- ebml_append_string (st , 0x4487 , value );
703- ebml_append_uint (st , 0x4484 , 1 );
704- ebml_append_string (st , 0x447a , lang ?: "und" );
702+ ebml_append_string (st , 0x45a3 , name ); //0x45a3 = TagName
703+ ebml_append_string (st , 0x4487 , value ); //0x4487 = TagString
704+ ebml_append_uint (st , 0x4484 , 1 ); //0x4484 = TagDefault
705+ ebml_append_string (st , 0x447a , lang ?: "und" ); //0x447a = TagLanguage
705706
706- ebml_append_master (q , 0x67c8 , st );
707+ ebml_append_master (q , 0x67c8 , st ); //0x67c8 = SimpleTag
707708 return q ;
708709}
709710
@@ -738,19 +739,22 @@ static htsbuf_queue_t *
738739_mk_build_metadata (const dvr_entry_t * de , const epg_broadcast_t * ebc ,
739740 const char * comment )
740741{
742+
743+ //Note: May 2025 DMC - If the recording is manual or from a timer
744+ //then only the 'comment' will be available, 'de' and 'ebc' will be null.
745+
741746 htsbuf_queue_t * q = htsbuf_queue_alloc (0 );
742- char datestr [64 ], ctype [100 ];
747+ char datestr [64 ], ctype [100 ], temp_rating [ 128 ] ;
743748 const epg_genre_t * eg = NULL ;
744749 epg_genre_t eg0 ;
745750 struct tm tm ;
746751 time_t t ;
747752 channel_t * ch = ebc ? ebc -> channel : NULL ;
748- lang_str_t * ls = NULL , * ls2 = NULL ;
753+ lang_str_t * ls = NULL , * ls2 = NULL , * ls3 = NULL ;
749754 const char * lang ;
750755 const lang_code_list_t * langs ;
751756 epg_episode_num_t num ;
752- int tmp_age ;
753-
757+
754758 if (de || ebc ) {
755759 localtime_r (de ? & de -> de_start : & ebc -> start , & tm );
756760 } else {
@@ -780,14 +784,32 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc,
780784 if (eg && epg_genre_get_str (eg , 1 , 0 , ctype , 100 , NULL ))
781785 addtag (q , build_tag_string ("CONTENT_TYPE" , ctype , NULL , 0 , NULL ));
782786
783- //TODO - MKV The spec suggests that this tag can consist of multiple value types.
784- //https://www.matroska.org/technical/tagging.html
785- //==> Depending on the COUNTRY it’s the format of the rating of a movie
786- //==> (P, R, X in the USA, an age in other countries or a URI defining a logo).
787- if (ebc ) {
788- tmp_age = ebc -> age_rating ;
789- addtag (q , build_tag_int ("LAW_RATING" , tmp_age , 0 , NULL ));
790- }
787+ //Only add the 'LAW_RATING' tag if 1) rating label processing is enabled globally
788+ //AND 2) this EPG entry has a rating label worth adding.
789+ if (epggrab_conf .epgdb_processparentallabels && ebc && ebc -> rating_label )
790+ {
791+ //If the rating label for this event is enabled AND it has a display_label.
792+ if (ebc -> rating_label -> rl_enabled && ebc -> rating_label -> rl_display_label )
793+ {
794+ //Build a fallback string with only the rating label.
795+ snprintf (temp_rating , sizeof (temp_rating ), "%s" , ebc -> rating_label -> rl_display_label );
796+
797+ //If there is a country code, build a string that includes the country code
798+ //eg: 'PG (AUS)'
799+ if (ebc -> rating_label -> rl_enabled && ebc -> rating_label -> rl_country )
800+ {
801+ snprintf (temp_rating , sizeof (temp_rating ), "%s (%s)" , ebc -> rating_label -> rl_display_label , ebc -> rating_label -> rl_country );
802+ }
803+ //Otherwise, if there is a authority name, build a string that includes the authority
804+ //eg: 'PG (ACMA)'
805+ else if (ebc -> rating_label -> rl_enabled && ebc -> rating_label -> rl_authority )
806+ {
807+ snprintf (temp_rating , sizeof (temp_rating ), "%s (%s)" , ebc -> rating_label -> rl_display_label , ebc -> rating_label -> rl_authority );
808+ }
809+
810+ addtag (q , build_tag_string ("LAW_RATING" , temp_rating , NULL , 0 , NULL ));
811+ }
812+ }//END we got a rating label from the EPG
791813
792814 if (ch )
793815 addtag (q , build_tag_string ("TVCHANNEL" ,
@@ -800,6 +822,11 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc,
800822 else if (ebc && ebc -> description )
801823 ls2 = ebc -> description ;
802824
825+ if (de && de -> de_subtitle )
826+ ls3 = de -> de_subtitle ;
827+ else if (ebc && ebc -> subtitle )
828+ ls3 = ebc -> subtitle ;
829+
803830 if (!ls )
804831 ls = ls2 ;
805832
@@ -814,7 +841,13 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc,
814841 RB_FOREACH (e , ls2 , link )
815842 addtag (q , build_tag_string ("DESCRIPTION" , e -> str , e -> lang , 0 , NULL ));
816843 }
817-
844+
845+ if (ls3 ) {
846+ lang_str_ele_t * e ;
847+ RB_FOREACH (e , ls3 , link )
848+ addtag (q , build_tag_string ("SUBTITLE" , e -> str , e -> lang , 0 , NULL ));
849+ }
850+
818851 epg_broadcast_get_epnum (ebc , & num );
819852 if (num .e_num )
820853 addtag (q , build_tag_int ("PART_NUMBER" , num .e_num ,
@@ -834,9 +867,25 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc,
834867 if ((langs = lang_code_split (NULL )) != NULL ) {
835868 lang = tvh_strdupa (langs -> codes [0 ]-> code2b );
836869 }
837-
838870 addtag (q , build_tag_string ("COMMENT" , comment , lang , 0 , NULL ));
839871 }
872+ else if (de && de -> de_comment ) { //Add the DVR recording comment if one exists and an explicit comment is omitted
873+ //If the DVR entry has a Title, 'borrow' the language code(s) for the comment
874+ if (de -> de_title ) {
875+ lang_str_ele_t * e ;
876+ RB_FOREACH (e , de -> de_title , link )
877+ addtag (q , build_tag_string ("COMMENT" , de -> de_comment , e -> lang , 0 , NULL ));
878+ }
879+ else //Otherwise, just use 'eng' as the default.
880+ {
881+ lang = "eng" ;
882+ if ((langs = lang_code_split (NULL )) != NULL ) {
883+ lang = tvh_strdupa (langs -> codes [0 ]-> code2b );
884+ }
885+
886+ addtag (q , build_tag_string ("COMMENT" , de -> de_comment , lang , 0 , NULL ));
887+ }
888+ }//END extra comment
840889
841890 return q ;
842891}
0 commit comments