diff --git a/extractors/geekbang/geekbang.go b/extractors/geekbang/geekbang.go index 6d5124abd..2a2f5f460 100644 --- a/extractors/geekbang/geekbang.go +++ b/extractors/geekbang/geekbang.go @@ -16,14 +16,35 @@ type geekData struct { Code int `json:"code"` Error json.RawMessage `json:"error"` Data struct { - Title string `json:"article_sharetitle"` - VideoMediaMap map[string]struct { - URL string `json:"url"` - Size int64 `json:"size"` - } `json:"video_media_map"` + VideoID string `json:"video_id"` + Title string `json:"article_sharetitle"` + ColumnHadSub bool `json:"column_had_sub"` } `json:"data"` } +type videoPlayAuth struct { + Code int `json:"code"` + Error json.RawMessage `json:"error"` + Data struct { + PlayAuth string `json:"play_auth"` + } `json:"data"` +} + +type playInfo struct { + VideoBase struct { + VideoID string `json:"VideoId"` + Title string `json:"Title"` + CoverURL string `josn:"CoverURL"` + } `json:"VideoBase"` + PlayInfoList struct { + PlayInfo []struct { + URL string `json:"PlayURL"` + Size int64 `json:"Size"` + Definition string `json:"Definition"` + } `json:"PlayInfo"` + } `json:"PlayInfoList"` +} + type geekURLInfo struct { URL string Size int64 @@ -58,8 +79,9 @@ func Extract(url string) ([]downloader.Data, error) { return nil, extractors.ErrURLParseFailed } + // Get video information heanders := map[string]string{"Origin": "https://time.geekbang.org", "Content-Type": "application/json", "Referer": url} - params := strings.NewReader("{\"id\":" + string(matches[2]+"}")) + params := strings.NewReader("{\"id\":" + string(matches[2]) + "}") res, err := request.Request(http.MethodPost, "https://time.geekbang.org/serv/v1/article", params, heanders) if err != nil { return nil, err @@ -75,11 +97,45 @@ func Extract(url string) ([]downloader.Data, error) { return nil, errors.New(string(data.Error)) } + if data.Data.VideoID == "" && !data.Data.ColumnHadSub { + return nil, errors.New("请先购买课程,或使用Cookie登录。") + } + + // Get video license token information + params = strings.NewReader("{\"source_type\":1,\"aid\":" + string(matches[2]) + ",\"video_id\":\"" + string(data.Data.VideoID) + "\"}") + res, err = request.Request(http.MethodPost, "https://time.geekbang.org/serv/v3/source_auth/video_play_auth", params, heanders) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var playAuth videoPlayAuth + if err = json.NewDecoder(res.Body).Decode(&playAuth); err != nil { + return nil, err + } + + if playAuth.Code < 0 { + return nil, errors.New(string(playAuth.Error)) + } + + // Get video playback information + heanders = map[string]string{"Accept-Encoding": ""} + res, err = request.Request(http.MethodGet, "http://ali.mantv.top/play/info?playAuth="+playAuth.Data.PlayAuth, nil, heanders) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var playInfo playInfo + if err = json.NewDecoder(res.Body).Decode(&playInfo); err != nil { + return nil, err + } + title := data.Data.Title - streams := make(map[string]downloader.Stream, len(data.Data.VideoMediaMap)) + streams := make(map[string]downloader.Stream, len(playInfo.PlayInfoList.PlayInfo)) - for key, media := range data.Data.VideoMediaMap { + for _, media := range playInfo.PlayInfoList.PlayInfo { m3u8URLs, err := geekM3u8(media.URL) if err != nil { @@ -95,10 +151,10 @@ func Extract(url string) ([]downloader.Data, error) { } } - streams[key] = downloader.Stream{ + streams[media.Definition] = downloader.Stream{ URLs: urls, Size: media.Size, - Quality: key, + Quality: media.Definition, } } diff --git a/extractors/geekbang/geekbang_test.go b/extractors/geekbang/geekbang_test.go index ec5150229..fef1c1558 100644 --- a/extractors/geekbang/geekbang_test.go +++ b/extractors/geekbang/geekbang_test.go @@ -19,16 +19,15 @@ func TestDownload(t *testing.T) { args: test.Args{ URL: "https://time.geekbang.org/course/detail/190-97203", Title: "02 | 内容综述 - 玩转webpack", - Size: 38556544, + Size: 10752472, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // data, err := Extract(tt.args.URL) - // test.CheckError(t, err) - // test.Check(t, tt.args, data[0]) - Extract(tt.args.URL) + data, err := Extract(tt.args.URL) + test.CheckError(t, err) + test.Check(t, tt.args, data[0]) }) } } diff --git a/extractors/miaopai/miaopai.go b/extractors/miaopai/miaopai.go index ce14257cb..58efd6e34 100644 --- a/extractors/miaopai/miaopai.go +++ b/extractors/miaopai/miaopai.go @@ -2,6 +2,7 @@ package miaopai import ( "encoding/json" + "errors" "fmt" "math/rand" "strings" @@ -35,7 +36,7 @@ func getRandomString(l int) string { for i := 0; i < l; i++ { s = append(s, chars[rand.Intn(len(chars)-1)]) } - return strings.Join(s, ",") + return strings.Join(s, "") } // Extract is the main function for extracting data @@ -46,18 +47,27 @@ func Extract(url string) ([]downloader.Data, error) { } id := ids[1] + randomString := getRandomString(10) + var data miaopaiData jsonString, err := request.Get( - fmt.Sprintf("https://n.miaopai.com/api/aj_media/info.json?smid=%s&appid=530&_cb=_jsonp%s", id, getRandomString(10)), + fmt.Sprintf("https://n.miaopai.com/api/aj_media/info.json?smid=%s&appid=530&_cb=_jsonp%s", id, randomString), url, nil, ) if err != nil { return nil, err } - err = json.Unmarshal([]byte(jsonString), &data) + + match := utils.MatchOneOf(jsonString, randomString+`\((.*)\);$`) + if match == nil || len(match) < 2 { + return nil, errors.New("获取视频信息失败。") + } + + err = json.Unmarshal([]byte(match[1]), &data) if err != nil { return nil, err } + realURL := data.Data.MetaData[0].URLs.M size, err := request.Size(realURL, url) if err != nil {