radiko.jpのタイムフリーを一括ダウンロードバッチ2022年10月修正

以前に記事にしたradiko.jpのタイムフリーを一括ダウンロードバッチのラジコプレミアムのログイン仕様が変更されたので対応版を作りました。
しかしまだ不明点があって後日修正するかもしれません。
今はこのブログでの活動時間があまりなく更新も時間差があります。
よく調査してないので不十分な点があります。
それでも良ければお付き合いください

利用にあたって
wget.exe
https://eternallybored.org/misc/wget/

ffmpeg.exe
https://ffmpeg.org/

新たにXMLの検証の為xmllintを使います
Windows版xmllintは、
このサイトの
https://www.zlatkovic.com/libxml.en.html

Win32 binaries (HTTP)

から、libxml2 バイナリをダウンロードしてください。

iconv-1.9.2.win32.zip
libxml2-2.7.8.win32.zip
zlib-1.2.5.win32.zip

をダウンロードして(将来ファイル名のバージョンは変わるかも)
iconv.dll
libxml2.dll
zlib1.dll
xmllint.exe
を置いてください

各種実行ファイルを集めて一つのフォルダに置いてください
ここでの例としてD:\RadikoRecに置いてます

以下のテキストソースも集めて一つのフォルダに置いてください

AYTHKeyGet.vbs


Option Explicit

Dim argStation,argDebug,argSilent,cookiefile,mail,pass,checkfile,loginfile

'radiko premium
mail = ""
pass = ""

argStation = "TBS"    '引数1(i):radiko.jp内の放送局のid
argDebug = "no"        '"yes"なら、動作の途中経過を逐次表示する
argSilent = "yes"    '"yes"なら、外部コマンドであるwgetの実行プロンプトを表示しない

Dim i
Dim strCmd
Dim objFSO
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Dim aUnnamed
Set aUnnamed = WScript.Arguments.Unnamed
If aUnnamed.Count > 0 Then
    For i = 0 To aUnnamed.Count - 1
        Select Case i
            Case 0
                argStation = aUnnamed.Item(i)
        End Select
    Next
End If


'シェル起動オブジェクトを生成
Dim WSHShell,gShellStyle
Set WSHShell = WScript.CreateObject("WScript.Shell")
If argSilent = "yes" Then
    gShellStyle = 7
Else
    gShellStyle = 10
End IF

'ファイルシステム用オブジェクトを生成(ラジコ独自認証でファイルを利用)
Dim WSHFS
Set WSHFS = CreateObject("Scripting.FileSystemObject")

    strCmd = GetRadikoCmd()

WScript.Quit(0)

