Repository: streamlink/streamlink
Pull request #3825 (2021-06-24 18:30:34)

plugins.mediaklikk: rewrite plugin
Closes #3824

This rewrites the plugin with proper data extraction and validation schemas, and adds support for these sites, including VODs:
- Mediaklikk.hu
- M4sport.hu
- Hirado.hu
- Petofilive.hu

https://dunamsz.hu
https://en.wikipedia.org/wiki/MTVA_(Hungary)#Internet

The sports channels are geo-locked, but it should find the correct HLS streams on all URLs listed in the tests nevertheless.

back-to: > The sports channels are geo-locked, but it should find the correct HLS streams on all URLs listed in the tests nevertheless.

```
https://c202-node61-cdn.connectmedia.hu/4501/vod/_definst_/r/mtva/2021/06/10/euro2020_bumper/index.smil/playlist.m3u8
https://c201-node61-cdn.connectmedia.hu/1104/xxx/xxx/index.m3u8?v=5iip:xxx
https://c202-node61-cdn.connectmedia.hu/4501/vod/_definst_/r/mtva/2021/06/10/euro2020_bumper/index.smil/playlist.m3u8

[cli][info] Available streams: 400k_alt (worst), 400k, 180p, 270p, 800k_alt, 800k, 360p, 1200k_alt, 1200k, 480p, 1600k_alt, 1600k, 720p, 3000k_alt, 3000k (best)
```

you need to skip vod urls for `https://m4sport.hu/elo/mtv4live/`

Patch:
diff --git a/src/streamlink/plugins/mediaklikk.py b/src/streamlink/plugins/mediaklikk.py
index 98a8fa6f..1a71fed0 100644
--- a/src/streamlink/plugins/mediaklikk.py
+++ b/src/streamlink/plugins/mediaklikk.py
@@ -1,10 +1,12 @@
 import logging
 import re
+from urllib.parse import unquote, urlparse
 
-from streamlink.exceptions import PluginError
 from streamlink.plugin import Plugin
+from streamlink.plugin.api import validate
 from streamlink.stream import HLSStream
-from streamlink.utils.url import update_scheme
+from streamlink.utils import parse_json, update_scheme
+
 
 log = logging.getLogger(__name__)
 
@@ -12,34 +14,68 @@ log = logging.getLogger(__name__)
 class Mediaklikk(Plugin):
     PLAYER_URL = "https://player.mediaklikk.hu/playernew/player.php"
 
-    _url_re = re.compile(r"https?://(?:www\.)?(?:mediaklikk|m4sport)\.hu/(?:[\w\-]+\-)?elo/?")
-    _id_re = re.compile(r'"streamId":"(\w+)"')
-    _file_re = re.compile(r'"file":\s*"([\w\./\\=:\-\?]+)"')
+    _url_re = re.compile(r"https?://(?:www\.)?(?:mediaklikk|m4sport|hirado|petofilive)\.hu/")
+
+    _re_player_manager = re.compile(r"""
+        mtva_player_manager\.player\s*\(\s*
+            document\.getElementById\(\s*"\w+"\s*\)\s*,\s*
+            (?P<json>{.*?})\s*
+        \)\s*;
+    """, re.VERBOSE | re.DOTALL)
+    _re_player_json = re.compile(r"pl\.setup\s*\(\s*(?P<json>{.*?})\s*\)\s*;", re.DOTALL)
 
     @classmethod
     def can_handle_url(cls, url):
         return cls._url_re.match(url) is not None
 
     def _get_streams(self):
-        # get the stream id
-        res = self.session.http.get(self.url)
-        m = self._id_re.search(res.text)
-        if not m:
-            raise PluginError("Stream ID could not be extracted.")
-
-        # get the m3u8 file url
-        params = {
-            "video": m.group(1),
+        params = self.session.http.get(self.url, schema=validate.Schema(
+            validate.transform(self._re_player_manager.search),
+            validate.any(None, validate.all(
+                validate.get("json"),
+                validate.transform(parse_json),
+                {
+                    "contentId": validate.any(str, int),
+                    validate.optional("streamId"): str,
+                    validate.optional("idec"): str,
+                    validate.optional("token"): str
+                }
+            ))
+        ))
+        if not params:
+            log.error("Could not find player manager data")
+            return
+
+        params.update({
+            "video": (unquote(params.pop("token"))
+                      if params.get("token") is not None else
+                      params.pop("streamId")),
             "noflash": "yes",
-        }
-        res = self.session.http.get(self.PLAYER_URL, params=params)
-        m = self._file_re.search(res.text)
-        if m:
-            url = update_scheme("https://",
-                                m.group(1).replace("\\/", "/"))
-
-            log.debug("URL={0}".format(url))
-            return HLSStream.parse_variant_playlist(self.session, url)
+            "embedded": "0",
+        })
+
+        url_parsed = urlparse(self.url)
+        skip_vods = url_parsed.netloc.endswith("m4sport.hu") and url_parsed.path.startswith("/elo")
+
+        self.session.http.headers.update({"Referer": self.url})
+        playlists = self.session.http.get(self.PLAYER_URL, params=params, schema=validate.Schema(
+            validate.transform(self._re_player_json.search),
+            validate.any(None, validate.all(
+                validate.get("json"),
+                validate.transform(parse_json),
+                {"playlist": [{
+                    "file": validate.url(),
+                    "type": str
+                }]},
+                validate.get("playlist"),
+                validate.filter(lambda p: p["type"] == "hls"),
+                validate.filter(lambda p: not skip_vods or "vod" not in p["file"]),
+                validate.map(lambda p: update_scheme(self.url, p["file"]))
+            ))
+        ))
+
+        for url in playlists or []:
+            yield from HLSStream.parse_variant_playlist(self.session, url).items()
 
 
 __plugin__ = Mediaklikk


Test patch:
diff --git a/tests/plugins/test_mediaklikk.py b/tests/plugins/test_mediaklikk.py
index ef17d4f1..e5725565 100644
--- a/tests/plugins/test_mediaklikk.py
+++ b/tests/plugins/test_mediaklikk.py
@@ -10,12 +10,12 @@ class TestPluginCanHandleUrlMediaklikk(PluginCanHandleUrl):
         'https://www.mediaklikk.hu/duna-world-radio-elo',
         'https://www.mediaklikk.hu/m1-elo',
         'https://www.mediaklikk.hu/m2-elo',
+        'https://mediaklikk.hu/video/hirado-2021-06-24-i-adas-6/',
         'https://m4sport.hu/elo/',
         'https://m4sport.hu/elo/?channelId=m4sport+',
         'https://m4sport.hu/elo/?showchannel=mtv4plus',
-    ]
-
-    should_not_match = [
-        'https://www.mediaklikk.hu',
-        'https://m4sport.hu',
+        'https://m4sport.hu/euro2020-video/goool2-13-resz',
+        'https://hirado.hu/videok/',
+        'https://hirado.hu/videok/nemzeti-sporthirado-2021-06-24-i-adas-2/',
+        'https://petofilive.hu/video/2021/06/23/the-anahit-klip-limited-edition/',
     ]