1
mirror of https://github.com/streamlink/streamlink synced 2024-11-14 01:04:54 +01:00

Merge pull request #1356 from back-to/mixer

[mixer] replaced beam.pro with mixer.com
This commit is contained in:
Forrest 2017-12-04 09:18:44 -08:00 committed by GitHub
commit e12fae5539
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 110 deletions

View File

@ -30,7 +30,6 @@ artetv arte.tv Yes Yes
atresplayer atresplayer.com Yes No Streams are geo-restricted to Spain.
bambuser bambuser.com Yes Yes
bbciplayer bbc.co.uk/iplayer Yes Yes Streams may be geo-restricted to the United Kingdom.
beam beam.pro Yes Yes
beattv be-at.tv Yes Yes Playlist not implemented yet.
bfmtv bfmtv.com Yes Yes
01net.com
@ -131,6 +130,7 @@ media_ccc_de - media.ccc.de Yes Yes Only mp4 and HLS are suppor
mediaklikk mediaklikk.hu Yes No Streams may be geo-restricted to Hungary.
mips mips.tv Yes -- Requires rtmpdump with K-S-V patches.
mitele mitele.es Yes No Streams may be geo-restricted to Spain.
mixer mixer.com Yes Yes
mlgtv mlg.tv Yes --
nbc nbc.com No Yes Streams are geo-restricted to USA. Authentication is not supported.
nbcsports nbcsports.com No Yes Streams maybe be geo-restricted to USA. Authentication is not supported.

View File

@ -1,109 +0,0 @@
import re
from streamlink.compat import urlparse, parse_qsl, urljoin
from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import HLSStream
from streamlink.stream import HTTPStream
from streamlink.stream import RTMPStream
_url_re = re.compile(r"http(s)?://(\w+.)?beam.pro/(?P<channel>[^/?]+)")
class Beam(Plugin):
api_url = "https://beam.pro/api/v1/{type}/{id}"
channel_manifest = "https://beam.pro/api/v1/channels/{id}/manifest.{type}"
_vod_schema = validate.Schema({
"state": "AVAILABLE",
"vods": [{
"baseUrl": validate.url(),
"data": validate.any(None, {
"Height": int
}),
"format": validate.text
}]
},
validate.get("vods"),
validate.filter(lambda x: x["format"] in ("raw", "hls")),
[validate.union({
"url": validate.get("baseUrl"),
"format": validate.get("format"),
"height": validate.all(validate.get("data"), validate.get("Height"))
})])
_assets_schema = validate.Schema(
validate.union({
"base": validate.all(
validate.xml_find("./head/meta"),
validate.get("base"),
validate.url(scheme="rtmp")
),
"videos": validate.all(
validate.xml_findall(".//video"),
[
validate.union({
"src": validate.all(
validate.get("src"),
validate.text
),
"height": validate.all(
validate.get("height"),
validate.text,
validate.transform(int)
)
})
]
)
})
)
@classmethod
def can_handle_url(cls, url):
return _url_re.match(url)
def _get_vod_stream(self, vod_id):
res = http.get(self.api_url.format(type="recordings", id=vod_id))
for sdata in http.json(res, schema=self._vod_schema):
if sdata["format"] == "hls":
hls_url = urljoin(sdata["url"], "manifest.m3u8")
yield "{0}p".format(sdata["height"]), HLSStream(self.session, hls_url)
elif sdata["format"] == "raw":
raw_url = urljoin(sdata["url"], "source.mp4")
yield "{0}p".format(sdata["height"]), HTTPStream(self.session, raw_url)
def _get_live_stream(self, channel):
res = http.get(self.api_url.format(type="channels", id=channel))
channel_info = http.json(res)
if not channel_info["online"]:
return
res = http.get(self.channel_manifest.format(id=channel_info["id"], type="smil"))
assets = http.xml(res, schema=self._assets_schema)
for video in assets["videos"]:
name = "{0}p".format(video["height"])
stream = RTMPStream(self.session, {
"rtmp": "{0}/{1}".format(assets["base"], video["src"])
})
yield name, stream
for s in HLSStream.parse_variant_playlist(self.session,
self.channel_manifest.format(id=channel_info["id"], type="m3u8")).items():
yield s
def _get_streams(self):
params = dict(parse_qsl(urlparse(self.url).query))
vod_id = params.get("vod")
match = _url_re.match(self.url)
channel = match.group("channel")
if vod_id:
self.logger.debug("Looking for VOD {0} from channel: {1}", vod_id, channel)
return self._get_vod_stream(vod_id)
else:
self.logger.debug("Looking for channel: {0}", channel)
return self._get_live_stream(channel)
__plugin__ = Beam

View File

@ -0,0 +1,87 @@
"""
Mixer API 1.0 documentation
https://dev.mixer.com/rest.html
"""
import re
from streamlink import NoStreamsError
from streamlink.compat import urlparse, parse_qsl, urljoin
from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import HLSStream
_url_re = re.compile(r"http(s)?://(\w+.)?mixer\.com/(?P<channel>[^/?]+)")
class Mixer(Plugin):
api_url = "https://mixer.com/api/v1/{type}/{id}"
_vod_schema = validate.Schema(
{
"state": "AVAILABLE",
"vods": [{
"baseUrl": validate.url(),
"data": validate.any(None, {
"Height": int
}),
"format": validate.text
}]
},
validate.get("vods"),
validate.filter(lambda x: x["format"] in ("raw", "hls")),
[validate.union({
"url": validate.get("baseUrl"),
"format": validate.get("format"),
"height": validate.all(validate.get("data"), validate.get("Height"))
})])
@classmethod
def can_handle_url(cls, url):
return _url_re.match(url)
def _get_api_res(self, api_type, api_id):
try:
res = http.get(self.api_url.format(type=api_type, id=api_id))
return res
except Exception as e:
if "404" in str(e):
self.logger.error("invalid {0} - {1}".format(api_type, api_id))
elif "429" in str(e):
self.logger.error("Too Many Requests, API rate limit exceeded.")
raise NoStreamsError(self.url)
def _get_vod_stream(self, vod_id):
res = self._get_api_res("recordings", vod_id)
for sdata in http.json(res, schema=self._vod_schema):
if sdata["format"] == "hls":
hls_url = urljoin(sdata["url"], "manifest.m3u8")
yield "{0}p".format(sdata["height"]), HLSStream(self.session, hls_url)
def _get_live_stream(self, channel):
res = self._get_api_res("channels", channel)
channel_info = http.json(res)
if not channel_info["online"]:
return
user_id = channel_info["id"]
hls_url = self.api_url.format(type="channels", id="{0}/manifest.m3u8".format(user_id))
for s in HLSStream.parse_variant_playlist(self.session, hls_url).items():
yield s
def _get_streams(self):
params = dict(parse_qsl(urlparse(self.url).query))
vod_id = params.get("vod")
match = _url_re.match(self.url)
channel = match.group("channel")
if vod_id:
self.logger.debug("Looking for VOD {0} from channel: {1}", vod_id, channel)
return self._get_vod_stream(vod_id)
else:
self.logger.debug("Looking for channel: {0}", channel)
return self._get_live_stream(channel)
__plugin__ = Mixer

View File

@ -0,0 +1,15 @@
import unittest
from streamlink.plugins.mixer import Mixer
class TestPluginMixer(unittest.TestCase):
def test_can_handle_url(self):
# should match
self.assertTrue(Mixer.can_handle_url('https://mixer.com/Minecraft?vod=11250002'))
self.assertTrue(Mixer.can_handle_url('https://mixer.com/Minecraft'))
self.assertTrue(Mixer.can_handle_url('https://mixer.com/Monstercat?vod=11030270'))
self.assertTrue(Mixer.can_handle_url('https://mixer.com/Monstercat'))
# shouldn't match
self.assertFalse(Mixer.can_handle_url('https://mixer.com'))