'Radiko.jpの独自認証を行う
Function GetRadikoCmd()

    Dim pSwfPlayer,pKeyFile,RtnCD,auth1_fms,auth2_fms

    Dim pTmpName
    pTmpName = WSHFS.GetTempName()
    pSwfPlayer = "player_" & argStation & pTmpName & ".swf"
    pKeyFile = "authkey_" & argStation & pTmpName & ".png"
    auth1_fms = "auth1_fms_" & argStation & pTmpName
    auth2_fms = "auth2_fms_" & argStation & pTmpName
    loginfile = "login_" & argStation & pTmpName & ".txt"
    cookiefile = "cookie_" & argStation & pTmpName & ".txt"
    checkfile = "pre_check_" & argStation & pTmpName & ".txt"

    'radiko premium
    if not (mail = "") then
        RtnCD = WSHShell.run("wget.exe -q --save-cookie=" & cookiefile _
                & " --keep-session-cookies" _
                & " --post-data=""mail=" & mail & "&pass=" & pass & """" _
                & " -O " & loginfile _
                & " https://radiko.jp/v4/api/member/login",gShellStyle,True)
        If RtnCD <> 0 Then
            GetRadikoCmd = "error 認証ステップ0:radiko premiumの取得失敗!"
            premiumLogoutCmd()
            Exit Function
        Else
            If argDebug = "yes" Then WScript.Echo "認証ステップ0"
        End If

        WScript.Sleep 2000
        if not objFso.FileExists(cookiefile) then
            WScript.Echo "failed login1"
            premiumLogoutCmd()
            Exit Function
        End IF
    End IF
    Dim pFile
    Dim pLine
    Dim pLine1
    Dim radiko_session,areafree
    Dim aryStrings
    Dim s
    Set pFile = WSHFS.OpenTextFile(loginfile,1)
    Do While pFile.AtEndOfStream = False
        pLine1 = pFile.ReadLine
        aryStrings = Split(pLine1, ",")

        For Each s In aryStrings
        pLine = s
        pLine =Replace(pLine,"}","")
        pLine =Replace(pLine,Chr(34),"")
        If InStr(LCase(pLine),"radiko_session") <> 0 Then
            radiko_session = mid(pLine,InStr(pLine,":")+1,len(pLine)-InStr(pLine,":"))
        ElseIf InStr(LCase(pLine),"areafree") <> 0 Then
            areafree = mid(pLine,InStr(pLine,":")+1)
        End If
    Next
    Loop
    pFile.Close

    '認証ステップ1:Radikoのauth1をダウンロード
    RtnCD = WSHShell.run("wget.exe -q --timeout=60 --tries=10" _
            & " --header ""X-Radiko-App: pc_html5""" _
            & " --header ""X-Radiko-App-Version: 0.0.1""" _
            & " --header ""X-Radiko-Device: pc""" _
            & " --header ""X-Radiko-User: dummy_user""" _
            & " --save-headers " _
            & " -O " & checkfile _
            & " https://radiko.jp/v2/api/auth1",gShellStyle,True)


    If RtnCD <> 0 Then
        GetRadikoCmd = "error 認証ステップ1:auth1の取得失敗!"
        premiumLogoutCmd()
        Exit Function
    Else
        If argDebug = "yes" Then WScript.Echo "認証ステップ1"
    End If


    '認証ステップ2:RadikoのplayerCommon.jsをダウンロード
    RtnCD = WSHShell.run("wget.exe -q --timeout=60 --tries=10" _
        & " -O """ & auth1_fms & """" _
        & " http://radiko.jp/apps/js/playerCommon.js",gShellStyle,True)
    If RtnCD <> 0 Then
        GetRadikoCmd = "error 認証ステップ2:playerCommon.jsの取得失敗!"
        premiumLogoutCmd()
        Exit Function
    Else
        If argDebug = "yes" Then WScript.Echo "認証ステップ2"
    End If

    '認証ステップ3:checkfileから、・X-Radiko-AuthToken・X-Radiko-KeyLength・X-Radiko-KeyOffset を読み取る
    Dim pAuthtoken,pLength,pOffset
    Set pFile = WSHFS.OpenTextFile(checkfile,1)
    Do While pFile.AtEndOfStream = False
        pLine = pFile.ReadLine
        If InStr(LCase(pLine),"x-radiko-authtoken:") <> 0 Then
            pAuthtoken = mid(pLine,InStr(pLine,":")+2,len(pLine)-InStr(pLine,":"))
        ElseIf InStr(LCase(pLine),"x-radiko-keylength:") <> 0 Then
            pLength = mid(pLine,InStr(pLine,":")+2,len(pLine)-InStr(pLine,":"))
        ElseIf InStr(LCase(pLine),"x-radiko-keyoffset:") <> 0 Then
            pOffset = mid(pLine,InStr(pLine,":")+2,len(pLine)-InStr(pLine,":"))
        End If
    Loop
    pFile.Close

    '認証ステップ4:playerCommon.jsから文字列取得
    Dim radikojsplayer
    Set pFile = WSHFS.OpenTextFile(auth1_fms,1)
    Do While pFile.AtEndOfStream = False
        pLine = pFile.ReadLine
        If InStr(LCase(pLine),"new radikojsplayer") <> 0 Then
            radikojsplayer = mid(pLine,65,40)
        End If
    Loop
    pFile.Close

    Dim fso
    Set fso = WScript.CreateObject("Scripting.FileSystemObject")
    Dim outputFile
    Set outputFile = fso.OpenTextFile(pKeyFile, 2, True)
    outputFile.WriteLine radikojsplayer
    outputFile.Close

    '認証ステップ5:playerCommon.jsの内容から、base64の値であるpartialkeyを求める
    Dim pXmldom,pB64,pStream,partialkey

    Set pXmldom = CreateObject("Microsoft.XMLDOM")
    Set pB64 = pXmldom.CreateElement("work")
    pB64.DataType = "bin.base64"

    Set pStream = CreateObject("ADODB.Stream")
    pStream.Type = 1
    pStream.Open
    pStream.LoadFromFile pKeyFile
    pStream.position = pOffset
    pB64.NodeTypedValue = pStream.Read(pLength)

    partialkey = pB64.Text
    pStream.Close
    Set pStream = Nothing

    '認証ステップ6:上記で求めたキーからauthtokenとpartialkeyをradikoに送り認証を成立させる

    Dim auth2_url_param
    auth2_url_param = "?radiko_session=" & radiko_session

    RtnCD = WSHShell.run("wget.exe -q --timeout=60 --tries=10" _
          & " --header ""X-Radiko-Device: pc""" _
          & " --header=""X-Radiko-Authtoken: " & pAuthtoken & """" _
          & " --header=""X-Radiko-Partialkey: " & partialkey & """" _
          & " --header ""X-Radiko-User: dummy_user"""_
          & " https://radiko.jp/v2/api/auth2" & auth2_url_param _
          & " -O """ & auth2_fms & """" ,gShellStyle,True)

    If RtnCD <> 0 Then
        GetRadikoCmd = "error 認証ステップ6:authtokenとpartialkeyの失敗!"
        premiumLogoutCmd()
        Exit Function
    Else
        If argDebug = "yes" Then WScript.Echo "認証ステップ6"
    End If

    'テンポラリファイルを消す
    If argDebug <> "yes" Then
        WSHFS.DeleteFile pKeyFile, True
        WSHFS.DeleteFile auth1_fms, True
        WSHFS.DeleteFile auth2_fms, True
        if not (mail = "") then
            WSHFS.DeleteFile loginfile, True
            WSHFS.DeleteFile checkfile, True
            WSHFS.DeleteFile cookiefile, True
        End If
    End If

        WScript.Echo(pAuthtoken)

End Function

Function premiumLogoutCmd()

    Dim RtnCD
    'radiko premium Logout
    if not (mail = "") then
        RtnCD = WSHShell.run("wget -q" _
            & " --header=""pragma: no-cache""" _
            & " --header=""Cache-Control: no-cache""" _
            & " --header=""Expires: Thu, 01 Jan 1970 00:00:00 GMT""" _
            & " --header=""Accept-Language: ja-jp""" _
            & " --header=""Accept-Encoding: gzip, deflate""" _
            & " --header=""Accept: application/json, text/javascript, */*; q=0.01""" _
            & " --header=""X-Requested-With: XMLHttpRequest""" _
            & " --no-check-certificate "_
            & " --load-cookies cookiefile "_
            & " --save-headers "_
            & " -O logoutfile" _
            & " https://radiko.jp/ap/member/webapi/member/logout",gShellStyle,True)
        If RtnCD <> 0 Then
            GetRadikoCmd = "error radiko premiumのLogout失敗!"
            Exit Function
        Else
            If argDebug = "yes" Then WScript.Echo "radiko premiumのLogout取得成功"
        End If
        if objFso.FileExists(cookiefile) then
            WSHFS.DeleteFile cookiefile, True
        End IF
    End IF

End Function

date.vbs

' CScript date.vbs w
' CScript date.vbs /d:-6

Option Explicit
dim argDay
Dim aNamed
Set aNamed = WScript.Arguments.Named
argDay = WScript.Arguments. Named.Item("d")
Dim strFormattedDate

Dim argC
Dim i
Dim strCmd
Dim objFSO
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Dim aUnnamed
Set aUnnamed = WScript.Arguments.Unnamed
argC = ""
If aUnnamed.Count > 0 Then
    For i = 0 To aUnnamed.Count - 1
        Select Case i
            Case 0
                argC = aUnnamed.Item(i)
        End Select
    Next
End If

if not (argDay = "") Then

    dim strNow
    strNow = Now()
    strFormattedDate = DateAdd("d",argDay,strNow)
    strFormattedDate = Left(strFormattedDate, 10)
    strFormattedDate = Replace(strFormattedDate, "/", "")
    WScript.Echo strFormattedDate

End If

if (argC = "w") Then
    WScript.Quit(WeekDay(Date))
End If

日付計算のためのスプリクトです。

timeshift_get.bat


D:
cd \RadikoRec

set F=./rec/
REM TBS,QRR,LFR,RN1,RN2,INT,FMT,FMJ,JORF,BAYFM78,NACK5,YFM,HOUSOU-DAIGAKU

goto DAY


:MON
for /f %%i in ('CScript date.vbs /d:%dayC%') do SET D=%%i
call :get_ts BAYFM78 %D%130000 %D%160000 %F%%D%1300_it
exit /b

:TUE
for /f %%i in ('CScript date.vbs /d:%dayC%') do SET D=%%i
call :get_ts BAYFM78 %D%130000 %D%160000 %F%%D%1300_it
exit /b

:WED
for /f %%i in ('CScript date.vbs /d:%dayC%') do SET D=%%i
call :get_ts BAYFM78 %D%130000 %D%160000 %F%%D%1300_it
exit /b

:THU
for /f %%i in ('CScript date.vbs /d:%dayC%') do SET D=%%i
call :get_ts BAYFM78 %D%130000 %D%160000 %F%%D%1300_it
exit /b

:FRI
for /f %%i in ('CScript date.vbs /d:%dayC%') do SET D=%%i
call :get_ts BAYFM78 %D%130000 %D%160000 %F%%D%1300_KISS&SMILE
exit /b

:SAT
for /f %%i in ('CScript date.vbs /d:%dayC%') do SET D=%%i
call :get_ts TBS %D%010000 %D%030000 %F%%D%0100_JUNK_バナナマンのバナナムーンGOLD
exit /b

:SUN
for /f %%i in ('CScript date.vbs /d:%dayC%') do SET D=%%i
call :get_ts TBS %D%130000 %D%170000 %F%%D%1300_爆笑問題の日曜サンデー
exit /b


REM ここから処理部分
:DAY
cscript /b date.vbs w
if %errorlevel%==7 set WDAY=SAT
if %errorlevel%==6 set WDAY=FRI
if %errorlevel%==5 set WDAY=THU
if %errorlevel%==4 set WDAY=WED
if %errorlevel%==3 set WDAY=TUE
if %errorlevel%==2 set WDAY=MON
if %errorlevel%==1 set WDAY=SUN

if "%WDAY%" == "MON" (
    set /a dayC=-6
    call :TUE
    set /a dayC=-5
    call :WED
    set /a dayC=-4
    call :THU
    set /a dayC=-3
    call :FRI
    set /a dayC=-2
    call :SAT
    set /a dayC=-1
    call :SUN
    set /a dayC=0
    call :MON
    goto end
)

if "%WDAY%" == "TUE" (
    set /a dayC=-6
    call :WED
    set /a dayC=-5
    call :THU
    set /a dayC=-4
    call :FRI
    set /a dayC=-3
    call :SAT
    set /a dayC=-2
    call :SUN
    set /a dayC=-1
    call :MON
    set /a dayC=0
    call :TUE
    goto end
)

if "%WDAY%" == "WED" (
    set /a dayC=-6
    call :THU
    set /a dayC=-5
    call :FRI
    set /a dayC=-4
    call :SAT
    set /a dayC=-3
    call :SUN
    set /a dayC=-2
    call :MON
    set /a dayC=-1
    call :TUE
    set /a dayC=0
    call :WED
    goto end
)

if "%WDAY%" == "THU" (
    set /a dayC=-6
    call :FRI
    set /a dayC=-5
    call :SAT
    set /a dayC=-4
    call :SUN
    set /a dayC=-3
    call :MON
    set /a dayC=-2
    call :TUE
    set /a dayC=-1
    call :WED
    set /a dayC=0
    call :THU
    goto end
)

if "%WDAY%" == "FRI" (
    set /a dayC=-6
    call :SAT
    set /a dayC=-5
    call :SUN
    set /a dayC=-4
    call :MON
    set /a dayC=-3
    call :TUE
    set /a dayC=-2
    call :WED
    set /a dayC=-1
    call :THU
    set /a dayC=0
    call :FRI
    goto end
)

if "%WDAY%" == "SAT" (
    set /a dayC=-6
    call :SUN
    set /a dayC=-5
    call :MON
    set /a dayC=-4
    call :TUE
    set /a dayC=-3
    call :WED
    set /a dayC=-2
    call :THU
    set /a dayC=-1
    call :FRI
    set /a dayC=0
    call :SAT
    goto end
)

if "%WDAY%" == "SUN" (
    set /a dayC=-6
    call :MON
    set /a dayC=-5
    call :TUE
    set /a dayC=-4
    call :WED
    set /a dayC=-3
    call :THU
    set /a dayC=-2
    call :FRI
    set /a dayC=-1
    call :SAT
    set /a dayC=0
    call :SUN
    goto end
)

echo date.vbs処理エラー
pause

:get_ts

set ch=%1
set ft=%2
set to=%3
set AYTHKeyGet=AYTHKeyGet.vbs
del timeshift.m3u8

for /f %%i in ('CScript %AYTHKeyGet% %ch%') do SET AUTHTOKEN=%%i

wget "https://radiko.jp/v3/station/stream/pc_html5/%ch%.xml"

for /f %%i in ('xmllint --xpath "/urls/url[@areafree=1][@timefree=1][1]/playlist_create_url/text()" %ch%.xml') do SET PLAYLIST_URL=%%i

DEL %ch%.xml
DEL %ch%.xml.?

ffmpeg -headers "X-Radiko-AuthToken: %AUTHTOKEN%" -i "%PLAYLIST_URL%?station_id=%ch%&start_at=%ft%&ft=%ft%&end_at=%to%&to=%to%&l=15&lsid=99999999999999999999999999999999&type=c" "%4.ts"

exit /b

:end

注意点
ffmpegの行の
lsid=99999999999999999999999999999999
は何のためのIDか不明です。9の文字はダミーです。
しかしこのIDを付加しないと400 Bad Requestになります。
将来の個人特定のIDかもしれませんが
今の所モニターしている限り固定値です
基本的にブラウザーのネットワークモニターで
m3u8ファイルを監視して
lsid=をコピペしたほうが無難です

xmllintでURLを抽出するターゲットとして
[@areafree=1][@timefree=1]
を指定してますエリアフリーなら1を
タイムフリーなら1を指定してください

XMLファイルからURLを抽出して
ダウンロードするように変更しましたが
このURLでダウンロードすると1倍速でダウンロードされて
時間がかかるようになりました
かなり不便になりました

不明な点が多いですがもし指摘点がございましたらコメントください

注意点終わり

毎日いつ実行してもいい状況を想定して書いてあります。
F=./rec/は録音ファイルのフォルダー名です。
MON(月)TUE(火)WED(水)THU(木)FRI(金)SAT(土)SUN(日)
なと曜日でダウンロードしたい日に番組を指定していきます。
get_ts 局名 開始時間 終了時間 録音ファイル名 です
EXITでバッチが終了します。
タイムフリーは一週間分しかダウンロードできません。
ラジコプレミアムに加入してればどの地域の人でも
ダウンロードできます。

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)