codecov[bot]: # [Codecov](https://codecov.io/gh/streamlink/streamlink/pull/3019?src=pr&el=h1) Report
> Merging [#3019](https://codecov.io/gh/streamlink/streamlink/pull/3019?src=pr&el=desc) into [master](https://codecov.io/gh/streamlink/streamlink/commit/b024d6f521706badb1154d9fa9850bccc2996e57&el=desc) will **increase** coverage by `0.01%`.
> The diff coverage is `38.46%`.


```diff
@@            Coverage Diff             @@
##           master    #3019      +/-   ##
==========================================
+ Coverage   52.62%   52.64%   +0.01%     
==========================================
  Files         251      251              
  Lines       15767    15755      -12     
==========================================
- Hits         8298     8294       -4     
+ Misses       7469     7461       -8     
```

diff --git a/docs/plugin_matrix.rst b/docs/plugin_matrix.rst
index 0d003504..6b15eb5b 100644
--- a/docs/plugin_matrix.rst
+++ b/docs/plugin_matrix.rst
@@ -38,7 +38,7 @@ bilibili                live.bilibili.com    Yes   ?
 bloomberg               bloomberg.com        Yes   Yes
 brightcove              players.brig... [6]_ Yes   Yes
 btsports                sport.bt.com         Yes   Yes   Requires subscription account
-btv                     btv.bg               Yes   No    Requires login, and geo-restricted to Bulgaria.
+btv                     btvplus.bg           Yes   No    Streams are geo-restricted to Bulgaria.
 canalplus               mycanal.fr           No    Yes   Streams may be geo-restricted to France.
 cdnbg                   - tv.bnt.bg          Yes   No    Streams may be geo-restricted to Bulgaria.
                         - bgonair.bg
diff --git a/src/streamlink/plugins/btv.py b/src/streamlink/plugins/btv.py
index a026c641..680711cc 100644
--- a/src/streamlink/plugins/btv.py
+++ b/src/streamlink/plugins/btv.py
@@ -1,38 +1,30 @@
-from __future__ import print_function
+import argparse
+import logging
 import re
 
-from streamlink import PluginError
-from streamlink.plugin import Plugin
+from streamlink.plugin import Plugin, PluginArguments, PluginArgument
 from streamlink.plugin.api import validate
 from streamlink.stream import HLSStream
 from streamlink.utils import parse_json
-from streamlink.plugin import PluginArgument, PluginArguments
+
+log = logging.getLogger(__name__)
 
 
 class BTV(Plugin):
     arguments = PluginArguments(
         PluginArgument(
             "username",
-            metavar="USERNAME",
-            requires=["password"],
-            help="""
-        A BTV username required to access any stream.
-        """
+            help=argparse.SUPPRESS
         ),
         PluginArgument(
             "password",
             sensitive=True,
-            metavar="PASSWORD",
-            help="""
-        A BTV account password to use with --btv-username.
-        """
+            help=argparse.SUPPRESS
         )
     )
-    url_re = re.compile(r"https?://(?:www\.)?btv\.bg/live/?")
 
-    api_url = "http://www.btv.bg/lbin/global/player_config.php"
-    check_login_url = "http://www.btv.bg/lbin/userRegistration/check_user_login.php"
-    login_url = "https://www.btv.bg/bin/registration2/login.php?action=login&settings=0"
+    url_re = re.compile(r"https?://(?:www\.)?btvplus\.bg/live/?")
+    api_url = "https://btvplus.bg/lbin/v3/btvplus/player_config.php"
 
     media_id_re = re.compile(r"media_id=(\d+)")
     src_re = re.compile(r"src: \"(http.*?)\"")
@@ -55,35 +47,19 @@ class BTV(Plugin):
     def can_handle_url(cls, url):
         return cls.url_re.match(url) is not None
 
-    def login(self, username, password):
-        res = self.session.http.post(self.login_url, data={"username": username, "password": password})
-        if "success_logged_in" in res.text:
-            return True
-        else:
-            return False
-
     def get_hls_url(self, media_id):
         res = self.session.http.get(self.api_url, params=dict(media_id=media_id))
-        try:
-            return parse_json(res.text, schema=self.api_schema)
-        except PluginError:
-            return
+        return parse_json(res.text, schema=self.api_schema)
 
     def _get_streams(self):
-        if not self.options.get("username") or not self.options.get("password"):
-            self.logger.error("BTV requires registration, set the username and password"
-                              " with --btv-username and --btv-password")
-        elif self.login(self.options.get("username"), self.options.get("password")):
-            res = self.session.http.get(self.url)
-            media_match = self.media_id_re.search(res.text)
-            media_id = media_match and media_match.group(1)
-            if media_id:
-                self.logger.debug("Found media id: {0}", media_id)
-                stream_url = self.get_hls_url(media_id)
-                if stream_url:
-                    return HLSStream.parse_variant_playlist(self.session, stream_url)
-        else:
-            self.logger.error("Login failed, a valid username and password is required")
+        res = self.session.http.get(self.url)
+        media_match = self.media_id_re.search(res.text)
+        media_id = media_match and media_match.group(1)
+        if media_id:
+            log.debug("Found media id: {0}", media_id)
+            stream_url = self.get_hls_url(media_id)
+            if stream_url:
+                return HLSStream.parse_variant_playlist(self.session, stream_url)
 
 
 __plugin__ = BTV

[bug] BTV plugin needs updating
## Bug Report
- [x] This is a bug report and I have read the contribution guidelines.


### Description
The location of the BTV livestream has moved to https://btvplus.bg/live/
**Edit**: Livestreaming no longer requires a user to login, so that can be removed from the plugin info page.


### Expected / Actual behavior
Streamlink should be able to handle the link.


### Reproduction steps / Explicit stream URLs to test
1. streamlink https://btvplus.bg/live/ best 
2. error: No plugin can handle URL: https://btvplus.bg/live/
diff --git a/tests/plugins/test_btv.py b/tests/plugins/test_btv.py
index b04e1933..32304e92 100644
--- a/tests/plugins/test_btv.py
+++ b/tests/plugins/test_btv.py
@@ -6,9 +6,9 @@ from streamlink.plugins.btv import BTV
 class TestPluginBTV(unittest.TestCase):
     def test_can_handle_url(self):
         # should match
-        self.assertTrue(BTV.can_handle_url("http://btv.bg/live"))
-        self.assertTrue(BTV.can_handle_url("http://btv.bg/live/"))
-        self.assertTrue(BTV.can_handle_url("http://www.btv.bg/live/"))
+        self.assertTrue(BTV.can_handle_url("http://btvplus.bg/live"))
+        self.assertTrue(BTV.can_handle_url("http://btvplus.bg/live/"))
+        self.assertTrue(BTV.can_handle_url("http://www.btvplus.bg/live/"))
 
         # shouldn't match
         self.assertFalse(BTV.can_handle_url("http://www.tvcatchup.com/"))

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.9
dev-requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi==2025.1.31
charset-normalizer==3.4.1
coverage==7.8.0
exceptiongroup==1.2.2
flake8==7.2.0
freezegun==1.5.1
idna==3.10
iniconfig==2.1.0
iso-639==0.4.5
iso3166==2.1.1
isodate==0.7.2
mccabe==0.7.0
mock==5.2.0
packaging==24.2
pluggy==1.5.0
pycodestyle==2.13.0
pycryptodome==3.22.0
pyflakes==3.3.1
PySocks==1.7.1
pytest==8.3.5
pytest-cov==6.0.0
python-dateutil==2.9.0.post0
requests==2.32.3
requests-mock==1.12.1
six==1.17.0
-e git+https://github.com/streamlink/streamlink.git@5059d64be20b3cd6adc6374e921382ef2c261978#egg=streamlink
tomli==2.2.1
urllib3==2.3.0
websocket-client==1.8.0

name: streamlink
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - flake8==7.2.0
      - freezegun==1.5.1
      - idna==3.10
      - iniconfig==2.1.0
      - iso-639==0.4.5
      - iso3166==2.1.1
      - isodate==0.7.2
      - mccabe==0.7.0
      - mock==5.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pycryptodome==3.22.0
      - pyflakes==3.3.1
      - pysocks==1.7.1
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - python-dateutil==2.9.0.post0
      - requests==2.32.3
      - requests-mock==1.12.1
      - six==1.17.0
      - tomli==2.2.1
      - urllib3==2.3.0
      - websocket-client==1.8.0
prefix: /opt/conda/envs/streamlink

tests/plugins/test_btv.py::TestPluginBTV::test_can_handle_url
docs/plugin_matrix.rst src/streamlink/plugins/btv.py
docs/plugin_matrix.rst src/streamlink/plugins/btv.py