Repository: streamlink/streamlink
Pull request #968 (2017-05-30 16:44:07)

Add capability of extracting current sport.francetv stream
Hello,

As the French Open (Roland Garros) is currently airing, Pluzz plugin dont work on the network sport dedicated website "sport.francetvinfo.fr".

This commit just adds the ability to retrieve the stream from this website.

Best regards.


Patch:
diff --git a/docs/ext_argparse.py b/docs/ext_argparse.py
index e95b9ab4..6d86a3df 100644
--- a/docs/ext_argparse.py
+++ b/docs/ext_argparse.py
@@ -28,7 +28,7 @@ _Argument = namedtuple("Argument", ["args", "options"])
 
 _block_re = re.compile(r":\n{2}\s{2}")
 _default_re = re.compile(r"Default is (.+)\.\n")
-_note_re = re.compile(r"Note: (.*)(?:\n\n|\n*$)", re.DOTALL)
+_note_re = re.compile(r"Note: (.*)\n\n", re.DOTALL)
 _option_re = re.compile(r"(?m)^((?!\s{2}).*)(--[\w-]+)")
 
 
diff --git a/src/streamlink/plugins/pluzz.py b/src/streamlink/plugins/pluzz.py
index 59555a26..d4da5143 100644
--- a/src/streamlink/plugins/pluzz.py
+++ b/src/streamlink/plugins/pluzz.py
@@ -15,10 +15,11 @@ class Pluzz(Plugin):
     PLAYER_GENERATOR_URL = 'https://sivideo.webservices.francetelevisions.fr/assets/staticmd5/getUrl?id=jquery.player.7.js'
     TOKEN_URL = 'http://hdfauthftv-a.akamaihd.net/esi/TA?url={0}'
 
-    _url_re = re.compile(r'https?://((?:www)\.france\.tv/.+\.html|www\.(ludo|zouzous)\.fr/heros/[\w-]+|france3-regions\.francetvinfo\.fr/.+?/tv/direct)')
+    _url_re = re.compile(r'https?://((?:www)\.france\.tv/.+\.html|www\.(ludo|zouzous)\.fr/heros/[\w-]+|(sport|france3-regions)\.francetvinfo\.fr/.+?/(tv/direct)?)')
     _pluzz_video_id_re = re.compile(r'data-main-video="(?P<video_id>.+?)"')
     _jeunesse_video_id_re = re.compile(r'playlist: \[{.*?,"identity":"(?P<video_id>.+?)@(?P<catalogue>Ludo|Zouzous)"')
     _f3_regions_video_id_re = re.compile(r'"http://videos\.francetv\.fr/video/(?P<video_id>.+)@Regions"')
+    _sport_video_id_re = re.compile(r'data-video="(?P<video_id>.+?)"')
     _player_re = re.compile(r'src="(?P<player>//staticftv-a\.akamaihd\.net/player/jquery\.player.+?-[0-9a-f]+?\.js)"></script>')
     _swf_re = re.compile(r'//staticftv-a\.akamaihd\.net/player/bower_components/player_flash/dist/FranceTVNVPVFlashPlayer\.akamai-[0-9a-f]+\.swf')
     _hds_pv_data_re = re.compile(r"~data=.+?!")
@@ -35,7 +36,10 @@ class Pluzz(Plugin):
     _api_schema = validate.Schema({
         'videos': validate.all(
             [{
-                'format': validate.text,
+                'format': validate.any(
+                    None,
+                    validate.text
+                ),
                 'url': validate.any(
                     None,
                     validate.url(),
@@ -96,6 +100,8 @@ class Pluzz(Plugin):
             match = self._jeunesse_video_id_re.search(res.text)
         elif 'france3-regions.francetvinfo.fr' in self.url:
             match = self._f3_regions_video_id_re.search(res.text)
+        elif 'sport.francetvinfo.fr' in self.url:
+            match = self._sport_video_id_re.search(res.text)
         if match is None:
             return
         video_id = match.group('video_id')
diff --git a/src/streamlink_cli/argparser.py b/src/streamlink_cli/argparser.py
index 40442d48..df890818 100644
--- a/src/streamlink_cli/argparser.py
+++ b/src/streamlink_cli/argparser.py
@@ -163,10 +163,8 @@ positional.add_argument(
     help="""
     A URL to attempt to extract streams from.
 
-    Usually, the protocol of http(s) URLs can be omitted ("https://"),
-    depending on the implementation of the plugin being used.
-
-    Alternatively, the URL can also be specified by using the --url option.
+    If it's a HTTP URL then "http://" can be omitted.
+    The URL can also be specified using the --url option.
     """
 )
 positional.add_argument(
@@ -177,13 +175,13 @@ positional.add_argument(
     help="""
     Stream to play.
 
-    Use "best" or "worst" for selecting the highest or lowest available quality.
+    Use "best" or "worst" for highest or lowest quality available.
 
     Fallback streams can be specified by using a comma-separated list:
 
       "720p,480p,best"
 
-    If no stream is specified and --default-stream is not used, then a
+    If no stream is specified and --default-stream is not used then a
     list of available streams will be printed.
     """
 )
@@ -226,8 +224,7 @@ general.add_argument(
     "--can-handle-url-no-redirect",
     metavar="URL",
     help="""
-    Same as --can-handle-url but without following redirects
-    when looking up the URL.
+    Same as --can-handle-url but without following redirects when looking up the URL.
     """
 )
 general.add_argument(
@@ -278,7 +275,7 @@ general.add_argument(
     help="""
     Enable or disable the automatic check for a new version of Streamlink.
 
-    Default is "no".
+    Default is no
     """
 )
 general.add_argument(
@@ -297,7 +294,7 @@ general.add_argument(
     subtitle and audio language.
 
     The locale is formatted as [language_code]_[country_code],
-    eg. en_US or es_ES.
+     eg. eg. en_US or es_ES
 
     Default is system locale.
     """
@@ -333,7 +330,7 @@ player.add_argument(
       streamlink --player "vlc --file-caching=5000" <url> <quality>
 
     As an alternative to this, see the --player-args parameter,
-    which does not log any custom player arguments.
+    which does not log the customer player arguments.
     """
 )
 player.add_argument(
@@ -343,8 +340,7 @@ player.add_argument(
     help="""
     This option allows you to customize the default arguments which
     are put together with the value of --player to create a command
-    to execute. Unlike the --player parameter, custom player
-    arguments will not be logged.
+    to execute.
 
     This value can contain formatting variables surrounded by curly
     braces, {{ and }}. If you need to include a brace character, it
@@ -361,11 +357,6 @@ player.add_argument(
     need to add arguments after the filename.
 
     Default is "{0}".
-
-    Example:
-
-      streamlink -p vlc -a "--play-and-exit {{filename}}" <url> <quality>
-
     """.format(DEFAULT_PLAYER_ARGUMENTS)
 )
 player.add_argument(
@@ -402,6 +393,7 @@ player.add_argument(
     This makes it possible to handle stream disconnects if your player
     is capable of reconnecting to a HTTP stream. This is usually
     done by setting your player to a "repeat mode".
+
     """
 )
 player.add_argument(
@@ -493,11 +485,9 @@ stream.add_argument(
     help="""
     A URL to attempt to extract streams from.
 
-    Usually, the protocol of http(s) URLs can be omitted (https://),
-    depending on the implementation of the plugin being used.
+    If it's a HTTP URL then "http://" can be omitted.
 
-    This is an alternative to setting the URL using a positional argument
-    and can be useful if set in a config file.
+    This is an alternative to setting the URL using a positional argument.
     """
 )
 stream.add_argument(
@@ -505,16 +495,7 @@ stream.add_argument(
     type=comma_list,
     metavar="STREAM",
     help="""
-    Stream to play.
-
-    Use "best" or "worst" for selecting the highest or lowest available quality.
-
-    Fallback streams can be specified by using a comma-separated list:
-
-      "720p,480p,best"
-
-    This is an alternative to setting the stream using a positional argument
-    and can be useful if set in a config file.
+    Open this stream when no stream argument is specified, e.g. "best".
     """
 )
 stream.add_argument(
@@ -547,8 +528,7 @@ stream.add_argument(
     The order will be used to separate streams when there are multiple
     streams with the same name but different stream types. Any stream type
     not listed will be omitted from the available streams list.  A ``*``
-    can be used as a wildcard to match any other type of stream,
-    eg. muxed-stream.
+    can be used as a wildcard to match any other type of stream, eg. muxed-stream.
 
     Default is "rtmp,hls,hds,http,akamaihd,*".
     """
@@ -703,11 +683,9 @@ transport.add_argument(
     type=str,
     metavar="CODE",
     help="""
-    Selects a specific audio source by language code
-    when multiple audio sources are available.
+    Selects a specific audio source, by language code, when multiple audio sources are available.
 
-    Note: This is only useful in special circumstances
-    where the regular locale option fails.
+    Note: This is only useful in special circumstances where the regular locale option fails.
     """)
 transport.add_argument(
     "--http-stream-timeout",
@@ -823,7 +801,6 @@ transport.add_argument(
 
     This is generic option used by streams not covered by other options,
     such as stream protocols specific to plugins, e.g. UStream.
-
     Default is 60.0.
     """)
 transport.add_argument(
@@ -837,7 +814,7 @@ transport.add_argument(
     "--subprocess-cmdline", "--cmdline", "-c",
     action="store_true",
     help="""
-    Print the command-line used internally to play the stream.
+    Print command-line used internally to play stream.
 
     This is only available on RTMP streams.
     """
@@ -867,7 +844,7 @@ transport.add_argument(
     "--ffmpeg-ffmpeg",
     metavar="FILENAME",
     help="""
-    FFMPEG is used to access or mux separate video and audio streams.
+    FFMPEG is used to access mux separate video and audio streams.
     You can specify the location of the ffmpeg executable if it is
     not in your PATH.
 
@@ -878,7 +855,7 @@ transport.add_argument(
     "--ffmpeg-verbose",
     action="store_true",
     help="""
-    Write the console output from ffmpeg to the console.
+    Write the console output from ffmpeg to the console
     """
 )
 transport.add_argument(
@@ -886,16 +863,14 @@ transport.add_argument(
     type=str,
     metavar="PATH",
     help="""
-    Path to write the output from the ffmpeg console.
+    Path to write the output from the ffmpeg console
     """
 )
 transport.add_argument(
     "--ffmpeg-video-transcode",
     metavar="CODEC",
     help="""
-    When muxing streams transcode the video to this CODEC.
-
-    Default is "copy".
+    When muxing streams transcode the video to this CODEC, defaults to copy (no transcode)
 
     Example: "h264"
     """
@@ -904,9 +879,7 @@ transport.add_argument(
     "--ffmpeg-audio-transcode",
     metavar="CODEC",
     help="""
-    When muxing streams transcode the audio to this CODEC.
-
-    Default is "copy".
+    When muxing streams transcode the audio to this CODEC, defaults to copy (no transcode)
 
     Example: "aac"
     """
@@ -919,7 +892,7 @@ http.add_argument(
     help="""
     A HTTP proxy to use for all HTTP requests.
 
-    Example: "http://hostname:port/"
+    Example: http://hostname:port/
     """
 )
 http.add_argument(
@@ -928,7 +901,7 @@ http.add_argument(
     help="""
     A HTTPS capable proxy to use for all HTTPS requests.
 
-    Example: "https://hostname:port/"
+    Example: http://hostname:port/
     """
 )
 http.add_argument(
@@ -1030,7 +1003,8 @@ plugin.add_argument(
     help="""
     Attempts to load plugins from these directories.
 
-    Multiple directories can be used by separating them with a semicolon.
+    Multiple directories can be used by separating them with a
+    semi-colon.
     """
 )
 plugin.add_argument(
@@ -1063,6 +1037,7 @@ plugin.add_argument(
     Note: This method is the old and clunky way of authenticating with
     Twitch, using --twitch-oauth-authenticate is the recommended and
     simpler way of doing it now.
+
     """
 )
 plugin.add_argument(
@@ -1111,7 +1086,7 @@ plugin.add_argument(
     metavar="SESSION_ID",
     help="""
     Set a specific session ID for crunchyroll, can be used to bypass
-    region restrictions.
+    region restrictions
     """
 )
 plugin.add_argument(
@@ -1132,8 +1107,7 @@ plugin.add_argument(
     "--schoolism-email",
     metavar="EMAIL",
     help="""
-    The email associated with your Schoolism account,
-    required to access any Schoolism stream.
+    The email associated with your Schoolism account, required to access any Schoolism stream.
     """
 )
 plugin.add_argument(
@@ -1149,23 +1123,23 @@ plugin.add_argument(
     default=1,
     metavar="PART",
     help="""
-    Play part number PART of the lesson.
+    Play part number PART of the lesson
 
-    Defaults is 1.
+    Defaults is 1
     """
 )
 plugin.add_argument(
     "--daisuki-mux-subtitles",
     action="store_true",
     help="""
-    Automatically mux available subtitles in to the output stream.
+    Automatically mux available subtitles in to the output stream
     """
 )
 plugin.add_argument(
     "--rtve-mux-subtitles",
     action="store_true",
     help="""
-    Automatically mux available subtitles in to the output stream.
+    Automatically mux available subtitles in to the output stream
     """
 )
 plugin.add_argument(
@@ -1174,16 +1148,16 @@ plugin.add_argument(
     choices=["en", "ja", "english", "japanese"],
     default="english",
     help="""
-    The audio language to use for Funimation streams; japanese or english.
+    The audio language to use for Funimation streams; japanese or english
 
-    Default is "english".
+    Default is english
     """
 )
 plugin.add_argument(
     "--funimation-mux-subtitles",
     action="store_true",
     help="""
-    Enable automatically including available subtitles in to the output stream.
+    Enable automatically including available subtitles in to the output stream
     """
 )
 plugin.add_argument(
@@ -1204,15 +1178,14 @@ plugin.add_argument(
     "--pluzz-mux-subtitles",
     action="store_true",
     help="""
-    Automatically mux available subtitles in to the output stream.
+    Automatically mux available subtitles in to the output stream
     """
 )
 plugin.add_argument(
     "--wwenetwork-email",
     metavar="EMAIL",
     help="""
-    The email associated with your WWE Network account,
-    required to access any WWE Network stream.
+    The email associated with your WWE Network account, required to access any WWE Network stream.
     """
 )
 plugin.add_argument(
@@ -1240,7 +1213,7 @@ plugin.add_argument(
     "--npo-subtitles",
     action="store_true",
     help="""
-    Include subtitles for the deaf or hard of hearing, if available.
+    Include subtitles for the deaf or hard of hearing, if available
     """
 )
 plugin.add_argument(


Test patch:
diff --git a/tests/test_plugin_pluzz.py b/tests/test_plugin_pluzz.py
index 9528479d..5a709c31 100644
--- a/tests/test_plugin_pluzz.py
+++ b/tests/test_plugin_pluzz.py
@@ -20,6 +20,9 @@ class TestPluginPluzz(unittest.TestCase):
         self.assertTrue(Pluzz.can_handle_url("http://www.zouzous.fr/heros/oui-oui"))
         self.assertTrue(Pluzz.can_handle_url("http://www.zouzous.fr/heros/marsupilami-1"))
         self.assertTrue(Pluzz.can_handle_url("http://france3-regions.francetvinfo.fr/bourgogne-franche-comte/tv/direct/franche-comte"))
+        self.assertTrue(Pluzz.can_handle_url("http://sport.francetvinfo.fr/roland-garros/direct"))
+        self.assertTrue(Pluzz.can_handle_url("http://sport.francetvinfo.fr/roland-garros/live-court-3"))
+        self.assertTrue(Pluzz.can_handle_url("http://sport.francetvinfo.fr/roland-garros/andy-murray-gbr-1-andrey-kuznetsov-rus-1er-tour-court-philippe-chatrier"))
 
         # shouldn't match
         self.assertFalse(Pluzz.can_handle_url("http://www.france.tv/"))