Repository: streamlink/streamlink

Issue:
Earthcam plugin on streamlink
Happy new year streamlink team! Listen I want everyone to make a plugin for earthcam so I can look at the live camera around NYC and the world thanks.


Discussion:
Can you please provide details regarding what the URL looks like for specific cameras/streaming?

Resolving patch:
diff --git a/src/streamlink/plugins/earthcam.py b/src/streamlink/plugins/earthcam.py
new file mode 100644
--- /dev/null
+++ b/src/streamlink/plugins/earthcam.py
@@ -0,0 +1,100 @@
+from __future__ import print_function
+
+import re
+
+from streamlink.plugin import Plugin
+from streamlink.plugin.api import http
+from streamlink.plugin.api import validate
+from streamlink.stream import HLSStream, RTMPStream
+from streamlink.utils import parse_json
+
+
+class EarthCam(Plugin):
+    url_re = re.compile(r"https?://(?:www.)?earthcam.com/.*")
+    playpath_re = re.compile(r"(?P<folder>/.*/)(?P<file>.*?\.flv)")
+    swf_url = "http://static.earthcam.com/swf/streaming/stream_viewer_v3.swf"
+    json_base_re = re.compile(r"""var[ ]+json_base[^=]+=.*?(\{.*?});""", re.DOTALL)
+    cam_name_re = re.compile(r"""var[ ]+currentName[^=]+=[ \t]+(?P<quote>["'])(?P<name>\w+)(?P=quote);""", re.DOTALL)
+    cam_data_schema = validate.Schema(
+        validate.transform(json_base_re.search),
+        validate.any(
+            None,
+            validate.all(
+                validate.get(1),
+                validate.transform(lambda d: d.replace("\\/", "/")),
+                validate.transform(parse_json),
+            )
+        )
+    )
+
+    @classmethod
+    def can_handle_url(cls, url):
+        return cls.url_re.match(url) is not None
+
+    def _get_streams(self):
+        res = http.get(self.url)
+        m = self.cam_name_re.search(res.text)
+        cam_name = m and m.group("name")
+        json_base = self.cam_data_schema.validate(res.text)
+
+        cam_data = json_base["cam"][cam_name]
+
+        self.logger.debug("Found cam for {0} - {1}", cam_data["group"], cam_data["title"])
+
+        is_live = (cam_data["liveon"] == "true" and cam_data["defaulttab"] == "live")
+
+        # HLS data
+        hls_domain = cam_data["html5_streamingdomain"]
+        hls_playpath = cam_data["html5_streampath"]
+
+        # RTMP data
+        rtmp_playpath = ""
+        if is_live:
+            n = "live"
+            rtmp_domain = cam_data["streamingdomain"]
+            rtmp_path = cam_data["livestreamingpath"]
+            rtmp_live = cam_data["liveon"]
+
+            if rtmp_path:
+                match = self.playpath_re.search(rtmp_path)
+                rtmp_playpath = match.group("file")
+                rtmp_url = rtmp_domain + match.group("folder")
+        else:
+            n = "vod"
+            rtmp_domain = cam_data["archivedomain"]
+            rtmp_path = cam_data["archivepath"]
+            rtmp_live = cam_data["archiveon"]
+
+            if rtmp_path:
+                rtmp_playpath = rtmp_path
+                rtmp_url = rtmp_domain
+
+        # RTMP stream
+        if rtmp_playpath:
+            self.logger.debug("RTMP URL: {0}{1}", rtmp_url, rtmp_playpath)
+
+            params = {
+                "rtmp": rtmp_url,
+                "playpath": rtmp_playpath,
+                "pageUrl": self.url,
+                "swfUrl": self.swf_url,
+                "live": rtmp_live
+            }
+
+            yield n, RTMPStream(self.session, params)
+
+        # HLS stream
+        if hls_playpath and is_live:
+            hls_url = hls_domain + hls_playpath
+
+            self.logger.debug("HLS URL: {0}", hls_url)
+
+            for s in HLSStream.parse_variant_playlist(self.session, hls_url).items():
+                yield s
+
+        if not (rtmp_playpath or hls_playpath):
+            self.logger.error("This cam stream appears to be in offline or "
+                              "snapshot mode and not live stream can be played.")
+            return
+
+__plugin__ = EarthCam


Test patch: