Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposed fixes 1.13 #1495

Closed
wants to merge 46 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
dd4d256
Update redirect URI after successful twitch auth
mindhalt Jun 19, 2016
ad7ec18
Fix Twitch plugin not working because bandwith was parsed as an int w…
e00E Jul 14, 2016
7ccb57d
Fixed weighting of Twitch stream names
mjbcopland Aug 9, 2016
f42f332
Use client ID for twitch.tv API calls
LoneFox78 Sep 16, 2016
6b05994
plugins.livestream: Prefer standard SWF players
intact Apr 9, 2016
498f8d3
plugins.livestream: Tolerate missing swf player URL
intact Sep 2, 2016
8f8073d
plugins.livestream: Fix player URL
intact Sep 6, 2016
9738715
ustream: fix desktop stream validation
pmrowla Feb 16, 2016
98f3cf6
Fix issue #1276 - ustream.tv KeyError: 'url'
Apr 11, 2016
41b9ce3
Update ustreamtv.py
snifer1981 May 3, 2016
fd39225
plugins.afreeca: fix stream
dsokal Jul 5, 2016
25f06f5
channel info url change in afreeca plugin
AleXoundOS Jul 16, 2016
7af3d9c
plugins.streamupcom: API changes
406NotAcceptable Feb 4, 2016
a48e6c9
plugins.tga: Support more streams
intact Apr 14, 2016
bb7db24
plugins.tga: Fix offline streams
intact Apr 14, 2016
1b6ef49
Change url schema to match changes at periscope
michaelnachname Apr 27, 2016
c769577
plugins.nrk: Updated for webpage changes.
sn4kebite Jun 11, 2016
5af17ba
plugins.nrk: Fixed _id_re regex not matching series URLs.
sn4kebite Aug 26, 2016
bccacc7
Add API support
tboss Jul 1, 2016
389cf13
plugins.vaughnlive: Fix INFO_URL
intact Jul 2, 2016
3adaf70
plugins.vaughnlive: Update for API change
intact Jul 23, 2016
f12842a
plugins.vaughnlive: Fix app for some ingest servers
intact Jul 28, 2016
584227d
plugins.vaughnlive: Remove debug print
intact Jul 28, 2016
6a4e74f
plugins.vaughnlive: Lowercase channel name
intact Aug 2, 2016
06d43b9
plugins.vaughnlive: Update for API change
intact Aug 11, 2016
c38c949
plugins.vaughnlive: Update for API change
intact Aug 20, 2016
c1ace87
update douyu
steven7851 Jul 24, 2016
89e749e
fix cdn..
steven7851 Jul 24, 2016
74ac48b
fix for Python 3.x..
steven7851 Jul 24, 2016
1f96241
use mobile api for reducing code
steven7851 Jul 24, 2016
a20169e
fix for non number channel
steven7851 Jul 24, 2016
15e110a
add middle and low quality
steven7851 Jul 24, 2016
1126ede
fix quality
steven7851 Jul 25, 2016
25bb88c
fix room id regex
steven7851 Jul 29, 2016
f5e78e0
make did by UUID module
steven7851 Aug 6, 2016
a0fa292
fix channel on event
steven7851 Aug 8, 2016
dc67e61
more retries for redirection
steven7851 Aug 9, 2016
7f034e3
remove useless lib
steven7851 Aug 9, 2016
c947485
try to support event page
steven7851 Aug 11, 2016
a40e529
use https protocol
steven7851 Sep 3, 2016
fc21dd4
Refactoring and update for the VOD support
int3l Aug 22, 2016
549520f
Fix goodgame find Streame
liz1rgin Aug 30, 2016
9c78b5b
Update goodgame.py
liz1rgin Sep 1, 2016
75a7e50
Picarto plugin: update RTMPStream-settings
Swirt Sep 5, 2016
698db70
Picarto plugin: update RTMPStream-settings
Swirt Sep 5, 2016
8bc1e30
plugins.vaughnlive: Update for API change
intact Sep 27, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/livestreamer/plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def stream_weight(stream):
if stream in weights:
return weights[stream], group

match = re.match("^(\d+)([k]|[p])?([\+])?$", stream)
match = re.match("^(\d+)([k]|[p])?(\d*)([\+])?$", stream)

if match:
if match.group(2) == "k":
Expand All @@ -53,7 +53,10 @@ def stream_weight(stream):
elif match.group(2) == "p":
weight = int(match.group(1))

if match.group(3) == "+":
if match.group(3):
weight += int(match.group(3))

if match.group(4) == "+":
weight += 1

return weight, "pixels"
Expand Down
8 changes: 4 additions & 4 deletions src/livestreamer/plugins/afreeca.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from livestreamer.plugin.api import http, validate
from livestreamer.stream import RTMPStream, HLSStream

CHANNEL_INFO_URL = "http://live.afreeca.com:8057/api/get_broad_state_list.php"
CHANNEL_INFO_URL = "http://live.afreecatv.com:8057/api/get_broad_state_list.php"
KEEP_ALIVE_URL = "{server}/stream_keepalive.html"
STREAM_INFO_URLS = {
"rtmp": "http://sessionmanager01.afreeca.tv:6060/broad_stream_assign.html",
Expand All @@ -16,7 +16,7 @@
CHANNEL_RESULT_OK = 1


_url_re = re.compile("http(s)?://(\w+\.)?afreeca.com/(?P<username>\w+)")
_url_re = re.compile("http(s)?://(\w+\.)?afreeca(tv)?.com/(?P<username>\w+)/\d+")

_channel_schema = validate.Schema(
{
Expand Down Expand Up @@ -49,10 +49,10 @@ def _get_channel_info(self, username):
headers = {
"Referer": self.url
}
data = {
params = {
"uid": username
}
res = http.post(CHANNEL_INFO_URL, data=data, headers=headers)
res = http.get(CHANNEL_INFO_URL, params=params, headers=headers)

return http.json(res, schema=_channel_schema)

Expand Down
130 changes: 105 additions & 25 deletions src/livestreamer/plugins/douyutv.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,82 @@
import hashlib
import re
import time
import uuid

from requests.adapters import HTTPAdapter

from livestreamer.plugin import Plugin
from livestreamer.plugin.api import http, validate
from livestreamer.stream import (
HTTPStream, HLSStream
)
from livestreamer.stream import HTTPStream

API_URL = "http://www.douyutv.com/api/v1/room/{0}?aid=android&client_sys=android&time={1}&auth={2}"
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
MAPI_URL = "https://m.douyu.com/html5/live?roomId={0}"
LAPI_URL = "https://www.douyu.com/lapi/live/getPlay/{0}"
LAPI_SECRET = "A12Svb&%1UUmf@hC"
SHOW_STATUS_ONLINE = 1
SHOW_STATUS_OFFLINE = 2
STREAM_WEIGHTS = {
"middle": 540,
"low": 540,
"middle": 720,
"source": 1080
}

_url_re = re.compile("""
http(s)?://(www\.)?douyutv.com
http(s)?://(www\.)?douyu.com
/(?P<channel>[^/]+)
""", re.VERBOSE)

_room_id_re = re.compile(r'"room_id"\s*:\s*(\d+),')
_room_id_alt_re = re.compile(r'data-room_id="(\d+)"')

_room_id_schema = validate.Schema(
validate.all(
validate.transform(_room_id_re.search),
validate.any(
None,
validate.all(
validate.get(1),
validate.transform(int)
)
)
)
)

_room_id_alt_schema = validate.Schema(
validate.all(
validate.transform(_room_id_alt_re.search),
validate.any(
None,
validate.all(
validate.get(1),
validate.transform(int)
)
)
)
)

_room_schema = validate.Schema(
{
"data": validate.any(None, {
"show_status": validate.all(
validate.text,
validate.transform(int)
),
"rtmp_url": validate.text,
"rtmp_live": validate.text,
"hls_url": validate.text,
"rtmp_multi_bitrate": validate.all(
validate.any([], {
validate.text: validate.text
}),
validate.transform(dict)
)
})
},
validate.get("data")
)

_lapi_schema = validate.Schema(
{
"data": validate.any(None, {
"rtmp_url": validate.text,
"rtmp_live": validate.text
})
},
validate.get("data")
)


class Douyutv(Plugin):
@classmethod
Expand All @@ -59,28 +94,73 @@ def _get_streams(self):
match = _url_re.match(self.url)
channel = match.group("channel")

ts = int(time.time())
sign = hashlib.md5(("room/{0}?aid=android&client_sys=android&time={1}".format(channel, ts) + "1231").encode("ascii")).hexdigest()
http.headers.update({"User-Agent": USER_AGENT})
http.verify=False
http.mount('http://', HTTPAdapter(max_retries=99))

res = http.get(API_URL.format(channel, ts, sign))
#Thanks to @ximellon for providing method.
try:
channel = int(channel)
except ValueError:
channel = http.get(self.url, schema=_room_id_schema)
if channel == 0:
channel = http.get(self.url, schema=_room_id_alt_schema)

res = http.get(MAPI_URL.format(channel))
room = http.json(res, schema=_room_schema)
if not room:
return

if room["show_status"] != SHOW_STATUS_ONLINE:
return

hls_url = "{room[hls_url]}?wsiphost=local".format(room=room)
hls_stream = HLSStream(self.session, hls_url)
yield "hls", hls_stream
ts = int(time.time() / 60)
did = uuid.uuid4().hex.upper()
sign = hashlib.md5(("{0}{1}{2}{3}".format(channel, did, LAPI_SECRET, ts)).encode("utf-8")).hexdigest()

data = {
"cdn": "ws",
"rate": "0",
"tt": ts,
"did": did,
"sign": sign
}

res = http.post(LAPI_URL.format(channel), data=data)
room = http.json(res, schema=_lapi_schema)

url = "{room[rtmp_url]}/{room[rtmp_live]}".format(room=room)
stream = HTTPStream(self.session, url)
yield "source", stream

for name, url in room["rtmp_multi_bitrate"].items():
url = "{room[rtmp_url]}/{url}".format(room=room, url=url)
stream = HTTPStream(self.session, url)
yield name, stream
data = {
"cdn": "ws",
"rate": "2",
"tt": ts,
"did": did,
"sign": sign
}

res = http.post(LAPI_URL.format(channel), data=data)
room = http.json(res, schema=_lapi_schema)

url = "{room[rtmp_url]}/{room[rtmp_live]}".format(room=room)
stream = HTTPStream(self.session, url)
yield "middle", stream

data = {
"cdn": "ws",
"rate": "1",
"tt": ts,
"did": did,
"sign": sign
}

res = http.post(LAPI_URL.format(channel), data=data)
room = http.json(res, schema=_lapi_schema)

url = "{room[rtmp_url]}/{room[rtmp_live]}".format(room=room)
stream = HTTPStream(self.session, url)
yield "low", stream

__plugin__ = Douyutv
4 changes: 2 additions & 2 deletions src/livestreamer/plugins/goodgame.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

_url_re = re.compile("http://(?:www\.)?goodgame.ru/channel/(?P<user>\w+)")
_stream_re = re.compile(
"iframe frameborder=\"0\" width=\"100%\" height=\"100%\" src=\"http://goodgame.ru/player(\d)?\?(\w+)\""
"meta property=\"og:video:iframe\" content=\"http://goodgame.ru/player/html\?(\w+)\""
)
_ddos_re = re.compile(
"document.cookie=\"(__DDOS_[^;]+)"
Expand Down Expand Up @@ -45,7 +45,7 @@ def _get_streams(self):
if not match:
return

stream_id = match.group(2)
stream_id = match.group(1)
streams = {}
for name, url_suffix in QUALITIES.items():
url = HLS_URL_FORMAT.format(stream_id, url_suffix)
Expand Down
29 changes: 14 additions & 15 deletions src/livestreamer/plugins/livecodingtv.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import re

from livestreamer.plugin import Plugin
from livestreamer.stream import RTMPStream
from livestreamer.stream import RTMPStream, HTTPStream
from livestreamer.plugin.api import http


_vod_re = re.compile('\"(http(s)?://.*\.mp4\?t=.*)\"')
_rtmp_re = re.compile('rtmp://[^"]+/(?P<channel>\w+)+[^/"]+')
_url_re = re.compile("http(s)?://(?:\w+.)?\livecoding\.tv")
_url_re = re.compile('http(s)?://(?:\w+.)?\livecoding\.tv')


class LivecodingTV(Plugin):
Expand All @@ -16,19 +16,18 @@ def can_handle_url(cls, url):

def _get_streams(self):
res = http.get(self.url)
match = _rtmp_re.search(res.text)
rtmp_url = match.group(0)

if not match:
match = _rtmp_re.search(res.content)
if match:
params = {
"rtmp": match.group(0),
"pageUrl": self.url,
"live": True,
}
yield 'live', RTMPStream(self.session, params)
return

stream = RTMPStream(self.session, {
"rtmp": rtmp_url,
"pageUrl": self.url,
"live": True,
})

return dict(live=stream)

match = _vod_re.search(res.content)
if match:
yield 'vod', HTTPStream(self.session, match.group(1))

__plugin__ = LivecodingTV
14 changes: 7 additions & 7 deletions src/livestreamer/plugins/livestream.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
),
}, None)
},
validate.optional("viewerPlusSwfUrl"): validate.url(scheme="http"),
validate.optional("hdPlayerSwfUrl"): validate.text
validate.optional("playerUri"): validate.text
})
_smil_schema = validate.Schema(validate.union({
"http_base": validate.all(
Expand Down Expand Up @@ -94,12 +93,13 @@ def _get_streams(self):

play_url = stream_info.get("play_url")
if play_url:
swf_url = info.get("viewerPlusSwfUrl") or info.get("hdPlayerSwfUrl")
if not swf_url.startswith("http"):
swf_url = "http://" + swf_url
swf_url = info.get("playerUri")
if swf_url:
if not swf_url.startswith("http"):
swf_url = "http://" + swf_url

# Work around broken SSL.
swf_url = swf_url.replace("https://", "http://")
# Work around broken SSL.
swf_url = swf_url.replace("https://", "http://")

qualities = stream_info["qualities"]
for bitrate, stream in self._parse_smil(play_url, swf_url):
Expand Down
36 changes: 24 additions & 12 deletions src/livestreamer/plugins/nrk.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re

from livestreamer.compat import urljoin
from livestreamer.plugin import Plugin
from livestreamer.plugin.api import http, validate
from livestreamer.stream import HLSStream
Expand All @@ -10,26 +11,30 @@
"preferred-player-live=hlslink"
)

_id_re = re.compile("/(?:program|direkte|serie/[^/]+)/([^/]+)")
_url_re = re.compile("https?://(tv|radio).nrk.no/")
_media_url_re = re.compile("""
<div[^>]*?id="playerelement"[^>]+
data-media="(?P<url>[^"]+)"
""", re.VERBOSE)
_api_baseurl_re = re.compile('apiBaseUrl:\s*"(?P<baseurl>[^"]+)"')

_schema = validate.Schema(
validate.transform(_media_url_re.search),
validate.transform(_api_baseurl_re.search),
validate.any(
None,
validate.all(
validate.get("url"),
validate.get("baseurl"),
validate.url(
scheme="http",
path=validate.endswith(".m3u8")
scheme="http"
)
)
)
)

_mediaelement_schema = validate.Schema({
"mediaUrl": validate.url(
scheme="http",
path=validate.endswith(".m3u8")
)
})


class NRK(Plugin):
@classmethod
Expand All @@ -42,10 +47,17 @@ def _get_streams(self):
cookie = {
"NRK_PLAYER_SETTINGS_{0}".format(stream_type): COOKIE_PARAMS
}
playlist_url = http.get(self.url, cookies=cookie, schema=_schema)
if not playlist_url:
return

return HLSStream.parse_variant_playlist(self.session, playlist_url)
# Construct API URL for this program.
baseurl = http.get(self.url, cookies=cookie, schema=_schema)
program_id = _id_re.search(self.url).group(1)

# Extract media URL.
json_url = urljoin(baseurl, "mediaelement/{}".format(program_id))
res = http.get(json_url, cookies=cookie)
media_element = http.json(res, schema=_mediaelement_schema)
media_url = media_element["mediaUrl"]

return HLSStream.parse_variant_playlist(self.session, media_url)

__plugin__ = NRK
Loading