Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ad5b9d0

Browse files
authored
Merge pull request streamlink#289 from beardypig/turkish-streams
Tweaks and updates for Turkish streams
2 parents 93e0204 + 14adeda commit ad5b9d0

8 files changed

Lines changed: 202 additions & 49 deletions

File tree

docs/plugin_matrix.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ antenna antenna.gr -- Yes
2121
ard_live live.daserste.de Yes -- Streams may be geo-restricted to Germany.
2222
ard_mediathek ardmediathek.de Yes Yes Streams may be geo-restricted to Germany.
2323
artetv arte.tv Yes Yes
24-
atv atv.com.tr Yes No
24+
atv - atv.com.tr Yes No
25+
- a2tv.com.tr
2526
azubutv azubu.tv Yes No
2627
bambuser bambuser.com Yes Yes
2728
beam beam.pro Yes No
@@ -39,6 +40,10 @@ dingittv dingit.tv Yes Yes
3940
disney_de - video.disney.de Yes Yes Streams may be geo-restricted to Germany.
4041
- disneychannel.de
4142
dmcloud api.dmcloud.net Yes --
43+
dogan - teve2.com.tr Yes Yes VOD is supported for teve2 and kanald
44+
- kanald.com.tr
45+
- dreamtv.com.tr
46+
- cnnturk.com
4247
dommune dommune.com Yes --
4348
douyutv douyutv.com Yes --
4449
dplay - dplay.se -- Yes Streams may be geo-restricted.
@@ -74,6 +79,7 @@ nos nos.nl Yes Yes Streams may be geo-restrict
7479
npo npo.nl Yes Yes Streams may be geo-restricted to Netherlands.
7580
nrk - tv.nrk.no Yes Yes Streams may be geo-restricted to Norway.
7681
- radio.nrk.no
82+
ntv ntv.com.tr Yes No
7783
oldlivestream original.liv... [3]_ Yes No Only mobile streams are supported.
7884
openrectv openrec.tv Yes Yes
7985
orf_tvthek tvthek.orf.at Yes Yes

src/streamlink/plugins/atv.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,35 @@
88

99
class ATV(Plugin):
1010
"""
11-
Plugin to support ATV Live stream from http://www.atv.com.tr/webtv/canli-yayin
11+
Plugin to support ATV/A2TV Live streams from www.atv.com.tr and www.a2tv.com.tr
1212
"""
1313

14-
_url_re = re.compile(r"http://www.atv.com.tr/webtv/canli-yayin")
15-
_hls_url = "http://trkvz-live.ercdn.net/atvhd/atvhd.m3u8"
14+
_url_re = re.compile(r"http://www.(a2?tv).com.tr/webtv/canli-yayin")
15+
_hls_url = "http://trkvz-live.ercdn.net/{channel}/{channel}.m3u8"
1616
_token_url = "http://videotoken.tmgrup.com.tr/webtv/secure"
17-
_token_schema = validate.Schema({
18-
"Success": True,
19-
"Url": validate.url(),
20-
})
17+
_token_schema = validate.Schema(validate.all(
18+
{
19+
"Success": True,
20+
"Url": validate.url(),
21+
},
22+
validate.get("Url"))
23+
)
2124

2225
@classmethod
2326
def can_handle_url(cls, url):
2427
return cls._url_re.match(url) is not None
2528

2629
def _get_streams(self):
27-
res = http.get(self._token_url, params={"url": self._hls_url})
28-
data = http.json(res, schema=self._token_schema)
29-
return HLSStream.parse_variant_playlist(self.session, data["Url"])
30+
domain = self._url_re.match(self.url).group(1)
31+
# remap the domain to channel
32+
channel = {"atv": "atvhd"}.get(domain, domain)
33+
34+
hls_url = self._hls_url.format(channel=channel)
35+
# get the secure HLS URL
36+
res = http.get(self._token_url, params={"url": hls_url})
37+
secure_hls_url = http.json(res, schema=self._token_schema)
38+
39+
return HLSStream.parse_variant_playlist(self.session, secure_hls_url)
3040

