Repository: streamlink/streamlink

Issue:
plugins.turkuvaz.py: URLs seems outdated
### Checklist

- [X] This is a plugin issue and not a different kind of issue
- [X] [I have read the contribution guidelines](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink)
- [X] [I have checked the list of open and recently closed plugin issues](https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22plugin+issue%22)
- [X] [I have checked the commit log of the master branch](https://github.com/streamlink/streamlink/commits/master)

### Streamlink version

Latest stable release

### Description

The pages for the Turkish streams like ATV and A2TV have been changed by the provider. Unlike before,now the streams are on the same page, the only difference is the directory after the domain directory, 
e.g.: 
https://www.atv.com.tr/canli-yayin
https://www.atv.com.tr/a2tv/canli-yayin.

Unfortunately, this means that only the stream for ATV is fetched not A2TV even though you gave the correct FQDNfor A2TV.

And ATV Avrupa is no longer recognized. you get the error message "No plugin can handle URL: https://www.atvavrupa.tv/canli-yayin" although the plugin apparently exists.

### Debug log

```text
error: No plugin can handle URL: https://www.atvavrupa.tv/canli-yayin
```


Discussion:
mkbloke: > ```shell
> ./script/test-plugin-urls.py turkuvaz
> ...

Thanks, I'd forgotten about that.

> First VOD listed on that redirected site returns an mp4 container instead of an HLS playlist. Since the plugin matches those URLs and since a fix is simple, this should be supported in addition to the live streams. The plugin metadata would also need an update.
> ...
> Sending a `HEAD` request returns the correct `Content-Type` header.

My bad - I tested some non-video pages, but not VOD pages. :-/

I did go the route you suggested and got it working, but then I decided to go all-in, which also turned out to be very simple, by just using `.video.VideoSmilUrl` instead of `.video.VideoUrl`.

Added `id` and `title` metadata.


Patch:
diff --git a/src/streamlink/plugins/turkuvaz.py b/src/streamlink/plugins/turkuvaz.py
index d6033af4..f259b9c6 100644
--- a/src/streamlink/plugins/turkuvaz.py
+++ b/src/streamlink/plugins/turkuvaz.py
@@ -10,58 +10,85 @@ $url atvavrupa.tv
 $url minikacocuk.com.tr
 $url minikago.com.tr
 $url vavtv.com.tr
-$type live
+$type live, vod
 $region various
 """
 
+import logging
 import re
-from urllib.parse import urlparse
 
 from streamlink.plugin import Plugin, pluginmatcher
 from streamlink.plugin.api import validate
 from streamlink.stream.hls import HLSStream
 
 
+log = logging.getLogger(__name__)
+
+
 @pluginmatcher(re.compile(r"""
     https?://(?:www\.)?
     (?:
-        (?:
-            atvavrupa\.tv
-            |
-            (?:atv|a2tv|ahaber|aspor|minikago|minikacocuk|anews)\.com\.tr
-        )/webtv/(?:live-broadcast|canli-yayin)
-        |
-        ahaber\.com\.tr/video/canli-yayin
+        atvavrupa\.tv
         |
-        (?:atv|apara|vavtv)\.com\.tr/canli-yayin
-        |
-        atv\.com\.tr/a2tv/canli-yayin
+        (?:a2tv|ahaber|anews|apara|aspor|atv|minikacocuk|minikago|vavtv)\.com\.tr
     )
 """, re.VERBOSE))
 class Turkuvaz(Plugin):
-    _URL_HLS = "https://trkvz-live.ercdn.net/{channel}/{channel}.m3u8"
-    _URL_TOKEN = "https://securevideotoken.tmgrup.com.tr/webtv/secure"
+    def _get_streams(self):
+        _find_and_get_attrs = validate.Schema(
+            validate.xml_find(".//div[@data-videoid][@data-websiteid]"),
+            validate.union_get("data-videoid", "data-websiteid"),
+        )
 
-    _MAP_HOST_TO_CHANNEL = {
-        "atv": "atvhd",
-        "ahaber": "ahaberhd",
-        "apara": "aparahd",
-        "aspor": "asporhd",
-        "anews": "anewshd",
-        "vavtv": "vavtv",
-        "minikacocuk": "minikagococuk",
-    }
+        id_data = self.session.http.get(
+            self.url,
+            schema=validate.Schema(
+                validate.parse_html(),
+                validate.any(
+                    _find_and_get_attrs,
+                    validate.all(
+                        validate.xml_xpath_string(
+                            ".//script[contains(text(),'data-videoid') and contains(text(),'data-websiteid')]/text()",
+                        ),
+                        validate.none_or_all(
+                            str,
+                            validate.regex(re.compile(r"""var\s+tmdPlayer\s*=\s*(?P<q>["'])(.*?)(?P=q)""")),
+                            validate.get(0),
+                            validate.parse_html(),
+                            _find_and_get_attrs,
+                        ),
+                    ),
+                ),
+            ),
+        )
 
-    def _get_streams(self):
-        hostname = re.sub(r"^www\.", "", urlparse(self.url).netloc).split(".")[0]
+        if not id_data:
+            return
+
+        video_id, website_id = id_data
+        log.debug(f"video_id={video_id}")
+        log.debug(f"website_id={website_id}")
 
-        # remap the domain to channel
-        channel = self._MAP_HOST_TO_CHANNEL.get(hostname, hostname)
-        hls_url = self._URL_HLS.format(channel=channel)
+        self.id, self.title, hls_url = self.session.http.get(
+            f"https://videojs.tmgrup.com.tr/getvideo/{website_id}/{video_id}",
+            schema=validate.Schema(
+                validate.parse_json(),
+                {
+                    "success": True,
+                    "video": {
+                        "VideoId": str,
+                        "Title": str,
+                        "VideoSmilUrl": validate.url(),
+                    },
+                },
+                validate.get("video"),
+                validate.union_get("VideoId", "Title", "VideoSmilUrl"),
+            ),
+        )
+        log.debug(f"hls_url={hls_url}")
 
-        # get the secure HLS URL
         secure_hls_url = self.session.http.get(
-            self._URL_TOKEN,
+            "https://securevideotoken.tmgrup.com.tr/webtv/secure",
             params=f"url={hls_url}",
             headers={"Referer": self.url},
             schema=validate.Schema(
@@ -73,6 +100,7 @@ class Turkuvaz(Plugin):
                 validate.get("Url"),
             ),
         )
+        log.debug(f"secure_hls_url={secure_hls_url}")
 
         if secure_hls_url:
             return HLSStream.parse_variant_playlist(self.session, secure_hls_url)


Test patch:
diff --git a/tests/plugins/test_turkuvaz.py b/tests/plugins/test_turkuvaz.py
index 15a1d962..9480b659 100644
--- a/tests/plugins/test_turkuvaz.py
+++ b/tests/plugins/test_turkuvaz.py
@@ -6,16 +6,35 @@ class TestPluginCanHandleUrlTurkuvaz(PluginCanHandleUrl):
     __plugin__ = Turkuvaz
 
     should_match = [
-        "https://www.ahaber.com.tr/video/canli-yayin",
-        "https://www.ahaber.com.tr/webtv/canli-yayin",
-        "https://www.anews.com.tr/webtv/live-broadcast",
-        "https://www.apara.com.tr/canli-yayin",
-        "https://www.aspor.com.tr/webtv/canli-yayin",
-        "https://www.atv.com.tr/canli-yayin",
-        "https://www.atv.com.tr/a2tv/canli-yayin",
-        "https://www.atv.com.tr/webtv/canli-yayin",
-        "https://www.atvavrupa.tv/webtv/canli-yayin",
-        "https://www.minikacocuk.com.tr/webtv/canli-yayin",
-        "https://www.minikago.com.tr/webtv/canli-yayin",
-        "https://www.vavtv.com.tr/canli-yayin",
+        # canonical live links
+        "https://www.ahaber.com.tr/video/canli-yayin", # ahbr
+        "https://www.ahaber.com.tr/canli-yayin-aspor.html", # ahbr: aspor
+        "https://www.ahaber.com.tr/canli-yayin-anews.html", # ahbr: anews
+        "https://www.ahaber.com.tr/canli-yayin-a2tv.html", # ahbr: a2tv
+        "https://www.ahaber.com.tr/canli-yayin-minikago.html", # ahbr: minika go
+        "https://www.ahaber.com.tr/canli-yayin-minikacocuk.html", # ahbr: minika cocuk
+        "https://www.anews.com.tr/webtv/live-broadcast", # anews
+        "https://www.apara.com.tr/canli-yayin", # apara
+        "https://www.aspor.com.tr/webtv/canli-yayin", # aspor
+        "https://www.atv.com.tr/canli-yayin", # atv
+        "https://www.atv.com.tr/a2tv/canli-yayin", # a2tv
+        "https://www.atvavrupa.tv/canli-yayin", # atvavrupa
+        "https://www.minikacocuk.com.tr/webtv/canli-yayin", # minika cocuk
+        "https://www.minikago.com.tr/webtv/canli-yayin", # minika go
+        "https://vavtv.com.tr/canli-yayin", # vavtv
+
+        #vods
+        "https://www.ahaber.com.tr/video/yasam-videolari/samsunda-sel-sularindan-kacma-ani-kamerada",
+        "https://www.anews.com.tr/webtv/world/pro-ukraine-militia-says-it-has-captured-russian-soldiers",
+        "https://www.apara.com.tr/video/ekonomide-bugun/bist-100de-kar-satislari-derinlesir-mi",
+        "https://www.aspor.com.tr/webtv/galatasaray/galatasaraydan-forma-tanitiminda-fenerbahceye-gonderme",
+        "https://www.atv.com.tr/kurulus-osman/127-bolum/izle",
+        "https://www.atvavrupa.tv/diger-program//ozelvideo/izle",
+        "https://www.minikacocuk.com.tr/webtv/olly/bolum/olly-eylul-tanitim",
+        "https://www.minikago.com.tr/webtv/mondo-yan/bolum/mondo-yan-eylul-tanitim",
+        "https://vavtv.com.tr/vavradyo/video/guncel/munafiklarin-ozellikleri-nelerdir",
+
+        # other links for info/doc
+        "https://www.atv.com.tr/webtv/canli-yayin", # redirect to atv.com.tr/canli-yayin
+        "https://www.ahaber.com.tr/canli-yayin-atv.html", # links to atv.com.tr/webtv/canli-yayin
     ]