Skip to content

Commit 40d9643

Browse files
committed
Added furstream and ZDFmediathek plugins
1 parent 3840c57 commit 40d9643

File tree

4 files changed

+119
-0
lines changed

4 files changed

+119
-0
lines changed

AUTHORS

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ N: John Peterson
5151
G: john-peterson
5252
D: Added a FilmOn Social TV plugin and various tweaks and fixes.
5353

54+
N: Pascal Romahn
55+
G: skulblakka
56+
D: Furstream and ZDFmediathek plugins
57+
5458
N: Christopher Rosell
5559
G: chrippa
5660
D: Primary author and maintainer of Livestreamer and most of its plugins.

docs/plugin_matrix.rst

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Livestreamer's primary focus is live streams, so VOD support is limited.
3535
+--------------------+--------+------+----------------------------------------+
3636
| Freedocast | Yes | No | |
3737
+--------------------+--------+------+----------------------------------------+
38+
| Furstream | Yes | No | |
39+
+--------------------+--------+------+----------------------------------------+
3840
| GOMeXP | Yes | -- | |
3941
+--------------------+--------+------+----------------------------------------+
4042
| Hashd.tv | Yes | Yes | |
@@ -68,4 +70,6 @@ Livestreamer's primary focus is live streams, so VOD support is limited.
6870
+--------------------+--------+------+----------------------------------------+
6971
| YouTube | Yes | Yes | |
7072
+--------------------+--------+------+----------------------------------------+
73+
| ZDFmediathek | Yes | Yes | |
74+
+--------------------+--------+------+----------------------------------------+
7175

src/livestreamer/plugins/furstream.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import re
2+
3+
from livestreamer.compat import urlparse
4+
from livestreamer.exceptions import PluginError, NoStreamsError
5+
from livestreamer.plugin import Plugin
6+
from livestreamer.plugin.api import http
7+
from livestreamer.stream import RTMPStream
8+
9+
10+
class Furstream(Plugin):
11+
@classmethod
12+
def can_handle_url(cls, url):
13+
return re.match("^http(s)?://(\w+\.)?furstre\.am/stream.+", url)
14+
15+
def _get_streams(self):
16+
if not RTMPStream.is_usable(self.session):
17+
raise PluginError("rtmpdump is not usable and required by Furstream plugin")
18+
19+
self.logger.debug("Fetching stream info")
20+
res = http.get(self.url)
21+
22+
match = re.search("rtmp://(?:(?!\").)*", res.text)
23+
if match:
24+
self.logger.debug("Stream URL: " + match.group(0))
25+
rtmp = match.group(0)
26+
else:
27+
raise NoStreamsError(self.url)
28+
29+
streams = {}
30+
streams["live"] = RTMPStream(self.session, {
31+
"rtmp": rtmp,
32+
"pageUrl": self.url,
33+
"live": True
34+
})
35+
36+
return streams
37+
38+
__plugin__ = Furstream
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import re
2+
3+
from livestreamer.compat import urlparse
4+
from livestreamer.exceptions import PluginError, NoStreamsError
5+
from livestreamer.plugin import Plugin
6+
from livestreamer.plugin.api import http
7+
from livestreamer.stream import RTMPStream, HDSStream
8+
from livestreamer.utils import parse_xml
9+
10+
QUALITY_WEIGHTS = {
11+
"hd": 720,
12+
"veryhigh": 480,
13+
"high": 240,
14+
"med": 176,
15+
"low": 112
16+
}
17+
18+
class zdf_mediathek(Plugin):
19+
@classmethod
20+
def can_handle_url(cls, url):
21+
return "zdf.de/ZDFmediathek" in url
22+
23+
@classmethod
24+
def stream_weight(cls, key):
25+
weight = QUALITY_WEIGHTS.get(key)
26+
if weight:
27+
return weight, "ZDFmediathek"
28+
29+
return Plugin.stream_weight(key)
30+
31+
def _get_streams(self):
32+
if not RTMPStream.is_usable(self.session):
33+
self.logger.warning("rtmpdump is not usable, only HDS streams will be available")
34+
35+
self.logger.debug("Fetching stream info")
36+
37+
match = re.search("/\w*/(live|video)*/\d*", self.url)
38+
if match:
39+
a = match.group(0).split('/')
40+
stream_id = a[len(a)-1]
41+
else:
42+
raise NoStreamsError(self.url)
43+
44+
api_url = "http://www.zdf.de/ZDFmediathek/xmlservice/web/beitragsDetails?ak=web&id={id}".format(id=stream_id)
45+
res = http.get(api_url)
46+
root = parse_xml(res.text.encode("utf8"))
47+
48+
streams = {}
49+
for formitaet in root.iter('formitaet'):
50+
url = formitaet.find('url').text
51+
quality = formitaet.find('quality').text
52+
53+
if formitaet.get('basetype') == "h264_aac_f4f_http_f4m_http":
54+
bb = re.search("http://(?:(?!\?).)*", url)
55+
hds_streams = HDSStream.parse_manifest(self.session, url)
56+
streams.update(hds_streams)
57+
elif formitaet.get('basetype') == 'h264_aac_mp4_rtmp_zdfmeta_http':
58+
streams[quality] = RTMPStream(self.session, {
59+
"rtmp": self._get_stream(url),
60+
"pageUrl": self.url,
61+
})
62+
63+
64+
return streams
65+
66+
67+
def _get_stream(self, meta_url):
68+
res = http.get(meta_url)
69+
root = parse_xml(res.text.encode("utf8"))
70+
stream_url = root.find("default-stream-url").text
71+
return stream_url
72+
73+
__plugin__ = zdf_mediathek

0 commit comments

Comments
 (0)