forked from streamlink/streamlink
/
radiko.py
114 lines (102 loc) · 3.91 KB
/
radiko.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""
$description Japanese live radio simulcasts and time-shifted broadcasts for over 100 stations.
$url radiko.jp
$type live, vod
$region Japan
"""
import base64
import datetime
import hashlib
import random
import re
from lxml.etree import XML
from streamlink.compat import urlencode
from streamlink.plugin import Plugin, pluginmatcher
from streamlink.stream.hls import HLSStream
@pluginmatcher(re.compile(
r'https?://radiko\.jp/(#!/)?(?P<state>live|ts)/(?P<station_id>[a-zA-Z0-9-]+)/?(?P<start_at>\d+)?'
))
class Radiko(Plugin):
_api_auth_1 = 'https://radiko.jp/v2/api/auth1'
_api_auth_2 = 'https://radiko.jp/v2/api/auth2'
_auth_key = 'bcd151073c03b352e1ef2fd66c32209da9ca0afa'
def _get_streams(self):
state = self.match.group('state')
station_id = self.match.group('station_id').upper()
if state == 'live':
url, token = self._live(station_id)
else:
start_at = self.match.group('start_at')
url, token = self._timefree(station_id, start_at)
headers = {
'X-Radiko-AuthToken': token
}
self.session.http.headers = headers
for s in HLSStream.parse_variant_playlist(self.session, url).items():
yield s
def _live(self, station_id):
live_url = 'http://f-radiko.smartstream.ne.jp/{}/_definst_/simul-stream.stream/playlist.m3u8'.format(station_id)
token, area_id = self._authorize()
lsid = hashlib.md5(str(random.random()).encode('utf-8')).hexdigest()
live_params = {
'station_id': station_id,
'l': 15,
'lsid': lsid,
'type': 'b'
}
url = live_url + '?' + urlencode(live_params)
return url, token
def _timefree(self, station_id, start_at):
m3u8_url = 'https://tf-rpaa.smartstream.ne.jp/tf/playlist.m3u8'
token, area_id = self._authorize()
lsid = hashlib.md5(str(random.random()).encode('utf-8')).hexdigest()
end_at = self._get_xml(start_at, station_id)
m3u8_params = {
'station_id': station_id,
'start_at': start_at,
'ft': start_at,
'end_at': end_at,
'to': end_at,
'l': 15,
'lsid': lsid,
'type': 'b'
}
url = m3u8_url + '?' + urlencode(m3u8_params)
return url, token
def _authorize(self):
headers = {
'x-radiko-app': 'pc_html5',
'x-radiko-app-version': '0.0.1',
'x-radiko-device': 'pc',
'x-radiko-user': 'dummy_user'
}
self.session.http.headers.update(headers)
r = self.session.http.get(self._api_auth_1)
token = r.headers.get('x-radiko-authtoken')
offset = int(r.headers.get('x-radiko-keyoffset'))
length = int(r.headers.get('x-radiko-keylength'))
partial_key = base64.b64encode(self._auth_key[offset:offset + length].encode('ascii')).decode('utf-8')
headers = {
'x-radiko-authtoken': token,
'x-radiko-device': 'pc',
'x-radiko-partialkey': partial_key,
'x-radiko-user': 'dummy_user'
}
self.session.http.headers.update(headers)
r = self.session.http.get(self._api_auth_2)
if r.status_code == 200:
return token, r.text.split(',')[0]
def _get_xml(self, start_at, station_id):
today = datetime.date(int(start_at[:4]), int(start_at[4:6]), int(start_at[6:8]))
yesterday = today - datetime.timedelta(days=1)
if int(start_at[8:10]) < 5:
date = yesterday.strftime('%Y%m%d')
else:
date = today.strftime('%Y%m%d')
api = 'http://radiko.jp/v3/program/station/date/{}/{}.xml'.format(date, station_id)
r = self.session.http.get(api)
tree = XML(r.content)
for x in tree[2][0][1].findall('prog'):
if x.attrib['ft'] == start_at:
return x.attrib['to']
__plugin__ = Radiko