1- package youtube
1+ package decipher
22
33import (
44 "fmt"
55 "io/ioutil"
6+ "net/http"
67 "net/url"
78 "regexp"
89 "strconv"
910 "strings"
11+
12+ "github.com/pkg/errors"
1013)
1114
12- func (y * Youtube ) parseDecipherOpsAndArgs () (operations []string , args []int , err error ) {
13- // try to get whole page
14- client , err := y .getHTTPClient ()
15+ type Decipher struct {
16+ client * http.Client
17+ }
18+
19+ func NewDecipher (client * http.Client ) * Decipher {
20+ return & Decipher {client : client }
21+ }
22+
23+ func (d Decipher ) Url (videoId string , cipher string ) (string , error ) {
24+ queryParams , err := url .ParseQuery (cipher )
25+ if err != nil {
26+ return "" , err
27+ }
28+ cipherMap := make (map [string ]string )
29+ for key , value := range queryParams {
30+ cipherMap [key ] = strings .Join (value , "" )
31+ }
32+ /* eg:
33+ extract decipher from https://youtube.com/s/player/4fbb4d5b/player_ias.vflset/en_US/base.js
34+
35+ var Mt={
36+ splice:function(a,b){a.splice(0,b)},
37+ reverse:function(a){a.reverse()},
38+ EQ:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c}};
39+
40+ a=a.split("");
41+ Mt.splice(a,3);
42+ Mt.EQ(a,39);
43+ Mt.splice(a,2);
44+ Mt.EQ(a,1);
45+ Mt.splice(a,1);
46+ Mt.EQ(a,35);
47+ Mt.EQ(a,51);
48+ Mt.splice(a,2);
49+ Mt.reverse(a,52);
50+ return a.join("")
51+ */
52+
53+ s := cipherMap ["s" ]
54+ bs := []byte (s )
55+ splice := func (b int ) {
56+ bs = bs [b :]
57+ }
58+ swap := func (b int ) {
59+ pos := b % len (bs )
60+ bs [0 ], bs [pos ] = bs [pos ], bs [0 ]
61+ }
62+ reverse := func (options ... interface {}) {
63+ l , r := 0 , len (bs )- 1
64+ for l < r {
65+ bs [l ], bs [r ] = bs [r ], bs [l ]
66+ l ++
67+ r --
68+ }
69+ }
70+ operations , args , err := d .parseDecipherOpsAndArgs (videoId )
1571 if err != nil {
16- return nil , nil , fmt . Errorf ( "get http client error=%s " , err )
72+ return " " , err
1773 }
74+ for i , op := range operations {
75+ switch op {
76+ case "splice" :
77+ splice (args [i ])
78+ case "swap" :
79+ swap (args [i ])
80+ case "reverse" :
81+ reverse (args [i ])
82+ }
83+ }
84+ cipherMap ["s" ] = string (bs )
85+
86+ decipheredUrl := fmt .Sprintf ("%s&%s=%s" , cipherMap ["url" ], cipherMap ["sp" ], cipherMap ["s" ])
87+ return decipheredUrl , nil
88+ }
89+ func (d Decipher ) parseDecipherOpsAndArgs (videoId string ) (operations []string , args []int , err error ) {
90+ // try to get whole page
91+ //client, err := y.getHTTPClient()
92+ //if err != nil {
93+ // return nil, nil, fmt.Errorf("get http client error=%s", err)
94+ //}
1895
19- if y . VideoID == "" {
96+ if videoId == "" {
2097 return nil , nil , fmt .Errorf ("video id is empty , err=%s" , err )
2198 }
22- embedUrl := fmt .Sprintf ("https://youtube.com/embed/%s?hl=en" , y . VideoID )
99+ embedUrl := fmt .Sprintf ("https://youtube.com/embed/%s?hl=en" , videoId )
23100
24- embeddedPageResp , err := client .Get (embedUrl )
101+ embeddedPageResp , err := d . client .Get (embedUrl )
25102 if err != nil {
26103 return nil , nil , err
27104 }
@@ -46,7 +123,7 @@ func (y *Youtube) parseDecipherOpsAndArgs() (operations []string, args []int, er
46123 // eg: ["js", "\/s\/player\/f676c671\/player_ias.vflset\/en_US\/base.js]
47124 arr := strings .Split (escapedBasejsUrl , ":\" " )
48125 basejsUrl := "https://youtube.com" + strings .ReplaceAll (arr [len (arr )- 1 ], "\\ " , "" )
49- basejsUrlResp , err := client .Get (basejsUrl )
126+ basejsUrlResp , err := d . client .Get (basejsUrl )
50127 if err != nil {
51128 return nil , nil , err
52129 }
@@ -67,22 +144,33 @@ func (y *Youtube) parseDecipherOpsAndArgs() (operations []string, args []int, er
67144
68145 // Ft=function(a){a=a.split("");Et.vw(a,2);Et.Zm(a,4);Et.Zm(a,46);Et.vw(a,2);Et.Zm(a,34);Et.Zm(a,59);Et.cn(a,42);return a.join("")} => get Ft
69146 arr = decipherFuncNamePattern .FindStringSubmatch (basejs )
147+ if len (arr ) < 2 {
148+ return nil , nil , errors .New ("decipher: function names not found" )
149+ }
70150 funcName := arr [1 ]
71151 decipherFuncBodyPattern := regexp .MustCompile (fmt .Sprintf (`[^h\.]%s=function\(\w+\)\{(.*?)\}` , funcName ))
72152
73153 // eg: get a=a.split("");Et.vw(a,2);Et.Zm(a,4);Et.Zm(a,46);Et.vw(a,2);Et.Zm(a,34);Et.Zm(a,59);Et.cn(a,42);return a.join("")
74154 arr = decipherFuncBodyPattern .FindStringSubmatch (basejs )
155+ if len (arr ) < 2 {
156+ return nil , nil , errors .New ("decipher: function bodies not found" )
157+ }
75158 decipherFuncBody := arr [1 ]
76159
77160 // FuncName in Body => get Et
78161 funcNameInBodyRegex := regexp .MustCompile (`(\w+).\w+\(\w+,\d+\);` )
79162 arr = funcNameInBodyRegex .FindStringSubmatch (decipherFuncBody )
163+ if len (arr ) < 2 {
164+ return nil , nil , errors .New ("decipher: function name from body not found" )
165+ }
80166 funcNameInBody := arr [1 ]
81- decipherDefBodyRegex := regexp .MustCompile (fmt .Sprintf (`var\s+ %s=\{(\w+:function\(\w+(,\w+)?\)\{(.*?)\}),?\};` , funcNameInBody ))
167+ decipherDefBodyRegex := regexp .MustCompile (fmt .Sprintf (`%s=\{(\w+:function\(\w+(,\w+)?\)\{(.*?)\}),?\};` , funcNameInBody ))
82168 re := regexp .MustCompile (`\r?\n` )
83169 basejs = re .ReplaceAllString (basejs , "" )
84170 arr1 := decipherDefBodyRegex .FindStringSubmatch (basejs )
85-
171+ if len (arr ) < 2 {
172+ return nil , nil , errors .New ("decipher: function def body not found" )
173+ }
86174 // eg: vw:function(a,b){a.splice(0,b)},cn:function(a){a.reverse()},Zm:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c}
87175 decipherDefBody := arr1 [1 ]
88176 // eq: [ a=a.split("") , Et.vw(a,2) , Et.Zm(a,4) , Et.Zm(a,46) , Et.vw(a,2) , Et.Zm(a,34), Et.Zm(a,59) , Et.cn(a,42) , return a.join("") ]
@@ -148,70 +236,3 @@ func (y *Youtube) parseDecipherOpsAndArgs() (operations []string, args []int, er
148236 }
149237 return funcSeq , funcArgs , nil
150238}
151-
152- func (y * Youtube ) decipher (cipher string ) (string , error ) {
153- queryParams , err := url .ParseQuery (cipher )
154- if err != nil {
155- return "" , err
156- }
157- cipherMap := make (map [string ]string )
158- for key , value := range queryParams {
159- cipherMap [key ] = strings .Join (value , "" )
160- }
161- /* eg:
162- extract decipher from https://youtube.com/s/player/4fbb4d5b/player_ias.vflset/en_US/base.js
163-
164- var Mt={
165- splice:function(a,b){a.splice(0,b)},
166- reverse:function(a){a.reverse()},
167- EQ:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c}};
168-
169- a=a.split("");
170- Mt.splice(a,3);
171- Mt.EQ(a,39);
172- Mt.splice(a,2);
173- Mt.EQ(a,1);
174- Mt.splice(a,1);
175- Mt.EQ(a,35);
176- Mt.EQ(a,51);
177- Mt.splice(a,2);
178- Mt.reverse(a,52);
179- return a.join("")
180- */
181-
182- s := cipherMap ["s" ]
183- bs := []byte (s )
184- splice := func (b int ) {
185- bs = bs [b :]
186- }
187- swap := func (b int ) {
188- pos := b % len (bs )
189- bs [0 ], bs [pos ] = bs [pos ], bs [0 ]
190- }
191- reverse := func (options ... interface {}) {
192- l , r := 0 , len (bs )- 1
193- for l < r {
194- bs [l ], bs [r ] = bs [r ], bs [l ]
195- l ++
196- r --
197- }
198- }
199- operations , args , err := y .parseDecipherOpsAndArgs ()
200- if err != nil {
201- return "" , err
202- }
203- for i , op := range operations {
204- switch op {
205- case "splice" :
206- splice (args [i ])
207- case "swap" :
208- swap (args [i ])
209- case "reverse" :
210- reverse (args [i ])
211- }
212- }
213- cipherMap ["s" ] = string (bs )
214-
215- decipheredUrl := fmt .Sprintf ("%s&%s=%s" , cipherMap ["url" ], cipherMap ["sp" ], cipherMap ["s" ])
216- return decipheredUrl , nil
217- }
0 commit comments