Repository: streamlink/streamlink

Issue:
plugins.atresplayer: error: Unable to validate response text: ValidationError(NoneOrAllSchema)
### 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

Using the latest app image with Tvheadend with command:

pipe:///usr/local/bin/streamlink -O https://www.atresplayer.com/directos/nova best

2022-10-09 23:21:29.885 mpegts: nova HD in Streams - tuning on IPTV #1
2022-10-09 23:21:29.927 subscription: 0121: "scan" subscribing to mux "nova HD", weight: 6, adapter: "IPTV #1", network: "Streams", service: "Raw PID Subscription"
2022-10-09 23:21:29.927 spawn: Executing "/usr/local/bin/streamlink"
2022-10-09 23:21:30.352 spawn: [cli][info] Found matching plugin atresplayer for URL https://www.atresplayer.com/directos/nova/
2022-10-09 23:21:30.621 spawn: [cli][info] Available streams: 360p (worst), 480p, 720p, 1080p (best)
2022-10-09 23:21:30.621 spawn: [cli][info] Opening stream: 1080p (hls)
2022-10-09 23:21:44.927 mpegts: nova HD in Streams - scan no data, failed
2022-10-09 23:21:44.927 subscription: 0121: "scan" unsubscribing

### Debug log

```text
nico@NUC:~/streamlink$ ./streamlink -l debug https://www.atresplayer.com/directos/nova
[cli][debug] OS:         Linux-5.15.0-48-generic-x86_64-with-glibc2.31
[cli][debug] Python:     3.10.7
[cli][debug] Streamlink: 5.0.1
[cli][debug] Dependencies:
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.1
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.15.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.28.1
[cli][debug]  websocket-client: 1.4.1
[cli][debug] Arguments:
[cli][debug]  url=https://www.atresplayer.com/directos/nova
[cli][debug]  --loglevel=debug
[cli][info] Found matching plugin atresplayer for URL https://www.atresplayer.com/directos/nova
error: Unable to validate response text: ValidationError(NoneOrAllSchema):
  ValidationError(dict):
    Unable to validate value of key 'links'
    Context(dict):
      Key '/directos/nova' not found in <{'/directos/nova/': {'url': '/directos/nova/', 'redirec...>

nuc@NUC:~/streamlink$ ./streamlink --version-check
[cli][info] Your Streamlink version (5.0.1) is up to date!

nuc@NUC:~/streamlink$ ./streamlink --version
streamlink 5.0.1

nuc@NUC:~/streamlink$ ./streamlink --plugins
Loaded plugins: abematv, adultswim, afreeca, albavision, aloula, app17, ard_live, ard_mediathek, artetv, atpchallenger, atresplayer, bbciplayer, bfmtv, bigo, bilibili, blazetv, bloomberg, booyah, brightcove, btv, cbsnews, cdnbg, ceskatelevize, cinergroup, clubbingtv, cmmedia, cnews, crunchyroll, dailymotion, dash, delfi, deutschewelle, dlive, dogan, dogus, drdk, earthcam, egame, euronews, facebook, filmon, foxtr, funimationnow, galatasaraytv, **goltelevision**, goodgame, googledrive, gulli, hiplayer, hls, http, htv, huajiao, huya, idf1, invintus, kugou, linelive, livestream, lnk, lrt, ltv_lsm_lv, mdstrm, mediaklikk, mediavitrina, mildom, mitele, mjunoon, mrtmk, n13tv, nbcnews, nhkworld, nicolive, nimotv, nos, nownews, nrk, ntv, okru, olympicchannel, oneplusone, onetv, openrectv, orf_tvthek, pandalive, picarto, piczel, pixiv, pluto, pluzz, qq, radiko, radionet, raiplay, reuters, rtbf, rtpa, rtpplay, rtve, rtvs, ruv, sbscokr, schoolism, showroom, sportal, sportschau, ssh101, stadium, steam, streamable, streann, stv, svtplay, swisstxt, telefe, tf1, trovo, turkuvaz, tv360, tv3cat, tv4play, tv5monde, tv8, tv999, tvibo, tviplayer, tvp, tvrby, tvrplus, tvtoya, twitcasting, twitch, useetv, ustreamtv, ustvnow, vidio, vimeo, vinhlongtv, vk, vlive, vtvgo, wasd, webtv, welt, wwenetwork, youtube, yupptv, zattoo, zdf_mediathek, zeenews, zengatv, zhanqi
```



Discussion:
> `https://www.atresplayer.com/directos/nova`

The URL passed to streamlink has to end with a `/` character. Otherwise the JSON payload contains an invalid key.

This doesn't mean though that the plugin shouldn't receive a fix for that.

Resolving patch:
diff --git a/src/streamlink/plugins/atresplayer.py b/src/streamlink/plugins/atresplayer.py
--- a/src/streamlink/plugins/atresplayer.py
+++ b/src/streamlink/plugins/atresplayer.py
@@ -22,10 +22,12 @@
     r"https?://(?:www\.)?atresplayer\.com/"
 ))
 class AtresPlayer(Plugin):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.url = update_scheme("https://", f"{self.url.rstrip('/')}/")
+
     def _get_streams(self):
-        self.url = update_scheme("https://", self.url)
         path = urlparse(self.url).path
-
         api_url = self.session.http.get(self.url, schema=validate.Schema(
             re.compile(r"""window.__PRELOADED_STATE__\s*=\s*({.*?});""", re.DOTALL),
             validate.none_or_all(


Test patch:
diff --git a/tests/plugins/test_atresplayer.py b/tests/plugins/test_atresplayer.py
--- a/tests/plugins/test_atresplayer.py
+++ b/tests/plugins/test_atresplayer.py
@@ -1,3 +1,7 @@
+from unittest.mock import Mock
+
+import pytest
+
 from streamlink.plugins.atresplayer import AtresPlayer
 from tests.plugins import PluginCanHandleUrl
 
@@ -6,8 +10,20 @@ class TestPluginCanHandleUrlAtresPlayer(PluginCanHandleUrl):
     __plugin__ = AtresPlayer
 
     should_match = [
-        'http://www.atresplayer.com/directos/antena3/',
-        'http://www.atresplayer.com/directos/lasexta/',
-        'https://www.atresplayer.com/directos/antena3/',
-        'https://www.atresplayer.com/flooxer/programas/unas/temporada-1/dario-eme-hache-sindy-takanashi-entrevista_123/',
+        "http://www.atresplayer.com/directos/antena3/",
+        "http://www.atresplayer.com/directos/lasexta/",
+        "https://www.atresplayer.com/directos/antena3/",
+        "https://www.atresplayer.com/flooxer/programas/unas/temporada-1/dario-eme-hache-sindy-takanashi-entrevista_123/",
     ]
+
+
+class TestAtresPlayer:
+    @pytest.mark.parametrize("url,expected", [
+        ("http://www.atresplayer.com/directos/antena3", "https://www.atresplayer.com/directos/antena3/"),
+        ("http://www.atresplayer.com/directos/antena3/", "https://www.atresplayer.com/directos/antena3/"),
+        ("https://www.atresplayer.com/directos/antena3", "https://www.atresplayer.com/directos/antena3/"),
+        ("https://www.atresplayer.com/directos/antena3/", "https://www.atresplayer.com/directos/antena3/"),
+    ])
+    def test_url(self, url, expected):
+        plugin = AtresPlayer(Mock(), url)
+        assert plugin.url == expected