Repository: streamlink/streamlink

Issue:
Add support for looch.tv 
- [x] This is a feature request.
- [x] This is a plugin (improvement) request.
- [x] I have read the contribution guidelines.

### Description
Any chance of getting a plugin for this?

https://looch.tv/channels



Discussion:
Do you have any additional details? An example channel, what requests look like, etc.? Anything would be appreciated.
Example:
https://looch.tv/channel/KofyeinTV

Resolving patch:
diff --git a/src/streamlink/plugins/looch.py b/src/streamlink/plugins/looch.py
new file mode 100644
--- /dev/null
+++ b/src/streamlink/plugins/looch.py
@@ -0,0 +1,75 @@
+import re
+
+import itertools
+
+from streamlink.plugin import Plugin
+from streamlink.plugin.api import http
+from streamlink.plugin.api import validate
+from streamlink.stream import HLSStream
+from streamlink.stream import HTTPStream
+from streamlink.utils import parse_json
+
+
+class Looch(Plugin):
+    url_re = re.compile(r"https?://(?:www\.)?looch\.tv/channel/(?P<name>[^/]+)(/videos/(?P<video_id>\d+))?")
+
+    api_base = "https://api.looch.tv"
+    channel_api = api_base + "/channels/{name}"
+    video_api = api_base + "/videos/{id}"
+
+    playback_schema = validate.Schema({"weight": int, "uri": validate.url()})
+    data_schema = validate.Schema({
+        "type": validate.text,
+        "attributes": {
+            validate.optional("playback"): [playback_schema],
+            validate.optional("resolution"): {"width": int, "height": int}
+        }})
+    channel_schema = validate.Schema(
+        validate.transform(parse_json),
+        {"included": validate.all(
+            [data_schema],
+            validate.filter(lambda x: x["type"] == "active_streams"),
+            validate.map(lambda x: x["attributes"].get("playback")),
+            validate.transform(lambda x: list(itertools.chain(*x)))
+        ),
+        }, validate.get("included"))
+    video_schema = validate.Schema(
+        validate.transform(parse_json),
+        {"data": data_schema},
+        validate.get("data"),
+        validate.get("attributes"))
+
+    @classmethod
+    def can_handle_url(cls, url):
+        return cls.url_re.match(url) is not None
+
+    def _get_live_stream(self, channel):
+        url = self.channel_api.format(name=channel)
+        self.logger.debug("Channel API call: {0}", url)
+        data = http.get(url, schema=self.channel_schema)
+        self.logger.debug("Got {0} channel playback items", len(data))
+        for playback in data:
+            for s in HLSStream.parse_variant_playlist(self.session, playback["uri"]).items():
+                yield s
+
+    def _get_video_stream(self, video_id):
+        url = self.video_api.format(id=video_id)
+        self.logger.debug("Video API call: {0}", url)
+        data = http.get(url, schema=self.video_schema)
+        self.logger.debug("Got video {0} playback items", len(data["playback"]))
+        res = data["resolution"]["height"]
+        for playback in data["playback"]:
+            yield "{0}p".format(res), HTTPStream(self.session, playback["uri"])
+
+
+    def _get_streams(self):
+        match = self.url_re.match(self.url)
+        self.logger.debug("Matched URL: name={name}, video_id={video_id}", **match.groupdict())
+
+        if match.group("video_id"):
+            return self._get_video_stream(match.group("video_id"))
+        elif match.group("name"):
+            return self._get_live_stream(match.group("name"))
+
+
+__plugin__ = Looch


Test patch: