Date: Sunday, February 8, 2015 @ 17:16:00 Author: foutrelis Revision: 127300
upgpkg: livestreamer 1.11.1-2 Add upstream fix for the hitbox plugin. Added: livestreamer/trunk/plugins-hitbox-Refactor-to-use-the-StreamMapper.patch Modified: livestreamer/trunk/PKGBUILD -------------------------------------------------------+ PKGBUILD | 15 - plugins-hitbox-Refactor-to-use-the-StreamMapper.patch | 197 ++++++++++++++++ 2 files changed, 209 insertions(+), 3 deletions(-) Modified: PKGBUILD =================================================================== --- PKGBUILD 2015-02-08 14:20:49 UTC (rev 127299) +++ PKGBUILD 2015-02-08 16:16:00 UTC (rev 127300) @@ -3,7 +3,7 @@ # Contributor: Christopher Rosell <[email protected]> pkgname=livestreamer pkgver=1.11.1 -pkgrel=1 +pkgrel=2 pkgdesc='CLI program that launches streams from various streaming services in a custom video player' arch=('any') url='https://github.com/chrippa/livestreamer' @@ -10,9 +10,18 @@ license=('BSD') depends=('python-requests' 'rtmpdump' 'python-setuptools') makedepends=('python-sphinx') -source=(https://pypi.python.org/packages/source/l/$pkgname/$pkgname-$pkgver.tar.gz) -sha256sums=('84dd83d301518ffcf96f30cbffc0e0598e0756e7ab8e7498d11285d42fe17f9c') +source=(https://pypi.python.org/packages/source/l/$pkgname/$pkgname-$pkgver.tar.gz + plugins-hitbox-Refactor-to-use-the-StreamMapper.patch) +sha256sums=('84dd83d301518ffcf96f30cbffc0e0598e0756e7ab8e7498d11285d42fe17f9c' + '1a305add9db98912f0cdb16862d1296588b2ed299587de26d93c5be53f9fd572') +prepare() { + cd "$srcdir/$pkgname-$pkgver" + + # https://github.com/chrippa/livestreamer/issues/648 + patch -Np1 -i ../plugins-hitbox-Refactor-to-use-the-StreamMapper.patch +} + build() { cd "$srcdir/$pkgname-$pkgver" python setup.py build_sphinx -b man Added: plugins-hitbox-Refactor-to-use-the-StreamMapper.patch =================================================================== --- plugins-hitbox-Refactor-to-use-the-StreamMapper.patch (rev 0) +++ plugins-hitbox-Refactor-to-use-the-StreamMapper.patch 2015-02-08 16:16:00 UTC (rev 127300) @@ -0,0 +1,197 @@ +From 3cd7f604b9f3327f7626b719a3035c47a68ea9a2 Mon Sep 17 00:00:00 2001 +From: Christopher Rosell <[email protected]> +Date: Tue, 16 Dec 2014 23:03:17 +0100 +Subject: [PATCH] plugins.hitbox: Refactor to use the StreamMapper. + +Also add support for HLS streams. + +Resolves #648. +--- + src/livestreamer/plugins/hitbox.py | 139 ++++++++++++++++++++++++------------- + 1 file changed, 92 insertions(+), 47 deletions(-) + +diff --git a/src/livestreamer/plugins/hitbox.py b/src/livestreamer/plugins/hitbox.py +index 306a318..556a3d9 100644 +--- a/src/livestreamer/plugins/hitbox.py ++++ b/src/livestreamer/plugins/hitbox.py +@@ -1,10 +1,13 @@ + import re + ++from itertools import chain ++ + from livestreamer.compat import urlparse + from livestreamer.plugin import Plugin +-from livestreamer.plugin.api import http, validate ++from livestreamer.plugin.api import StreamMapper, http, validate + from livestreamer.stream import HLSStream, HTTPStream, RTMPStream + ++HLS_PLAYLIST_BASE = "http://www.hitbox.tv{0}" + LIVE_API = "http://www.hitbox.tv/api/media/live/{0}?showHidden=true" + PLAYER_API = "http://www.hitbox.tv/api/player/config/{0}/{1}?embed=false&showHidden=true" + SWF_BASE = "http://edge.vie.hitbox.tv/static/player/flowplayer/" +@@ -43,7 +46,7 @@ + validate.filter(lambda b: b.get("url") and b.get("label")), + [{ + "label": validate.text, +- "url": validate.text ++ "url": validate.text, + }], + ) + }, +@@ -52,15 +55,20 @@ + validate.optional("netConnectionUrl"): validate.text, + validate.optional("bitrates"): [{ + "label": validate.text, +- "url": validate.text ++ "url": validate.text, ++ "provider": validate.text + }] + }], +- "plugins": { +- validate.optional("clustering"): { +- "netConnectionUrl": validate.text, +- "url": validate.text ++ "plugins": validate.all( ++ dict, ++ validate.filter(lambda k, v: k in ["rtmp", "rtmpHitbox", "hls"]), ++ { ++ validate.text: { ++ validate.optional("netConnectionUrl"): validate.text, ++ "url": validate.text ++ } + } +- } ++ ) + } + ) + +@@ -77,6 +85,79 @@ def _get_quality(self, label): + + return "live" + ++ def _create_hls_streams(self, bitrate): ++ url = bitrate["url"] ++ quality = self._get_quality(bitrate["label"]) ++ ++ if not url.startswith("http"): ++ url = HLS_PLAYLIST_BASE.format(url) ++ ++ if bitrate["label"] == "Auto": ++ try: ++ streams = HLSStream.parse_variant_playlist(self.session, url) ++ return streams.items() ++ except IOError as err: ++ self.logger.warning("Failed to extract HLS streams: {0}", err) ++ else: ++ return quality, HLSStream(self.session, url) ++ ++ def _create_rtmp_stream(self, rtmp, swf_url, bitrate): ++ quality = self._get_quality(bitrate["label"]) ++ url = bitrate["url"] ++ stream = RTMPStream(self.session, { ++ "rtmp": rtmp, ++ "pageUrl": self.url, ++ "playpath": url, ++ "swfVfy": swf_url, ++ "live": True ++ }) ++ ++ return quality, stream ++ ++ def _get_live_streams(self, player): ++ mappers = [] ++ swf_url = SWF_URL ++ for playlist in player.get("playlist", []): ++ bitrates = playlist.get("bitrates") ++ provider = playlist.get("connectionProvider") ++ rtmp = None ++ ++ if bitrates: ++ rtmp = playlist.get("netConnectionUrl") ++ elif provider and provider in player["plugins"]: ++ provider = player["plugins"][provider] ++ swf_name = provider["url"] ++ swf_url = SWF_BASE + swf_name ++ rtmp = provider["netConnectionUrl"] ++ bitrates = player["clip"]["bitrates"] ++ else: ++ continue ++ ++ mapper = StreamMapper( ++ cmp=lambda provider, bitrate: bitrate["provider"].startswith(provider) ++ ) ++ mapper.map("hls", self._create_hls_streams) ++ mapper.map("rtmp", self._create_rtmp_stream, rtmp, swf_url) ++ mappers.append(mapper(bitrates)) ++ ++ return chain.from_iterable(mappers) ++ ++ def _create_video_stream(self, cls, base_url, bitrate): ++ url = base_url + "/" + bitrate["url"] ++ quality = self._get_quality(bitrate["label"]) ++ return quality, cls(self.session, url) ++ ++ def _get_video_streams(self, player): ++ base_url = player["clip"]["baseUrl"] or VOD_BASE_URL ++ mapper = StreamMapper( ++ cmp=lambda ext, bitrate: urlparse(bitrate["url"]).path.endswith(ext) ++ ) ++ mapper.map(".m3u8", self._create_video_stream, HLSStream, base_url) ++ mapper.map(".mp4", self._create_video_stream, HTTPStream, base_url) ++ mapper.map(".flv", self._create_video_stream, HTTPStream, base_url) ++ ++ return mapper(player["clip"]["bitrates"]) ++ + def _get_streams(self): + match = _url_re.match(self.url) + if not match: +@@ -96,46 +177,10 @@ def _get_streams(self): + + res = http.get(PLAYER_API.format(media_type, media_id)) + player = http.json(res, schema=_player_schema) ++ + if media_type == "live": +- swf_url = SWF_URL +- for playlist in player.get("playlist", []): +- bitrates = playlist.get("bitrates") +- provider = playlist.get("connectionProvider") +- rtmp = None +- +- if bitrates: +- rtmp = playlist.get("netConnectionUrl") +- elif provider and provider in player["plugins"]: +- provider = player["plugins"][provider] +- swf_name = provider["url"] +- swf_url = SWF_BASE + swf_name +- rtmp = provider["netConnectionUrl"] +- bitrates = player["clip"]["bitrates"] +- else: +- continue +- +- for bitrate in bitrates: +- quality = self._get_quality(bitrate["label"]) +- url = bitrate["url"] +- stream = RTMPStream(self.session, { +- "rtmp": rtmp, +- "pageUrl": self.url, +- "playpath": url, +- "swfVfy": swf_url, +- "live": True +- }) +- yield quality, stream ++ return self._get_live_streams(player) + else: +- base_url = player["clip"]["baseUrl"] or VOD_BASE_URL +- for bitrate in player["clip"]["bitrates"]: +- url = base_url + "/" + bitrate["url"] +- quality = self._get_quality(bitrate["label"]) +- +- if urlparse(url).path.endswith("m3u8"): +- stream = HLSStream(self.session, url) +- else: +- stream = HTTPStream(self.session, url) +- +- yield quality, stream ++ return self._get_video_streams(player) + + __plugin__ = Hitbox