3141

3242
__plugin__ = ATV

src/streamlink/plugins/dogan.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# coding=utf-8
2+
from __future__ import print_function
3+
import xml.etree.ElementTree as ET
4+
import re
5+
6+
from streamlink.plugin import Plugin
7+
from streamlink.plugin.api import http
8+
from streamlink.plugin.api import validate
9+
from streamlink.stream import HLSStream
10+
from streamlink.compat import urljoin
11+
12+
13+
class Dogan(Plugin):
14+
"""
15+
Support for the live streams from Doğan Media Group channels
16+
"""
17+
url_re = re.compile(r"""
18+
https?://(?:www.)?
19+
(?:teve2.com.tr/(?:canli-yayin|filmler/.*|programlar/.*)|
20+
kanald.com.tr/.*|
21+
cnnturk.com/canli-yayin|
22+
dreamtv.com.tr/canli-yayin)
23+
""", re.VERBOSE)
24+
playerctrl_re = re.compile(r'''<div[^>]*?ng-controller=(?P<quote>["'])(?:Live)?PlayerCtrl(?P=quote).*?>''', re.DOTALL)
25+
data_id_re = re.compile(r'''data-id=(?P<quote>["'])(?P<id>\w+)(?P=quote)''')
26+
content_id_re = re.compile(r'"contentId", "(\w+)"')
27+
content_api = "/actions/content/media/{id}"
28+
alt_content_api = "/action/media/{id}"
29+
content_api_schema = validate.Schema({
30+
"Id": validate.text,
31+
"Media": {
32+
"Link": {
33+
"DefaultServiceUrl": validate.url(),
34+
validate.optional("ServiceUrl"): validate.url(),
35+
"SecurePath": validate.text,
36+
}
37+
}
38+
})
39+
40+
@classmethod
41+
def can_handle_url(cls, url):
42+
print(url)
43+
return cls.url_re.match(url) is not None
44+
45+
def _get_content_id(self):
46+
res = http.get(self.url)
47+
# find the PlayerCtrl div
48+
player_ctrl_m = self.playerctrl_re.search(res.text)
49+
if player_ctrl_m:
50+
# extract the content id from the player control data
51+
player_ctrl_div = player_ctrl_m.group(0)
52+
content_id_m = self.data_id_re.search(player_ctrl_div)
53+
if content_id_m:
54+
return content_id_m.group("id")
55+
56+
# use the fall back regex
57+
content_id_m = self.content_id_re.search(res.text)
58+
return content_id_m and content_id_m.group(1)
59+
60+
def _get_hls_url(self, content_id):
61+
# make the api url relative to the current domain
62+
if "cnnturk" in self.url:
63+
self.logger.debug("Using alternative content API url")
64+
api_url = urljoin(self.url, self.alt_content_api.format(id=content_id))
65+
else:
66+
api_url = urljoin(self.url, self.content_api.format(id=content_id))
67+
68+
apires = http.get(api_url)
69+
70+
stream_data = http.json(apires, schema=self.content_api_schema)
71+
d = stream_data["Media"]["Link"]
72+
return urljoin((d["ServiceUrl"] or d["DefaultServiceUrl"]), d["SecurePath"])
73+
74+
def _get_streams(self):
75+
content_id = self._get_content_id()
76+
if content_id:
77+
self.logger.debug(u"Loading content: {}", content_id)
78+
hls_url = self._get_hls_url(content_id)
79+
return HLSStream.parse_variant_playlist(self.session, hls_url)
80+
else:
81+
self.logger.error(u"Could not find the contentId for this stream")
82+
83+
84+
__plugin__ = Dogan

src/streamlink/plugins/foxtr.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from streamlink.plugin.api import validate
77
from streamlink.stream import HLSStream
88

