Radiko Streaming Application


In [1]:
playerurl = "http://radiko.jp/player/swf/player_3.0.0.01.swf"
playerfile = "tmp.swf"
keyfile = "tmp.png"

In [2]:
import urllib
import urllib2
import os
import sys
import base64
import subprocess

get player


In [3]:
body = urllib2.urlopen(playerurl).read()

if not os.path.exists(playerfile):
    try:
        with open(playerfile, "w") as f:
            f.write(body)
    except URLError, e:
        print e
        exit()

get keydata (need swftool)


In [4]:
if not os.path.exists(keyfile):
    cmd = "swfextract -b 14 %s -o %s" % (playerfile, keyfile)
    subprocess.call(cmd.strip().split(" "))

access auth1_fms


In [5]:
auth_response = {}
url = "https://radiko.jp/v2/api/auth1_fms"
headers = {
    "pragma":"no-cache",
    "X-Radiko-App":"pc_1",
    "X-Radiko-App-Version":"2.0.1",
    "X-Radiko-User":"test-stream",
    "X-Radiko-Device":"pc"
}
values = {"\r\n:": ""}
data = urllib.urlencode(values)

In [6]:
try:
    req = urllib2.Request(url, "\r\n", headers)
    res = urllib2.urlopen(req)
    auth_response["body"] = res.read()
    auth_response["headers"] = dict(res.info())
except:
    print "failed auth1 process"

In [7]:
auth_response


Out[7]:
{'body': 'X-Radiko-AppType=pc\r\nX-Radiko-AuthToken=2bD3QG2RDavxwd4XZ0pVAQ\r\nX-Radiko-AuthWait=0\r\nX-Radiko-Delay=0\r\nX-Radiko-KeyLength=16\r\nX-Radiko-KeyOffset=89370\r\n\r\nplease send a part of key',
 'headers': {'connection': 'close',
  'content-type': 'text/plain',
  'date': 'Thu, 13 Nov 2014 04:48:44 GMT',
  'server': 'nginx',
  'transfer-encoding': 'chunked',
  'x-radiko-apptype': 'pc',
  'x-radiko-authtoken': '2bD3QG2RDavxwd4XZ0pVAQ',
  'x-radiko-authwait': '0',
  'x-radiko-delay': '0',
  'x-radiko-keylength': '16',
  'x-radiko-keyoffset': '89370'}}

get partial key


In [8]:
authtoken  = auth_response["headers"]["x-radiko-authtoken"]
offset = auth_response["headers"]["x-radiko-keyoffset"]
length = auth_response["headers"]["x-radiko-keylength"]

In [9]:
offset = int(offset)
length = int(length)

In [10]:
with open(keyfile, "rb+") as f:
    f.seek(offset)
    data = f.read(length)
    partialkey = base64.b64encode(data)

In [11]:
print "authtoken: %s \noffset: %s length: %s \npartialkey: %s" % (authtoken,offset,length,partialkey)


authtoken: 2bD3QG2RDavxwd4XZ0pVAQ 
offset: 89370 length: 16 
partialkey: uPOf8aVwEOJAQFxlF/hB5w==

access auth2_fms


In [12]:
auth_success_responce = {}
url = "https://radiko.jp/v2/api/auth2_fms"
headers = {
    "pragma":"no-cache",
    "X-Radiko-App":"pc_1",
    "X-Radiko-App-Version":"2.0.1",
    "X-Radiko-User":"test-stream",
    "X-Radiko-Device":"pc",
    "X-Radiko-Authtoken":authtoken,
    "X-Radiko-Partialkey":partialkey , 
}

In [13]:
try:
    req = urllib2.Request(url, "\r\n", headers)
    res = urllib2.urlopen(req)
    auth_success_responce["body"] = res.read()
    auth_success_responce["headers"] = dict(res.info())
except URLError, e:
    print e

In [14]:
auth_success_responce


Out[14]:
{'body': '\r\n\r\nJP13,\xe6\x9d\xb1\xe4\xba\xac\xe9\x83\xbd,tokyo Japan\r\n',
 'headers': {'connection': 'close',
  'content-type': 'text/plain',
  'date': 'Thu, 13 Nov 2014 04:49:19 GMT',
  'server': 'nginx',
  'transfer-encoding': 'chunked'}}

In [15]:
area = auth_success_responce["body"].strip().split(",")
areaid = area[0]
print area, areaid


