Repository: streamlink/streamlink

Issue:
plugins.aloula: 403 Client Error: Forbidden for url
### Checklist

- [X] This is a [plugin issue](https://streamlink.github.io/plugins.html) and not [a different kind of issue](https://github.com/streamlink/streamlink/issues/new/choose)
- [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

streamlink 6.5.1

### Description

Can't generate stream URL for aloula plugin with forbidden code (403).
Streamlink can play the stream well on latest versions (using external vlc player). but generating --stream-url fails.

Desktop:~$ streamlink https://www.aloula.sa/en/live/sunnatvsa  --stream-url
error: Unable to open URL: https://live.kwikmotion.com/ksasunnalive/ksasunna.smil/playlist_dvr.m3u8?hdnts=exp=1705848372~acl=/ksasunnalive/ksasunna.smil/*~hmac=5ea60ce1704942dc93782e233063d9142c19f4980ea6a4bb856331a08bc73c6c (403 Client Error: Forbidden for url: https://live.kwikmotion.com/ksasunnalive/ksasunna.smil/playlist_dvr.m3u8?hdnts=exp=1705848372~acl=/ksasunnalive/ksasunna.smil/*~hmac=5ea60ce1704942dc93782e233063d9142c19f4980ea6a4bb856331a08bc73c6c)


### Debug log

```text
andalus@Desktop:~$ streamlink https://www.aloula.sa/en/live/sunnatvsa  --loglevel=debug
[cli][debug] OS:         Linux-5.15.90.1-microsoft-standard-WSL2-x86_64-with-glibc2.35
[cli][debug] Python:     3.10.12
[cli][debug] OpenSSL:    OpenSSL 3.0.2 15 Mar 2022
[cli][debug] Streamlink: 6.5.1
[cli][debug] Dependencies:
[cli][debug]  certifi: 2023.7.22
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.3
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.19.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.22.2
[cli][debug]  trio-websocket: 0.10.4
[cli][debug]  typing-extensions: 4.8.0
[cli][debug]  urllib3: 2.0.4
[cli][debug]  websocket-client: 1.6.3
[cli][debug] Arguments:
[cli][debug]  url=https://www.aloula.sa/en/live/sunnatvsa
[cli][debug]  --loglevel=debug
[cli][info] Found matching plugin aloula for URL https://www.aloula.sa/en/live/sunnatvsa
[utils.l10n][debug] Language code: en_US
Available streams: 360p (worst), 480p, 1080p (best)
```


Discussion:
bastimeyer: Btw, @andaluspc, if you're just getting the HLS playlist URL using `--stream-url`, then you have to set the `Origin` and `Referer` headers manually.

Patch:
diff --git a/src/streamlink/plugins/aloula.py b/src/streamlink/plugins/aloula.py
index 6d550d2f..2df74d9d 100644
--- a/src/streamlink/plugins/aloula.py
+++ b/src/streamlink/plugins/aloula.py
@@ -19,14 +19,14 @@ from streamlink.stream.hls import HLSStream
 log = logging.getLogger(__name__)
 
 
-@pluginmatcher(re.compile(r"""
-    https?://(?:www\.)?aloula\.sa/(?:\w{2}/)?
-    (?:
-        live/(?P<live_slug>[^/?&]+)
-    |
-        episode/(?P<vod_id>\d+)
-    )
-""", re.VERBOSE))
+@pluginmatcher(
+    name="live",
+    pattern=re.compile(r"https?://(?:www\.)?aloula\.sa/(?:\w{2}/)?live/(?P<live_slug>[^/?&]+)"),
+)
+@pluginmatcher(
+    name="vod",
+    pattern=re.compile(r"https?://(?:www\.)?aloula\.sa/(?:\w{2}/)?episode/(?P<vod_id>\d+)"),
+)
 class Aloula(Plugin):
     def get_live(self, live_slug):
         live_data = self.session.http.get(
@@ -106,13 +106,15 @@ class Aloula(Plugin):
         return HLSStream.parse_variant_playlist(self.session, hls_url)
 
     def _get_streams(self):
-        live_slug = self.match.group("live_slug")
-        vod_id = self.match.group("vod_id")
+        self.session.http.headers.update({
+            "Origin": "https://www.aloula.sa",
+            "Referer": "https://www.aloula.sa/",
+        })
 
-        if live_slug:
-            return self.get_live(live_slug)
-        elif vod_id:
-            return self.get_vod(vod_id)
+        if self.matches["live"]:
+            return self.get_live(self.match["live_slug"])
+        else:
+            return self.get_vod(self.match["vod_id"])
 
 
 __plugin__ = Aloula


Test patch:
diff --git a/tests/plugins/test_aloula.py b/tests/plugins/test_aloula.py
index 1e52bbe2..1c96988d 100644
--- a/tests/plugins/test_aloula.py
+++ b/tests/plugins/test_aloula.py
@@ -6,15 +6,10 @@ class TestPluginCanHandleUrlAloula(PluginCanHandleUrl):
     __plugin__ = Aloula
 
     should_match_groups = [
-        ("https://www.aloula.sa/live/slug", {"live_slug": "slug"}),
-        ("https://www.aloula.sa/en/live/slug", {"live_slug": "slug"}),
-        ("https://www.aloula.sa/de/live/slug/abc", {"live_slug": "slug"}),
-        ("https://www.aloula.sa/episode/123", {"vod_id": "123"}),
-        ("https://www.aloula.sa/en/episode/123", {"vod_id": "123"}),
-        ("https://www.aloula.sa/episode/123abc/456", {"vod_id": "123"}),
-        ("https://www.aloula.sa/de/episode/123abc/456", {"vod_id": "123"}),
-        ("https://www.aloula.sa/episode/123?continue=8", {"vod_id": "123"}),
-        ("https://www.aloula.sa/xx/episode/123?continue=8", {"vod_id": "123"}),
+        (("live", "https://www.aloula.sa/live/saudiatv"), {"live_slug": "saudiatv"}),
+        (("live", "https://www.aloula.sa/en/live/saudiatv"), {"live_slug": "saudiatv"}),
+        (("vod", "https://www.aloula.sa/episode/6676"), {"vod_id": "6676"}),
+        (("vod", "https://www.aloula.sa/en/episode/6676"), {"vod_id": "6676"}),
     ]
 
     should_not_match = [