Repository: streamlink/streamlink

Issue:
plugins.dogan: CnnTurk and KanalD doesn't show live TV
### 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

stulluk ~ $  streamlink --version
streamlink 5.4.0
stulluk ~ $

Thank you very much for this plugin. it works beautifully with "teve2" as follows: 

`streamlink https://www.teve2.com.tr/canli-yayin best`

And it shows the live TV show.

However, it doesn't show liveTV streams of CnnTurk and KanalD.

To reproduce, one can compare played streams by checking live TV from the corresponding website via web browser.

I provide debug log below, but I am not sure if this is helpful at all, because below debug log is just fine, however played video is not live TV (some other advertisement is being played)



### Debug log

```text
stulluk ~ $  streamlink https://www.kanald.com.tr/canli-yayin best -l all
[13:14:12.746759][cli][debug] OS:         Linux-6.2.10-060210-generic-x86_64-with-glibc2.35
[13:14:12.746857][cli][debug] Python:     3.10.6
[13:14:12.746898][cli][debug] Streamlink: 5.4.0
[13:14:12.747355][cli][debug] Dependencies:
[13:14:12.749140][cli][debug]  certifi: 2020.6.20
[13:14:12.749491][cli][debug]  isodate: 0.6.1
[13:14:12.749835][cli][debug]  lxml: 4.8.0
[13:14:12.750241][cli][debug]  pycountry: 20.7.3
[13:14:12.750469][cli][debug]  pycryptodome: 3.17
[13:14:12.751129][cli][debug]  PySocks: 1.7.1
[13:14:12.751392][cli][debug]  requests: 2.28.2
[13:14:12.753761][cli][debug]  urllib3: 1.26.5
[13:14:12.754057][cli][debug]  websocket-client: 1.2.3
[13:14:12.754299][cli][debug]  importlib-metadata: 4.6.4
[13:14:12.754439][cli][debug] Arguments:
[13:14:12.754480][cli][debug]  url=https://www.kanald.com.tr/canli-yayin
[13:14:12.754517][cli][debug]  stream=['best']
[13:14:12.754558][cli][debug]  --loglevel=all
[13:14:12.754722][cli][info] Found matching plugin dogan for URL https://www.kanald.com.tr/canli-yayin
[13:14:12.864279][plugins.dogan][debug] Loading content: 542410a361361f36f4c3fcf1
[13:14:12.951559][utils.l10n][debug] Language code: en_US
[13:14:13.319823][stream.hls_playlist][all] #EXT-X-VERSION:3
[13:14:13.319996][stream.hls_playlist][all] #EXT-X-STREAM-INF:BANDWIDTH=510400,RESOLUTION=640x360,CODECS="avc1.42c01e,mp4a.40.2"
[13:14:13.320122][stream.hls_playlist][all] kanald-v0/playlist.m3u8?live=true&app=com.kanald&min=0&max=1250
[13:14:13.320311][stream.hls_playlist][all] #EXT-X-STREAM-INF:BANDWIDTH=1445400,RESOLUTION=1280x720,CODECS="avc1.4d401f,mp4a.40.2"
[13:14:13.320415][stream.hls_playlist][all] kanald-v1/playlist.m3u8?live=true&app=com.kanald&min=0&max=1250
[13:14:13.321506][cli][info] Available streams: 360p (worst), 720p (best)
[13:14:13.321740][cli][info] Opening stream: 720p (hls)
[13:14:13.321884][cli][info] Starting player: /usr/bin/vlc
[13:14:13.322923][stream.hls][debug] Reloading playlist
[13:14:13.323106][cli][debug] Pre-buffering 8192 bytes
[13:14:13.511675][stream.hls_playlist][all] #EXT-X-VERSION:3
[13:14:13.511825][stream.hls_playlist][all] #EXT-X-TARGETDURATION:8
[13:14:13.511903][stream.hls_playlist][all] #EXT-X-MEDIA-SEQUENCE:593337
[13:14:13.511969][stream.hls_playlist][all] #EXTINF:8.000000,
[13:14:13.512038][stream.hls_playlist][all] kanald_2023_04_17_11_49_07_593337.ts?live=true&app=com.kanald&min=0&max=1250
[13:14:13.512150][stream.hls_playlist][all] #EXTINF:8.000000,
[13:14:13.512224][stream.hls_playlist][all] kanald_2023_04_17_11_49_15_593338.ts?live=true&app=com.kanald&min=0&max=1250
[13:14:13.512323][stream.hls_playlist][all] #EXTINF:8.000000,
[13:14:13.512392][stream.hls_playlist][all] kanald_2023_04_17_11_49_23_593339.ts?live=true&app=com.kanald&min=0&max=1250
[13:14:13.512481][stream.hls_playlist][all] #EXTINF:8.000000,
[13:14:13.512547][stream.hls_playlist][all] kanald_2023_04_17_11_49_31_593340.ts?live=true&app=com.kanald&min=0&max=1250
[13:14:13.512635][stream.hls_playlist][all] #EXTINF:8.000000,
[13:14:13.512707][stream.hls_playlist][all] kanald_2023_04_17_11_49_39_593341.ts?live=true&app=com.kanald&min=0&max=1250
[13:14:13.512816][stream.hls_playlist][all] #EXTINF:8.000000,
[13:14:13.512887][stream.hls_playlist][all] kanald_2023_04_17_11_49_47_593342.ts?live=true&app=com.kanald&min=0&max=1250
[13:14:13.512978][stream.hls_playlist][all] #EXTINF:8.000000,
[13:14:13.513043][stream.hls_playlist][all] kanald_2023_04_17_11_49_55_593343.ts?live=true&app=com.kanald&min=0&max=1250
[13:14:13.513133][stream.hls_playlist][all] #EXTINF:8.000000,
[13:14:13.513183][stream.hls_playlist][all] kanald_2023_04_17_11_50_03_593344.ts?live=true&app=com.kanald&min=0&max=1250
[13:14:13.513240][stream.hls_playlist][all] #EXT-X-ENDLIST
[13:14:13.513309][stream.hls][debug] First Sequence: 593337; Last Sequence: 593344
[13:14:13.513355][stream.hls][debug] Start offset: 0; Duration: None; Start Sequence: 593337; End Sequence: 593344
[13:14:13.513394][stream.hls][debug] Adding segment 593337 to queue
[13:14:13.513671][stream.hls][debug] Adding segment 593338 to queue
[13:14:13.513793][stream.hls][debug] Adding segment 593339 to queue
[13:14:13.513853][stream.hls][debug] Adding segment 593340 to queue
[13:14:13.513918][stream.hls][debug] Adding segment 593341 to queue
[13:14:13.513999][stream.hls][debug] Adding segment 593342 to queue
[13:14:13.514133][stream.hls][debug] Adding segment 593343 to queue
[13:14:13.514324][stream.hls][debug] Adding segment 593344 to queue
[13:14:13.515244][stream.segmented][debug] Closing worker thread
[13:14:17.113485][stream.hls][debug] Writing segment 593337 to output
[13:14:17.114717][stream.hls][debug] Segment 593337 complete
[13:14:17.114984][cli.output][debug] Opening subprocess: /usr/bin/vlc --input-title-format https://www.kanald.com.tr/canli-yayin -
[13:14:17.617393][cli][debug] Writing stream to output
[13:14:19.328000][stream.hls][debug] Writing segment 593338 to output
[13:14:19.328587][stream.hls][debug] Segment 593338 complete
[13:14:22.449293][stream.hls][debug] Writing segment 593339 to output
[13:14:22.450269][stream.hls][debug] Segment 593339 complete
[13:14:27.685175][stream.hls][debug] Writing segment 593340 to output
[13:14:27.686156][stream.hls][debug] Segment 593340 complete
[13:14:29.403924][stream.hls][debug] Writing segment 593341 to output
[13:14:29.404655][stream.hls][debug] Segment 593341 complete
[13:14:30.516949][stream.hls][debug] Writing segment 593342 to output
[13:14:30.517549][stream.hls][debug] Segment 593342 complete
[13:14:31.776268][stream.hls][debug] Writing segment 593343 to output
[13:14:31.777112][stream.hls][debug] Segment 593343 complete
[13:14:33.022958][stream.hls][debug] Writing segment 593344 to output
[13:14:33.023646][stream.hls][debug] Segment 593344 complete
[13:14:33.023785][stream.segmented][debug] Closing writer thread
[13:14:33.024727][cli][info] Stream ended
[13:14:33.525547][cli][info] Closing currently open stream...
stulluk ~ $
```