['JP13', '\xe6\x9d\xb1\xe4\xba\xac\xe9\x83\xbd', 'tokyo Japan'] JP13

list of channels


In [16]:
channels = subprocess.check_output("curl -s http://radiko.jp/v2/api/program/today?area_id=%s " % areaid+
"| xmllint --format --xpath //station/@id - " + " | ruby -ne 'puts $_.split ' " , shell=True)

In [17]:
print channels


id="TBS"
id="QRR"
id="LFR"
id="RN1"
id="RN2"
id="INT"
id="FMT"
id="FMJ"
id="JORF"
id="BAYFM78"
id="NACK5"
id="YFM"
id="HOUSOU-DAIGAKU"


In [18]:
channel = "FMT"
#if not channel in channels:
#    print "station %s is not available." % channel
#    exit(1)

get stream url


In [19]:
tmp_xml = "%s.xml" % channel
if os.path.exists(tmp_xml):
    os.remove(tmp_xml)

In [20]:
channel_url = "http://radiko.jp/v2/station/stream/%s.xml" % channel
try:
    body = urllib2.urlopen(channel_url).read()
except:
    print "error in to get %s" % tmp_xml
with open(tmp_xml, 'w') as f:
    f.write(body)

In [21]:
cmd = "xmllint %s.xml --xpath /url/item[1]/text() " % channel
stream_url = subprocess.check_output(cmd.strip().split(" "))
print stream_url


rtmpe://f-radiko.smartstream.ne.jp/FMT/_definst_/simul-stream.stream

In [22]:
cmd = "echo '%s' | perl -pe 's!^(.*)://(.*?)/(.*)/(.*?)$/!$1://$2 $3 $4!'" % stream_url
print cmd


echo 'rtmpe://f-radiko.smartstream.ne.jp/FMT/_definst_/simul-stream.stream' | perl -pe 's!^(.*)://(.*?)/(.*)/(.*?)$/!$1://$2 $3 $4!'

In [23]:
ret = subprocess.check_output(cmd, shell=True)
ret


Out[23]:
'rtmpe://f-radiko.smartstream.ne.jp FMT/_definst_ simul-stream.stream'

In [24]:
url_parts = ret.split(" ")
url_parts


Out[24]:
['rtmpe://f-radiko.smartstream.ne.jp', 'FMT/_definst_', 'simul-stream.stream']

In [25]:
os.remove(tmp_xml)

In [26]:
DURATION = 60 #1m
options = (
    url_parts[0],
    url_parts[1],
    url_parts[2],
    playerurl,
    authtoken,
    DURATION
)
play_cmd = 'rtmpdump -v \
    -r %s \
    --app %s\
    --playpath %s \
    -W %s \
    -C S:"" -C S:"" -C S:"" -C S:%s \
    --live \
    --stop %s ' % options
play_cmd


Out[26]:
'rtmpdump -v     -r rtmpe://f-radiko.smartstream.ne.jp     --app FMT/_definst_    --playpath simul-stream.stream     -W http://radiko.jp/player/swf/player_3.0.0.01.swf     -C S:"" -C S:"" -C S:"" -C S:2bD3QG2RDavxwd4XZ0pVAQ     --live     --stop 60 '

In [27]:
p1 = subprocess.Popen(play_cmd.strip().split(" "), stdout=subprocess.PIPE)
p2 = subprocess.Popen(["mplayer", "-"], stdin=p1.stdout)
p1.stdout.close()
output = p2.communicate()[0]


---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-27-01b3ce9a35db> in <module>()
      2 p2 = subprocess.Popen(["mplayer", "-"], stdin=p1.stdout)
      3 p1.stdout.close()
----> 4 output = p2.communicate()[0]

/usr/lib/python2.7/subprocess.pyc in communicate(self, input)
    794                 stderr = _eintr_retry_call(self.stderr.read)
    795                 self.stderr.close()
--> 796             self.wait()
    797             return (stdout, stderr)
    798 

/usr/lib/python2.7/subprocess.pyc in wait(self)
   1374             while self.returncode is None:
   1375                 try:
-> 1376                     pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0)
   1377                 except OSError as e:
   1378                     if e.errno != errno.ECHILD:

/usr/lib/python2.7/subprocess.pyc in _eintr_retry_call(func, *args)
    474     while True:
    475         try:
--> 476             return func(*args)
    477         except (OSError, IOError) as e:
    478             if e.errno == errno.EINTR:

KeyboardInterrupt: 

In [ ]: