Skip to content

Commit daf3b49

Browse files
committed
Pass visitorData to requests
This is a nearly verbatim copy of [1], for now the code is pretty rough and I'm not even sure that this data should be passed for all requests but it does seem to unblock the iOS client for now. [1] TeamNewPipe/NewPipeExtractor#1262
1 parent 51b72ca commit daf3b49

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

client.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212
"net/http"
1313
"net/url"
1414
"strconv"
15+
"strings"
1516
"sync/atomic"
17+
"time"
1618
)
1719

1820
const (
@@ -23,6 +25,8 @@ const (
2325
playerParams = "CgIQBg=="
2426
)
2527

28+
const CONTENT_PLAYBACK_NONCE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
29+
2630
var ErrNoFormat = errors.New("no video format provided")
2731

2832
// DefaultClient type to use. No reason to change but you could if you wanted to.
@@ -157,6 +161,7 @@ type innertubeClient struct {
157161
TimeZone string `json:"timeZone"`
158162
UTCOffset int `json:"utcOffsetMinutes"`
159163
DeviceModel string `json:"deviceModel,omitempty"`
164+
VisitorData string `json:"visitorData,omitempty"`
160165
}
161166

162167
// client info for the innertube API
@@ -232,6 +237,33 @@ func (c *Client) transcriptDataByInnertube(ctx context.Context, id string, lang
232237
return c.httpPostBodyBytes(ctx, "https://www.youtube.com/youtubei/v1/get_transcript?key="+c.client.key, data)
233238
}
234239

240+
func randString(alphabet string, sz int) string {
241+
var buf strings.Builder
242+
buf.Grow(sz)
243+
for i := 0; i < sz; i++ {
244+
buf.WriteByte(alphabet[rand.Intn(len(alphabet))])
245+
}
246+
return buf.String()
247+
}
248+
249+
func randomVisitorData(countryCode string) string {
250+
var pbE2 ProtoBuilder
251+
252+
pbE2.String(2, "")
253+
pbE2.Varint(4, int64(rand.Intn(255)+1))
254+
255+
var pbE ProtoBuilder
256+
pbE.String(1, countryCode)
257+
pbE.Bytes(2, pbE2.ToBytes())
258+
259+
var pb ProtoBuilder
260+
pb.String(1, randString(CONTENT_PLAYBACK_NONCE_ALPHABET, 11))
261+
pb.Varint(5, time.Now().Unix()-int64(rand.Intn(600000)))
262+
pb.Bytes(6, pbE.ToBytes())
263+
264+
return pb.ToUrlEncodedBase64()
265+
}
266+
235267
func prepareInnertubeContext(clientInfo clientInfo) inntertubeContext {
236268
return inntertubeContext{
237269
Client: innertubeClient{
@@ -243,6 +275,7 @@ func prepareInnertubeContext(clientInfo clientInfo) inntertubeContext {
243275
ClientVersion: clientInfo.version,
244276
AndroidSDKVersion: clientInfo.androidVersion,
245277
UserAgent: clientInfo.userAgent,
278+
VisitorData: randomVisitorData("US"),
246279
},
247280
}
248281
}

protobuilder.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package youtube
2+
3+
import (
4+
"bytes"
5+
"encoding/base64"
6+
"net/url"
7+
)
8+
9+
type ProtoBuilder struct {
10+
byteBuffer bytes.Buffer
11+
}
12+
13+
func (pb *ProtoBuilder) ToBytes() []byte {
14+
return pb.byteBuffer.Bytes()
15+
}
16+
17+
func (pb *ProtoBuilder) ToUrlEncodedBase64() string {
18+
b64 := base64.URLEncoding.EncodeToString(pb.ToBytes())
19+
return url.QueryEscape(b64)
20+
}
21+
22+
func (pb *ProtoBuilder) writeVarint(val int64) error {
23+
if val == 0 {
24+
_, err := pb.byteBuffer.Write([]byte{0})
25+
return err
26+
}
27+
for {
28+
b := byte(val & 0x7F)
29+
val >>= 7
30+
if val != 0 {
31+
b |= 0x80
32+
}
33+
_, err := pb.byteBuffer.Write([]byte{b})
34+
if err != nil {
35+
return err
36+
}
37+
if val == 0 {
38+
break
39+
}
40+
}
41+
return nil
42+
}
43+
44+
func (pb *ProtoBuilder) field(field int, wireType byte) error {
45+
val := int64(field<<3) | int64(wireType&0x07)
46+
return pb.writeVarint(val)
47+
}
48+
49+
func (pb *ProtoBuilder) Varint(field int, val int64) error {
50+
err := pb.field(field, 0)
51+
if err != nil {
52+
return err
53+
}
54+
return pb.writeVarint(val)
55+
}
56+
57+
func (pb *ProtoBuilder) String(field int, stringVal string) error {
58+
strBts := []byte(stringVal)
59+
return pb.Bytes(field, strBts)
60+
}
61+
62+
func (pb *ProtoBuilder) Bytes(field int, bytesVal []byte) error {
63+
if err := pb.field(field, 2); err != nil {
64+
return err
65+
}
66+
67+
if err := pb.writeVarint(int64(len(bytesVal))); err != nil {
68+
return err
69+
}
70+
71+
_, err := pb.byteBuffer.Write(bytesVal)
72+
return err
73+
}

protobuilder_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package youtube
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestProtoBuilder(t *testing.T) {
10+
var pb ProtoBuilder
11+
12+
pb.Varint(1, 128)
13+
pb.Varint(2, 1234567890)
14+
pb.Varint(3, 1234567890123456789)
15+
pb.String(4, "Hello")
16+
pb.Bytes(5, []byte{1, 2, 3})
17+
assert.Equal(t, "CIABENKF2MwEGJWCpu_HnoSRESIFSGVsbG8qAwECAw%3D%3D", pb.ToUrlEncodedBase64())
18+
}

0 commit comments

Comments
 (0)