[vlc-commits] [Git][videolan/vlc][master] 7 commits: playlist: m3u: export channel as publisher

Hugo Beauzée-Luyssen (@chouquette) gitlab at videolan.org
Wed Nov 24 21:01:40 UTC 2021



Hugo Beauzée-Luyssen pushed to branch master at VideoLAN / VLC


Commits:
d195dc61 by Francois Cartegnie at 2021-11-24T20:32:00+00:00
playlist: m3u: export channel as publisher

- - - - -
2e9148ce by Francois Cartegnie at 2021-11-24T20:32:00+00:00
playlist: m3u: handle IPTV EXTGRP tag

- - - - -
85d2158a by Francois Cartegnie at 2021-11-24T20:32:00+00:00
playlist: m3u: fix parsing IPTV syntax

- - - - -
77ab598b by Francois Cartegnie at 2021-11-24T20:32:00+00:00
playlist: m3u: fix bogus token matching

- - - - -
da6cf697 by Francois Cartegnie at 2021-11-24T20:32:00+00:00
playlist: m3u: parse EXTINF time as non integer

- - - - -
058936a9 by Francois Cartegnie at 2021-11-24T20:32:00+00:00
playlist: m3u: update current node item name

- - - - -
16d6b231 by Francois Cartegnie at 2021-11-24T20:32:00+00:00
tests: playlist: add m3u

- - - - -


3 changed files:

- modules/demux/playlist/m3u.c
- test/Makefile.am
- + test/modules/demux/playlist/m3u.c


Changes:

=====================================
modules/demux/playlist/m3u.c
=====================================
@@ -241,7 +241,7 @@ static int CreateEntry( input_item_node_t *p_node, const struct entry_meta_s *me
     if( meta->psz_tvgid )
         input_item_AddInfo( p_input, "XMLTV", "tvg-id", "%s", meta->psz_tvgid );
     if( meta->psz_grouptitle )
-        input_item_SetAlbum( p_input, meta->psz_grouptitle );
+        input_item_SetPublisher( p_input, meta->psz_grouptitle );
 
     input_item_node_AppendItem( p_node, p_input );
     input_item_Release( p_input );
@@ -252,6 +252,7 @@ static int CreateEntry( input_item_node_t *p_node, const struct entry_meta_s *me
 static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
 {
     char       *psz_line;
+    char       *psz_group = NULL; /* group is toggling tag */
     struct entry_meta_s meta;
     entry_meta_Init( &meta );
     char *    (*pf_dup) (const char *) = p_demux->p_sys;
@@ -283,6 +284,15 @@ static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
                 meta.i_duration = INPUT_DURATION_INDEFINITE;
                 parseEXTINF( psz_parse, pf_dup, &meta );
             }
+            else if( !strncasecmp( psz_parse, "EXTGRP:", sizeof("EXTGRP:") -1 ) )
+            {
+                psz_parse += sizeof("EXTGRP:") - 1;
+                if( *psz_parse )
+                {
+                    free( psz_group );
+                    psz_group = pf_dup( psz_parse );
+                }
+            }
             else if( !strncasecmp( psz_parse, "EXTVLCOPT:",
                                    sizeof("EXTVLCOPT:") -1 ) )
             {
@@ -310,7 +320,7 @@ static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
                       sizeof( "PLAYLIST:" ) - 1 ) )
             {
                 psz_parse += sizeof( "PLAYLIST:" ) - 1;
-                input_item_SetTitle( p_demux->p_input_item, psz_parse );
+                input_item_SetTitle( p_subitems->p_item, psz_parse );
             }
         }
         else if( !strncasecmp( psz_parse, "RTSPtext", sizeof("RTSPtext") -1 ) )
@@ -323,6 +333,8 @@ static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
             if( !meta.psz_name && psz_parse )
                 /* Use filename as name for relative entries */
                 meta.psz_name = strdup( psz_parse );
+            if( psz_group && !meta.psz_grouptitle )
+                meta.psz_grouptitle = strdup( psz_group );
 
             meta.psz_mrl = ProcessMRL( psz_parse, p_demux->psz_url );
             free( psz_parse );
@@ -343,6 +355,7 @@ static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
             /* Cleanup state */
             entry_meta_Clean( &meta );
             entry_meta_Init( &meta );
+            free( psz_group );
         }
     }
     return VLC_SUCCESS; /* Needed for correct operation of go back */
@@ -399,47 +412,50 @@ static void parseEXTINFIptvDiots( char *psz_string,
                                   char *(*pf_dup)(const char *),
                                   struct entry_meta_s *meta )
 {
-    char **ppsz_meta = NULL;
-    if( strncmp( psz_string, "tvg-", 4 ) &&
-        strncmp( psz_string, "group-", 6 ) )
-        return;
-    char *psz_sep = strchr( psz_string + 4, '=' );
-    if( unlikely(!psz_sep) )
+    char *psz = strchr( psz_string, '=' );
+    if( unlikely(!psz) )
         return;
-    size_t i_keylen = psz_sep - psz_string;
-
-    if( !strncmp( psz_string + 4, "logo", i_keylen - 4 ) )
-        ppsz_meta = &meta->psz_album_art;
-    else if( !strncmp( psz_string + 4, "name", i_keylen - 4 ) )
-        ppsz_meta = &meta->psz_name;
-    else if( !strncmp( psz_string + 4, "language", i_keylen - 4 ) )
-        ppsz_meta = &meta->psz_language;
-    else if( !strncmp( psz_string + 4, "id", i_keylen - 4 ) )
-        ppsz_meta = &meta->psz_tvgid;
-    else if( !strncmp( psz_string + 6, "title", i_keylen - 4 ) )
+
+    char **ppsz_meta = NULL;
+    *psz = 0;
+    if( !strncasecmp( psz_string, "tvg-", 4 ) )
+    {
+        if( !strcasecmp( psz_string + 4, "logo" ) )
+            ppsz_meta = &meta->psz_album_art;
+        else if( !strcasecmp( psz_string + 4, "name" ) )
+            ppsz_meta = &meta->psz_name;
+        else if( !strcasecmp( psz_string + 4, "language" ) )
+            ppsz_meta = &meta->psz_language;
+        else if( !strcasecmp( psz_string + 4, "id" ) )
+            ppsz_meta = &meta->psz_tvgid;
+    }
+    else if( !strcasecmp( psz_string, "group-title" ) )
+    {
         ppsz_meta = &meta->psz_grouptitle;
+    }
+    *psz = '=';
 
     if( !ppsz_meta || *ppsz_meta /* no overwrite */ )
         return;
 
-    char *psz_value = psz_sep + 1;
-    size_t i_valuelen = strlen( psz_value );
+    size_t i_valuelen = strlen( ++psz );
     if( unlikely(i_valuelen == 0) )
         return;
 
-    bool b_escaped = (*psz_value == '"');
+    bool b_escaped = (*psz == '"');
     if( i_valuelen > 2 && b_escaped )
     {
-        psz_value[ i_valuelen - 1 ] = 0;
-        *ppsz_meta = pf_dup( psz_value + 1 );
+        psz[ i_valuelen - 1 ] = 0;
+        *ppsz_meta = pf_dup( psz + 1 );
     }
     else
-        *ppsz_meta = pf_dup( psz_value );
+        *ppsz_meta = pf_dup( psz );
 }
 
 static void parseEXTINFIptvDiotsInDuration( char *psz_string,
                                             char *(*pf_dup)(const char *),
-                                            struct entry_meta_s *meta )
+                                            struct entry_meta_s *meta,
+                                            char **ppsz_end )
 {
     for( ;; )
     {
@@ -456,6 +472,18 @@ static void parseEXTINFIptvDiotsInDuration( char *psz_string,
         {
             switch( *psz_string )
             {
+                case ',': /* Last comma for title */
+                    if(!b_escaped)
+                    {
+                        if(b_value)
+                        {
+                            *psz_string = '\0';
+                            parseEXTINFIptvDiots( psz_start, pf_dup, meta );
+                        }
+                        *ppsz_end = psz_string + 1;
+                        return;
+                    }
+                    break;
                 case '"':
                     if(!b_escaped && b_value)
                         return;
@@ -502,24 +530,41 @@ static void parseEXTINF( char *psz_string,
     while( psz_string < end && ( *psz_string == '\t' || *psz_string == ' ' ) )
         psz_string++;
 
-    /* duration: read to next comma */
-    char *psz_comma = strchr( psz_string, ',' );
-    if( psz_comma )
-    {
-        *psz_comma = '\0'; /* Split strings */
-        if( ++psz_comma < end )
-            parseEXTINFTitle( psz_comma, pf_dup, meta );
-    }
-
     /* Parse duration */
     char *psz_end = NULL;
-    long i_parsed_duration = strtol( psz_string, &psz_end, 10 );
+    float i_parsed_duration = us_strtof( psz_string, &psz_end );
     if( i_parsed_duration > 0 )
-        meta->i_duration = vlc_tick_from_sec( i_parsed_duration );
+        meta->i_duration = vlc_tick_from_sec( (double)i_parsed_duration );
+
+    /* skip to first unmatched */
+    if( psz_end )
+        psz_string = psz_end;
+
+    /* skip whitespaces */
+    while( psz_string < end && ( *psz_string == '\t' || *psz_string == ' ' ) )
+        psz_string++;
+
+    if( psz_string == end )
+        return;
 
-    if( psz_end && psz_end != psz_string && ( *psz_end == '\t' || *psz_end == ' ' ) )
+    /* EXTINF:1,title*/
+    /* EXTINF: -123.12  ,title*/
+    if( *psz_string == ',' )
     {
-        parseEXTINFIptvDiotsInDuration( psz_end, pf_dup, meta );
+        if( ++psz_string < end )
+            parseEXTINFTitle( psz_string, pf_dup, meta );
+    }
+    /* EXTINF: -1  tvg-foo="val" tvg-foo2="val",title */
+    /* EXTINF: -1  tvg-foo="val,val2" ,title */
+    else if( *psz_string >= 'A' && *psz_string <= 'z' )
+    {
+        psz_end = NULL;
+        parseEXTINFIptvDiotsInDuration( psz_string, pf_dup, meta, &psz_end );
+        if( psz_end && *psz_end ) /* returns past last comma */
+        {
+            free(meta->psz_name);
+            meta->psz_name = pf_dup( psz_end );
+        }
     }
 }
 


=====================================
test/Makefile.am
=====================================
@@ -41,6 +41,7 @@ check_PROGRAMS = \
 	test_modules_keystore \
 	test_modules_demux_timestamps_filter \
 	test_modules_demux_ts_pes \
+	test_modules_playlist_m3u \
 	$(NULL)
 
 if ENABLE_SOUT
@@ -151,6 +152,8 @@ test_modules_demux_ts_pes_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_modules_demux_ts_pes_SOURCES = modules/demux/ts_pes.c \
 				../modules/demux/mpeg/ts_pes.c \
 				../modules/demux/mpeg/ts_pes.h
+test_modules_playlist_m3u_SOURCES = modules/demux/playlist/m3u.c
+test_modules_playlist_m3u_LDADD = $(LIBVLCCORE) $(LIBVLC)
 
 
 checkall:


=====================================
test/modules/demux/playlist/m3u.c
=====================================
@@ -0,0 +1,223 @@
+/*****************************************************************************
+ * m3u.c: M3U unit testing
+ *****************************************************************************
+ * Copyright (C) 2021 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc/vlc.h>
+#include "../../../../lib/libvlc_internal.h"
+#include "../../../libvlc/test.h"
+
+#include <vlc_common.h>
+#include <vlc_modules.h>
+#include <vlc_demux.h>
+#include <vlc_meta.h>
+#include <vlc_input_item.h>
+
+#define BAILOUT(run) { fprintf(stderr, "failed %s line %d\n", run, __LINE__); \
+                        return 1; }
+#define EXPECT(foo) if(!(foo)) BAILOUT(run)
+#define NOPFIL(n) "bar"#n
+#define NOPURI(n) INPUT_ITEM_URI_NOP "/" NOPFIL(n)
+
+static int runtest(const char *run,
+                   libvlc_instance_t *vlc,
+                   const char *data, size_t datasz,
+                   int(*checkfunc)(const char *, const input_item_node_t *))
+{
+    stream_t *s = vlc_stream_MemoryNew(vlc->p_libvlc_int, (uint8_t *)data, datasz, true);
+    if(!s)
+        BAILOUT(run);
+
+    demux_t *pl = demux_New(VLC_OBJECT(vlc->p_libvlc_int), "m3u", INPUT_ITEM_URI_NOP, s, NULL);
+    if(!pl || !pl->pf_readdir)
+    {
+        vlc_stream_Delete(s);
+        BAILOUT(run);
+    }
+
+    int ret = 0;
+    input_item_t *p_item = input_item_New(NULL, NULL);
+    if(p_item)
+    {
+        input_item_node_t *p_node = input_item_node_Create(p_item);
+        if(p_node)
+        {
+            pl->pf_readdir(pl, p_node);
+            ret = checkfunc(run, p_node);
+            input_item_node_Delete(p_node);
+        }
+        else
+        {
+            ret = 1;
+        }
+        input_item_Release(p_item);
+    }
+    else
+    {
+        ret = 1;
+    }
+
+    demux_Delete(pl);
+
+    return ret;
+}
+
+const char m3uplaylist0[] =
+"#EXTM3U\n"
+"#JUNK\n"
+NOPFIL(0) "\n"
+NOPURI(1) "\n";
+
+static int check0(const char *run, const input_item_node_t *p_node)
+{
+    EXPECT(p_node->i_children == 2);
+
+    const input_item_t *p_item = p_node->pp_children[0]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(NOPURI(0), p_item->psz_uri));
+    EXPECT(!strcmp(NOPFIL(0), p_item->psz_name));
+    EXPECT(p_item->i_duration == INPUT_DURATION_INDEFINITE);
+
+    p_item = p_node->pp_children[1]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(p_item->psz_name, p_item->psz_uri));
+    EXPECT(!strcmp(NOPURI(1), p_item->psz_name));
+
+    return 0;
+}
+
+const char m3uplaylist1[] =
+"#EXTM3U\n"
+"#EXTINF: 1\n"
+NOPFIL(0) "\n"
+"#EXTINF: 1.11\n"
+NOPURI(1) "\n"
+"#EXTINF: -2,\n"
+"#JUNK:foo\n"
+NOPURI(2) "\n"
+"#EXTINF: 3,artist3 - name3\n"
+NOPURI(3) "\n"
+"#EXTINF: 4,,name4\n"
+NOPURI(4) "\n"
+"#EXTINF: 5,artist5,name5\n"
+NOPURI(5) "\n";
+
+static int check1(const char *run, const input_item_node_t *p_node)
+{
+    EXPECT(p_node->i_children == 6);
+
+    const input_item_t *p_item = p_node->pp_children[0]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(NOPFIL(0), p_item->psz_name));
+    EXPECT(p_item->i_duration == vlc_tick_from_sec(1));
+
+    p_item = p_node->pp_children[1]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(NOPURI(1), p_item->psz_name));
+    EXPECT(p_item->i_duration == vlc_tick_from_sec(1.11));
+
+    p_item = p_node->pp_children[2]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(NOPURI(2), p_item->psz_uri));
+    EXPECT(!strcmp(p_item->psz_name, p_item->psz_uri));
+    EXPECT(p_item->i_duration == INPUT_DURATION_INDEFINITE);
+
+    p_item = p_node->pp_children[3]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(NOPURI(3), p_item->psz_uri));
+    EXPECT(!strcmp("name3", p_item->psz_name));
+    const char *p = vlc_meta_Get(p_item->p_meta, vlc_meta_Artist);
+    EXPECT(p && !strcmp("artist3", p));
+
+    p_item = p_node->pp_children[4]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp("name4", p_item->psz_name));
+    EXPECT(vlc_meta_Get(p_item->p_meta, vlc_meta_Artist) == NULL);
+
+    p_item = p_node->pp_children[5]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp("name5", p_item->psz_name));
+    p = vlc_meta_Get(p_item->p_meta, vlc_meta_Artist);
+    EXPECT(p && !strcmp("artist5", p));
+
+    return 0;
+}
+
+const char m3uplaylist2[] =
+"#EXTM3U\n"
+"#PLAYLIST:playlist0\n"
+"#EXTINF:-1 tvg-id=\"id0\" tvg-logo=\"logo0\" group-title=\"group0\",name0\n"
+NOPURI(0)"\n"
+"#EXTGRP:group1\n"
+"#EXTINF:-1,name1\n"
+NOPURI(1)"\n"
+"#EXTINF:-1,name2\n"
+NOPURI(2)"\n";
+
+static int check2(const char *run, const input_item_node_t *p_node)
+{
+    EXPECT(p_node->i_children == 3);
+    const char *p = vlc_meta_Get(p_node->p_item->p_meta, vlc_meta_Title);
+    EXPECT(p && !strcmp(p, "playlist0"));
+
+    const input_item_t *p_item = p_node->pp_children[0]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(NOPURI(0), p_item->psz_uri));
+    EXPECT(!strcmp("name0", p_item->psz_name));
+    p = vlc_meta_Get(p_item->p_meta, vlc_meta_Publisher);
+    EXPECT(p && !strcmp("group0", p));
+    p = vlc_meta_Get(p_item->p_meta, vlc_meta_ArtworkURL);
+
+    p_item = p_node->pp_children[1]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(NOPURI(1), p_item->psz_uri));
+    EXPECT(!strcmp("name1", p_item->psz_name));
+    p = vlc_meta_Get(p_item->p_meta, vlc_meta_Publisher);
+    EXPECT(p && !strcmp("group1", p));
+
+    p_item = p_node->pp_children[2]->p_item;
+    EXPECT(p_item->psz_name && p_item->psz_uri);
+    EXPECT(!strcmp(NOPURI(2), p_item->psz_uri));
+    EXPECT(!strcmp("name2", p_item->psz_name));
+    p = vlc_meta_Get(p_item->p_meta, vlc_meta_Publisher);
+    EXPECT(p && !strcmp("group1", p));
+
+    return 0;
+}
+
+
+int main(void)
+{
+    test_init();
+
+    libvlc_instance_t *vlc = libvlc_new(0, NULL);
+    if(!vlc)
+        return 1;
+
+    int ret = runtest("run0", vlc, m3uplaylist0, sizeof(m3uplaylist0), check0);
+    if(!ret)
+        ret = runtest("run1", vlc, m3uplaylist1, sizeof(m3uplaylist1), check1);
+    if(!ret)
+        ret = runtest("run2", vlc, m3uplaylist2, sizeof(m3uplaylist2), check2);
+
+    libvlc_release(vlc);
+    return ret;
+}



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/b35560babc5f31042d0ecd8eb02a7668d41c48e2...16d6b2311d7780bedfe938be64051a7587ce81cd

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/b35560babc5f31042d0ecd8eb02a7668d41c48e2...16d6b2311d7780bedfe938be64051a7587ce81cd
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list