9+
910
class FoxTR(Plugin):
1011
"""
1112
Support for Turkish Fox live stream: http://www.fox.com.tr/canli-yayin
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
from __future__ import print_function
2+
23
import re
34

4-
from streamlink.plugin import Plugin
55
from streamlink.plugin.api import http
6-
from streamlink.plugin.api import validate
7-
from streamlink.stream import HLSStream
6+
from streamlink.plugins.startv import StarTVBase
87

98

10-
class KralMuzik(Plugin):
9+
class KralMuzik(StarTVBase):
1110
url_re = re.compile(r"https?://www.kralmuzik.com.tr/tv/kral-tv")
12-
stream_url_re = re.compile(r"(?P<quote>[\"'])(?P<url>https?://[^ ]*?/live/hls/[^ ]*?\?token=[^ ]*?)(?P=quote);")
13-
14-
@classmethod
15-
def can_handle_url(cls, url):
16-
return cls.url_re.match(url) is not None
11+
mobile_url_re = re.compile(r"(?P<quote>[\"'])(?P<url>https?://[^ ]*?/live/hls/[^ ]*?\?token=[^ ]*?)(?P=quote);")
12+
desktop_url_re = re.compile(r"(?P<quote>[\"'])(?P<url>https?://[^ ]*?/live/hds/[^ ]*?\?token=[^ ]*?)(?P=quote);")
1713

1814
def _get_streams(self):
1915
res = http.get(self.url)
20-
match = self.stream_url_re.search(res.text)
21-
if match:
22-
return HLSStream.parse_variant_playlist(self.session, match.group("url"))
16+
mobile_match = self.mobile_url_re.search(res.text)
17+
desktop_match = self.desktop_url_re.search(res.text)
18+
19+
mobile_url = mobile_match.group("url") if mobile_match else None
20+
desktop_url = desktop_match.group("url") if desktop_match else None
21+
22+
return self._get_star_streams(desktop_url, mobile_url)
2323

2424

2525
__plugin__ = KralMuzik

src/streamlink/plugins/ntv.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from __future__ import print_function
2+
3+
import re
4+
5+
from streamlink.plugin.api import http
6+
from streamlink.plugins.startv import StarTVBase
7+
8+
9+
class NTR(StarTVBase):
10+
"""
11+
Support for live streams from http://www.ntv.com.tr/, very similar to StarTV
12+
"""
13+
url_re = re.compile(r"https?://www.ntv.com.tr/canli-yayin/ntv")
14+
token_re = re.compile(r"data-player-token[ ]*=[ ]*(?P<quote>[\"'])(?P<token>.*?)(?P=quote)")
15+
player_src = re.compile(r"data-player-src[ ]*=[ ]*(?P<quote>[\"'])(?P<url>.*?)(?P=quote)")
16+
player_mobile_src = re.compile(r"data-player-mobile[ ]*=[ ]*(?P<quote>[\"'])(?P<url>.*?)(?P=quote)")
17+
18+
def _get_streams(self):
19+
res = http.get(self.url)
20+
token_m = self.token_re.search(res.text)
21+
desktop_player_m = self.player_src.search(res.text)
22+
mobile_player_m = self.player_mobile_src.search(res.text)
23+
desktop_url, mobile_url = None, None
24+
25+
if token_m and desktop_player_m: # desktop stream
26+
desktop_url = desktop_player_m.group("url") + token_m.group("token")
27+
28+
if token_m and mobile_player_m: # mobile stream
29+
mobile_url = mobile_player_m.group("url") + token_m.group("token")
30+
31+
return self._get_star_streams(desktop_url, mobile_url)
32+
33+
34+
__plugin__ = NTR

src/streamlink/plugins/startv.py

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,67 @@
11
from __future__ import print_function
22

3-
import json
43
import re
54

65
from streamlink.plugin import Plugin
76
from streamlink.plugin.api import http
87
from streamlink.plugin.api import validate
8+
from streamlink.stream import HDSStream
99
from streamlink.stream import HLSStream
1010

1111

12-
class StarTV(Plugin):
12+
class StarTVBase(Plugin):
1313
"""
14-
Support for the Live Stream from www.startv.com.tr, only the mobile stream (HLS) is supported as the HDS stream
15-
has some kind of token authentication.
16-
17-
TODO: support HDS streams
14+
Base class for all StarTV streams
1815
"""
19-
url_re = re.compile(r"http://www.startv.com.tr/canli-yayin")
20-
playervars_re = re.compile(r"mobileUrl: \"(.*?)\"")
21-
token_schema = validate.Schema(
22-
validate.transform(playervars_re.search), # search the playerArray variable
23-
validate.any(
24-
None,
25-
validate.all(
26-
validate.get(1), # get the first match
27-
validate.url(),
28-
)
29-
)
30-
)
31-
api_schema = validate.Schema(validate.all(
16+
url_re = None
17+
hds_schema = validate.Schema(validate.all(
3218
{
3319
"success": True,
34-
"xtra": {"url": validate.url()}
20+
"xtra": {
21+
"url": validate.url(),
22+
"use_akamai": bool
23+
}
3524
},
36-
validate.get("xtra"), validate.get("url")
25+
validate.get("xtra")
3726
))
27+
SWF_URL = "http://dygassets.akamaized.net/player2/plugins/flowplayer/flowplayer.httpstreaming-3.2.11.swf"
3828

3929
@classmethod
4030
def can_handle_url(cls, url):
31+
if not cls.url_re:
32+
raise NotImplementedError
4133
return cls.url_re.match(url) is not None
4234

35+
def _get_star_streams(self, desktop_url, mobile_url):
36+
if mobile_url:
37+
for s in HLSStream.parse_variant_playlist(self.session,
38+
mobile_url,
39+
headers={"Referer": self.url}).items():
40+
yield s
41+
if desktop_url:
42+
# get the HDS stream URL
43+
res = http.get(desktop_url)
44+
stream_data = http.json(res, schema=self.hds_schema)
45+
for s in HDSStream.parse_manifest(self.session,
46+
stream_data["url"],
47+
pvswf=self.SWF_URL,
48+
is_akamai=stream_data["use_akamai"],
49+
headers={"Referer": self.url}).items():
50+
yield s
51+
52+
53+
class StarTV(StarTVBase):
54+
"""
55+
Support for the Live Stream from www.startv.com.tr, both HLS and HDS streams
56+
"""
57+
url_re = re.compile(r"http://www.startv.com.tr/canli-yayin")
58+
mobile_player = re.compile(r"mobileUrl: \"(.*?)\"")
59+
desktop_player = re.compile(r"flashUrl: \"(.*?)\"")
60+
4361
def _get_streams(self):
4462
res = http.get(self.url)
45-
mobile_url = self.token_schema.validate(res.text)
46-
if mobile_url:
47-
return HLSStream.parse_variant_playlist(self.session, mobile_url, headers={"Referer": self.url})
63+
mobile_url = self.mobile_player.search(res.text)
64+
desktop_url = self.desktop_player.search(res.text)
65+
return self._get_star_streams(desktop_url.group(1), mobile_url.group(1))
4866

4967
__plugin__ = StarTV

src/streamlink/stream/hds.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ def open(self):
418418
return reader
419419

420420
@classmethod
421-
def parse_manifest(cls, session, url, timeout=60, pvswf=None,
421+
def parse_manifest(cls, session, url, timeout=60, pvswf=None, is_akamai=False,
422422
**request_params):
423423
"""Parses a HDS manifest and returns its substreams.
424424
@@ -440,7 +440,7 @@ def parse_manifest(cls, session, url, timeout=60, pvswf=None,
440440
request_params.pop("timeout", None)
441441
request_params.pop("url", None)
442442

443-
if "akamaihd" in url:
443+
if "akamaihd" in url or is_akamai:
444444
request_params["params"]["hdcore"] = HDCORE_VERSION
445445

446446
res = session.http.get(url, exception=IOError, **request_params)

0 commit comments

Comments
 (0)