Repository: streamlink/streamlink

Issue:
plugins.bfmtv: No playable streams found
 Hello. for few days, the plugin isn't working anymore


/usr/local/bin/streamlink --loglevel debug  https://rmcdecouverte.bfmtv.com/mediaplayer-direct/ best
[cli][info] streamlink is running as root! Be careful!
[cli][debug] OS:         Linux-5.8.0-44-generic-x86_64-with-glibc2.29
[cli][debug] Python:     3.8.5
[cli][debug] Streamlink: 2.1.1
[cli][debug] Requests(2.22.0), Socks(1.7.1), Websocket(0.58.0)
[cli][debug] Arguments:
[cli][debug]  url=https://rmcdecouverte.bfmtv.com/mediaplayer-direct/
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][info] Found matching plugin bfmtv for URL https://rmcdecouverte.bfmtv.com/mediaplayer-direct/
error: No playable streams found on this URL: https://rmcdecouverte.bfmtv.com/mediaplayer-direct/


Discussion:

Resolving patch:
diff --git a/src/streamlink/plugins/bfmtv.py b/src/streamlink/plugins/bfmtv.py
--- a/src/streamlink/plugins/bfmtv.py
+++ b/src/streamlink/plugins/bfmtv.py
@@ -1,8 +1,11 @@
 import logging
 import re
+from urllib.parse import urljoin, urlparse
 
 from streamlink.plugin import Plugin
+from streamlink.plugin.api.utils import itertags
 from streamlink.plugins.brightcove import BrightcovePlayer
+from streamlink.stream import HTTPStream
 
 log = logging.getLogger(__name__)
 
@@ -22,29 +25,68 @@ class BFMTV(Plugin):
         r'<iframe.*?src=".*?/(?P<video_id>\w+)"',
         re.DOTALL
     )
+    _main_js_url_re = re.compile(r'src="([\w/]+/main\.\w+\.js)"')
+    _js_brightcove_video_re = re.compile(
+        r'i\?\([A-Z]="[^"]+",y="(?P<video_id>[0-9]+).*"data-account"\s*:\s*"(?P<account_id>[0-9]+)',
+    )
 
     @classmethod
     def can_handle_url(cls, url):
         return cls._url_re.match(url) is not None
 
     def _get_streams(self):
-        # Retrieve URL page and search for Brightcove video data
         res = self.session.http.get(self.url)
-        match = self._brightcove_video_re.search(res.text) or self._brightcove_video_alt_re.search(res.text)
-        if match is not None:
-            account_id = match.group('account_id')
+
+        m = self._brightcove_video_re.search(res.text) or self._brightcove_video_alt_re.search(res.text)
+        if m:
+            account_id = m.group('account_id')
             log.debug(f'Account ID: {account_id}')
-            video_id = match.group('video_id')
+            video_id = m.group('video_id')
             log.debug(f'Video ID: {video_id}')
             player = BrightcovePlayer(self.session, account_id)
             yield from player.get_streams(video_id)
-        else:
-            # Try to find the Dailymotion video ID
-            match = self._embed_video_id_re.search(res.text)
-            if match is not None:
-                video_id = match.group('video_id')
+            return
+
+        # Try to find the Dailymotion video ID
+        m = self._embed_video_id_re.search(res.text)
+        if m:
+            video_id = m.group('video_id')
+            log.debug(f'Video ID: {video_id}')
+            yield from self.session.streams(self._dailymotion_url.format(video_id)).items()
+            return
+
+        # Try the JS for Brightcove video data
+        m = self._main_js_url_re.search(res.text)
+        if m:
+            log.debug(f'JS URL: {urljoin(self.url, m.group(1))}')
+            res = self.session.http.get(urljoin(self.url, m.group(1)))
+            m = self._js_brightcove_video_re.search(res.text)
+            if m:
+                account_id = m.group('account_id')
+                log.debug(f'Account ID: {account_id}')
+                video_id = m.group('video_id')
                 log.debug(f'Video ID: {video_id}')
-                yield from self.session.streams(self._dailymotion_url.format(video_id)).items()
+                player = BrightcovePlayer(self.session, account_id)
+                yield from player.get_streams(video_id)
+                return
+
+        # Audio Live
+        audio_url = None
+        for source in itertags(res.text, 'source'):
+            url = source.attributes.get('src')
+            if url:
+                p_url = urlparse(url)
+                if p_url.path.endswith(('.mp3')):
+                    audio_url = url
+
+        # Audio VOD
+        for div in itertags(res.text, 'div'):
+            if div.attributes.get('class') == 'audio-player':
+                audio_url = div.attributes.get('data-media-url')
+
+        if audio_url:
+            yield 'audio', HTTPStream(self.session, audio_url)
+            return
 
 
 __plugin__ = BFMTV


Test patch: