/
radiko.go
157 lines (134 loc) · 3.88 KB
/
radiko.go
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package radiko
import (
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/YuheiNakasaka/radiorec/internal/filemanager"
"github.com/mattn/go-shellwords"
"github.com/YuheiNakasaka/radiorec/internal/recorder"
)
var (
swfURL = "http://radiko.jp/apps/js/flash/myplayer-release.swf"
rtmpURL = "rtmpe://f-radiko.smartstream.ne.jp"
)
// Radiko is radiko struct
type Radiko struct {
programID int
airtime int
storage string
}
// ProgramID is method to fill recorder.Recorder interface.
func (r *Radiko) ProgramID() int {
return r.programID
}
// Airtime is method to fill recorder.Recorder interface.
func (r *Radiko) Airtime() int {
return r.airtime
}
// Storage is method to fill recorder.Recorder interface.
func (r *Radiko) Storage() string {
return r.storage
}
// RecordCommand is method to fill recorder.Recorder interface.
// It returns rtmpdump command to record during airtime.
func (r *Radiko) RecordCommand(outputPath string) string {
authToken := authorize()
return "rtmpdump -q -r " + rtmpURL + " --playpath 'simul-stream.stream' --app 'QRR/_definst_' -W " + swfURL + " -C S:'' -C S:'' -C S:'' -C S:" + authToken + " --live --stop " + strconv.Itoa(r.airtime) + " -o " + outputPath + ".flv"
}
// Start : record ag program
func (r *Radiko) Start(programID int, airtime int, storage string) error {
radiko := &Radiko{}
radiko.programID = programID
radiko.airtime = airtime
radiko.storage = storage
return recorder.Record(radiko)
}
// authorize : 認証用のswf取得
func authorize() string {
// keyファイル作成
parentDir, _ := filemanager.StaticFilePath()
swfPath := filepath.Join(parentDir, "player2.swf")
keyPath := filepath.Join(parentDir, "radikokey2.png")
// 認証用swfダウンロード
response, err := http.Get(swfURL)
if err != nil {
panic(err)
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
panic(err)
}
file, err := os.OpenFile(swfPath, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
panic(err)
}
defer file.Close()
file.Write(body)
// 認証用フレーム抽出
swfCmd := "swfextract -b 12 " + swfPath + " -o " + keyPath
swfC, err := shellwords.Parse(swfCmd)
if err != nil {
panic(err)
}
exec.Command(swfC[0], swfC[1:]...).Run()
// 認証1
values := url.Values{}
req, err := http.NewRequest("POST", "https://radiko.jp/v2/api/auth1_fms", strings.NewReader(values.Encode()))
if err != nil {
panic(err)
}
req.Header.Add("pragma", "no-cache")
req.Header.Add("X-Radiko-App", "pc_ts")
req.Header.Add("X-Radiko-App-Version", "4.0.0")
req.Header.Add("X-Radiko-User", "test-stream")
req.Header.Add("X-Radiko-Device", "pc")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
autoToken := resp.Header.Get("X-Radiko-Authtoken")
keyOffset, _ := strconv.Atoi(resp.Header.Get("X-Radiko-KeyOffset"))
keyLength, _ := strconv.Atoi(resp.Header.Get("X-Radiko-Keylength"))
defer resp.Body.Close()
// 認証2
// partial key作成
fd, err := os.Open(keyPath)
if err != nil {
panic(err)
}
buf := make([]byte, keyLength)
_, err = fd.ReadAt(buf, int64(keyOffset))
if err != nil {
panic(err)
}
partialkey := base64.StdEncoding.EncodeToString(buf)
values2 := url.Values{}
req2, err := http.NewRequest("POST", "https://radiko.jp/v2/api/auth2_fms", strings.NewReader(values2.Encode()))
if err != nil {
panic(err)
}
req2.Header.Add("pragma", "no-cache")
req2.Header.Add("X-Radiko-App", "pc_ts")
req2.Header.Add("X-Radiko-App-Version", "4.0.0")
req2.Header.Add("X-Radiko-User", "test-stream")
req2.Header.Add("X-Radiko-Device", "pc")
req2.Header.Add("X-Radiko-Authtoken", autoToken)
req2.Header.Add("X-Radiko-Partialkey", partialkey)
client2 := &http.Client{}
resp2, err := client2.Do(req2)
if err != nil {
panic(err)
}
buf2, _ := ioutil.ReadAll(resp2.Body)
fmt.Println(string(buf2))
defer resp.Body.Close()
return autoToken
}