Discussion:

Resolving patch:
diff --git a/src/streamlink/plugins/dogan.py b/src/streamlink/plugins/dogan.py
--- a/src/streamlink/plugins/dogan.py
+++ b/src/streamlink/plugins/dogan.py
@@ -33,34 +33,41 @@ class Dogan(Plugin):
     ]
     API_URL_OLD = "/actions/media?id={id}"
 
-    def _get_content_id(self):
-        return self.session.http.get(
-            self.url,
-            schema=validate.Schema(
-                validate.parse_html(),
-                validate.any(
-                    validate.all(
-                        validate.xml_xpath_string("""
-                            .//div[@data-id][
-                                @data-live
-                                or @id='video-element'
-                                or @id='player-container'
-                                or contains(@class, 'player-container')
-                            ][1]/@data-id
-                        """),
-                        str,
-                    ),
-                    # xpath query needs to have a lower priority
-                    validate.all(
-                        validate.xml_xpath_string(
-                            ".//body[@data-content-id][1]/@data-content-id",
-                        ),
-                        str,
+    @staticmethod
+    def _get_hls_url(root):
+        schema = validate.Schema(
+            validate.xml_xpath_string(".//*[@data-live][contains(@data-url,'.m3u8')]/@data-url"),
+        )
+
+        return schema.validate(root)
+
+    @staticmethod
+    def _get_content_id(root):
+        schema=validate.Schema(
+            validate.any(
+                validate.all(
+                    validate.xml_xpath_string("""
+                        .//div[@data-id][
+                            @data-live
+                            or @id='video-element'
+                            or @id='player-container'
+                            or contains(@class, 'player-container')
+                        ][1]/@data-id
+                    """),
+                    str,
+                ),
+                # xpath query needs to have a lower priority
+                validate.all(
+                    validate.xml_xpath_string(
+                        ".//body[@data-content-id][1]/@data-content-id",
                     ),
+                    str,
                 ),
             ),
         )
 
+        return schema.validate(root)
+
     def _api_query_new(self, content_id, api_url):
         url = urljoin(self.url, api_url.format(id=content_id))
         data = self.session.http.get(
@@ -131,7 +138,7 @@ def _api_query_old(self, content_id):
 
         return urljoin(service_url or default_service_url, secure_path)
 
-    def _get_hls_url(self, content_id):
+    def _query_hls_url(self, content_id):
         for idx, match in enumerate(self.matches[:len(self.API_URLS)]):
             if match:
                 return self._api_query_new(content_id, self.API_URLS[idx])
@@ -139,18 +146,21 @@ def _get_hls_url(self, content_id):
         return self._api_query_old(content_id)
 
     def _get_streams(self):
-        try:
-            content_id = self._get_content_id()
-        except PluginError:
-            log.error("Could not find the content ID for this stream")
-            return
+        root = self.session.http.get(self.url, schema=validate.Schema(validate.parse_html()))
 
-        log.debug(f"Loading content: {content_id}")
-        hls_url = self._get_hls_url(content_id)
+        hls_url = self._get_hls_url(root)
         if not hls_url:
-            return
+            try:
+                content_id = self._get_content_id(root)
+            except PluginError:
+                log.error("Could not find the content ID for this stream")
+                return
+
+            log.debug(f"Loading content: {content_id}")
+            hls_url = self._query_hls_url(content_id)
 
-        return HLSStream.parse_variant_playlist(self.session, hls_url)
+        if hls_url:
+            return HLSStream.parse_variant_playlist(self.session, hls_url)
 
 
 __plugin__ = Dogan


Test patch: