]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
OTA Genre translation squashed v2
authorDeltaMikeCharlie <127641886+DeltaMikeCharlie@users.noreply.github.com>
Sat, 22 Jul 2023 22:13:07 +0000 (08:13 +1000)
committerFlole998 <Flole998@users.noreply.github.com>
Sun, 30 Jul 2023 15:17:18 +0000 (17:17 +0200)
docs/property/ota_genre_translation.md [new file with mode: 0644]
src/epggrab.c
src/epggrab.h
src/epggrab/module/eit.c
src/epggrab/otamux.c
src/webui/static/img/doc/channel/epgconf_tab.png [changed mode: 0644->0755]

diff --git a/docs/property/ota_genre_translation.md b/docs/property/ota_genre_translation.md
new file mode 100644 (file)
index 0000000..f6165d0
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+Use this setting to translate broadcaster-specific, country-specific or other customised genre tags into tags recognised by tvheadend.
+
+Example:
+
+```
+192=20
+208=16
+224=35
+```
+
+| Line        | Meaning         |
+| ------------- | ------------- |
+|192=20|Translate decimal 192 (0xC0 = Australian-specific 'comedy') to decimal 20 (0x14 = ETSI standard 'comedy').|
+|208=16|Translate decimal 208 (0xD0 = Australian-specific 'drama') to decimal 16 (0x10 = ETSI standard 'movie/drama (general)').|
+|224=35|Translate decimal 224 (0xE0 = Australian-specific 'documentary') to decimal 35 (0x23 = ETSI standard 'documentary').|
+
+See: [Search the ETSI web site for the latest version of the 'ETSI EN 300 468' standard.](https://www.etsi.org/standards#page=1&search=%22ETSI%20EN%20300%20468%22&title=0&etsiNumber=1&content=0&version=1&onApproval=1&published=1&withdrawn=1&historical=1&isCurrent=1&superseded=1&startDate=1988-01-15&endDate=2023-07-09&harmonized=0&keyword=&TB=&stdType=&frequency=&mandate=&collection=&sort=1)
+
+Search for 'content_descriptor' in the standards document.
\ No newline at end of file
index 6e419f071a1dec6982f5743fb8f99d4a07da5b52..415250061c2afba56c86d3f3b7992247af419145 100644 (file)
@@ -328,8 +328,15 @@ epggrab_class_ota_cron_notify(void *self, const char *lang)
   epggrab_ota_set_cron();
 }
 
+static void
+epggrab_class_ota_genre_translation_notify(void *self, const char *lang)
+{
+  epggrab_ota_set_genre_translation();
+}
+
 CLASS_DOC(epgconf)
 PROP_DOC(cron)
+PROP_DOC(ota_genre_translation)
 
 const idclass_t epggrab_class = {
   .ic_snode      = &epggrab_conf.idnode,
@@ -353,7 +360,11 @@ const idclass_t epggrab_class = {
          .name   = N_("OTA (Over-the-air) Grabber Settings"),
          .number = 3,
       },
-      {}
+      {
+         .name   = N_("OTA (Over-the-air) Genre Translation"),
+         .number = 4,
+      },
+    {}
   },
   .ic_properties = (const property_t[]){
     {
@@ -471,6 +482,21 @@ const idclass_t epggrab_class = {
       .opts   = PO_EXPERT,
       .group  = 3,
     },
+    {
+      .type   = PT_STR,
+      .id     = "ota_genre_translation",
+      .name   = N_("Over-the-air Genre Translation"),
+      .desc   = N_("Translate the genre codes received from the broadcaster to another genre code."
+                   "<br>Use the form xxx=yyy, where xxx and yyy are "
+                   "'ETSI EN 300 468' content descriptor values expressed in decimal (0-255). "
+                   "<br>Genre code xxx will be converted to genre code yyy."
+                   "<br>Use a separate line for each genre code to be converted."),
+      .doc    = prop_doc_ota_genre_translation,
+      .off    = offsetof(epggrab_conf_t, ota_genre_translation),
+      .notify = epggrab_class_ota_genre_translation_notify,
+      .opts   = PO_MULTILINE | PO_EXPERT,
+      .group  = 4,
+    },
     {}
   }
 };
index b8f2798cefea7858864d2726e8dee812179e2666..90346fdca7db240c479acc116b5733c6c935fdaa 100644 (file)
@@ -319,6 +319,7 @@ typedef struct epggrab_conf {
   uint32_t              epgdb_periodicsave;
   uint32_t              epgdb_saveafterimport;
   char                 *ota_cron;
+  char                 *ota_genre_translation;
   uint32_t              ota_timeout;
   uint32_t              ota_initial;
   uint32_t              int_initial;
@@ -365,10 +366,11 @@ extern int                   epggrab_ota_running;
 /*
  * Set configuration
  */
-int epggrab_activate_module       ( epggrab_module_t *mod, int activate );
-void epggrab_ota_set_cron         ( void );
-void epggrab_ota_trigger          ( int secs );
-void epggrab_rerun_internal       ( void );
+int epggrab_activate_module            ( epggrab_module_t *mod, int activate );
+void epggrab_ota_set_cron              ( void );
+void epggrab_ota_set_genre_translation ( void );
+void epggrab_ota_trigger               ( int secs );
+void epggrab_rerun_internal            ( void );
 
 /*
  * Load/Save
@@ -398,6 +400,11 @@ epggrab_ota_mux_t *epggrab_ota_find_mux ( struct mpegts_mux *mm );
 htsmsg_t *epggrab_ota_module_id_list( const char *lang );
 const char *epggrab_ota_check_module_id( const char *id );
 
+/*
+ * Global variable for genre translation
+ */
+extern unsigned char                epggrab_ota_genre_translation[256];
+
 #endif /* __EPGGRAB_H__ */
 
 /* **************************************************************************
index 8b938f5d54eb9c89a9562c697b5aabc79d858628..4ae2b126f72172f51414b703137cfae9d73c2f31 100644 (file)
@@ -391,12 +391,21 @@ static int _eit_desc_component
 static int _eit_desc_content
   ( epggrab_module_t *mod, const uint8_t *ptr, int len, eit_event_t *ev )
 {
+  uint8_t tempPtr = 0;  //Temporary variable to hold a (potentially) changed *ptr value.
+  tempPtr = *ptr;
   while (len > 1) {
-    if (*ptr == 0xb1)
+    //Get the potentially new genre value.
+    tempPtr = epggrab_ota_genre_translation[*ptr];
+    //If we did get a translation, write a trace for debugging.
+    if(tempPtr != *ptr)
+    {
+      tvhtrace(LS_TBL_EIT, "Translating '%d' to '%d'", *ptr, tempPtr);
+    }
+    if (tempPtr == 0xb1)  //0xB1 is the genre code for 'Black and White'
       ev->bw = 1;
-    else if (*ptr < 0xb0) {
+    else if (tempPtr < 0xb0) {  //0xB0 is the start of the 'Special Characteristics' block.
       if (!ev->genre) ev->genre = calloc(1, sizeof(epg_genre_list_t));
-      epg_genre_list_add_by_eit(ev->genre, *ptr);
+      epg_genre_list_add_by_eit(ev->genre, (const uint8_t)tempPtr);  //Cast as a 'const'
     }
     len -= 2;
     ptr += 2;
index 4888158bab7e087aef7e8f1624934fe48b62ae37..c55b126cdf5fe6b58fc2d570f68deecfe5126d13 100644 (file)
@@ -56,6 +56,8 @@ int                          epggrab_ota_pending_flag;
 
 tvh_mutex_t                  epggrab_ota_mutex;
 
+unsigned char                epggrab_ota_genre_translation[256];
+
 SKEL_DECLARE(epggrab_ota_mux_skel, epggrab_ota_mux_t);
 SKEL_DECLARE(epggrab_svc_link_skel, epggrab_ota_svc_link_t);
 
@@ -1035,6 +1037,92 @@ epggrab_ota_set_cron ( void )
   epggrab_ota_arm((time_t)-1);
 }
 
+void
+epggrab_ota_set_genre_translation ( void )
+{
+
+  tvhtrace(LS_EPGGRAB, "Processing genre code translations.");
+
+  //Take the raw setting data and parse it into the genre translation table.
+  //There is some sanity checking done, however, the data is entered as freeform
+  //text by the user and errors may still slip through.
+  //Note: Each line that comes back from the WebUI is separated by a LF (0x0A).
+  //^^^^  This has been tested with a Windows WebUI client and it only sends LF, not CR/LF.
+
+  lock_assert(&global_lock);
+  tvh_mutex_lock(&epggrab_ota_mutex);
+
+  //Reset the translation table to 1:1 here before proceeding.
+  for(int i = 0; i < 256; i++)
+  {
+    epggrab_ota_genre_translation[i] = i;
+  }
+
+  int               tempPos = 0;
+  int               outPos = 0;
+  int               tempLen = 0;
+  char             *tempPair = NULL;
+  int               tempFrom = -1;
+  int               tempTo = -1;
+  tempLen = strlen(epggrab_conf.ota_genre_translation);
+
+  //Make sure that this is large enough to take the whole config string in one go
+  //to allow for it being full of nonsense entered by the user.
+  tempPair = calloc(tempLen + 1, 1);
+
+  //Read through the user input byte by byte and parse out the lines
+  for(tempPos = 0; tempPos < tempLen; tempPos++)
+  {
+    //If the current character is not a new line or a comma.  (Comma is an undocumented courtesy!)
+    if(epggrab_conf.ota_genre_translation[tempPos] != 10 && epggrab_conf.ota_genre_translation[tempPos] != ',')
+    {
+      //Only allow numerals, '=' and '#' to pass through.
+      //ASCII 0-9 = 48-57, '=' = 61 (in decimal)
+      if((epggrab_conf.ota_genre_translation[tempPos] >= 48 && epggrab_conf.ota_genre_translation[tempPos] <= 57) || epggrab_conf.ota_genre_translation[tempPos] == 61 || epggrab_conf.ota_genre_translation[tempPos] == '#')
+      {
+        tempPair[outPos] = epggrab_conf.ota_genre_translation[tempPos];
+        tempPair[outPos + 1] = 0;
+        outPos++;
+      }
+    }
+
+    //If we have a new line or a comma or we are at the end of the config string
+    if(epggrab_conf.ota_genre_translation[tempPos] == 10 || epggrab_conf.ota_genre_translation[tempPos] == ',' || tempPos == (tempLen - 1))
+    {
+      //Only process non-null strings.
+      if(strlen(tempPair) != 0)
+      {
+        tempFrom = -1;
+        tempTo = -1;
+        sscanf(tempPair, "%d=%d", &tempFrom, &tempTo);
+        //Test that the to/from values are sane before proceeding.
+        if(tempFrom > -1 && tempFrom < 256 && tempTo > -1 && tempTo < 256)
+        {
+          tvhtrace(LS_EPGGRAB, "Valid translation from '%d' to '%d'.", tempFrom, tempTo);
+          epggrab_ota_genre_translation[tempFrom] = tempTo;
+        }
+        else
+        {
+          tvhtrace(LS_EPGGRAB, "Ignoring '%s'.", tempPair);
+        }
+        outPos = 0;
+        tempPair[0] = 0;
+      }
+    }
+  }
+
+  for(int x = 0; x < 256; x++)
+  {
+    if(epggrab_ota_genre_translation[x] != x)
+    {
+      tvhtrace(LS_EPGGRAB, "Genre '%d' (0x%02x) translates to genre '%d' (0x%02x).", x, x, epggrab_ota_genre_translation[x], epggrab_ota_genre_translation[x]);
+    }
+  }
+  free(tempPair);
+
+  tvh_mutex_unlock(&epggrab_ota_mutex);
+}
+
 /******************************************************************************
  * Editor Configuration
  *
old mode 100644 (file)
new mode 100755 (executable)
index dc5e111..be4db94
Binary files a/src/webui/static/img/doc/channel/epgconf_tab.png and b/src/webui/static/img/doc/channel/epgconf_tab.png differ