diff --git a/app/register.go b/app/register.go index bf7523cf1..b0392472d 100644 --- a/app/register.go +++ b/app/register.go @@ -39,4 +39,5 @@ import ( _ "github.com/iawia002/lux/extractors/yinyuetai" _ "github.com/iawia002/lux/extractors/youku" _ "github.com/iawia002/lux/extractors/youtube" + _ "github.com/iawia002/lux/extractors/zhihu" ) diff --git a/extractors/zhihu/types.go b/extractors/zhihu/types.go new file mode 100644 index 000000000..c2d630d96 --- /dev/null +++ b/extractors/zhihu/types.go @@ -0,0 +1,17 @@ +package zhihu + +// minimum field +type video struct { + PlayList struct { + FHD resolution `json:"FHD"` + HD resolution `json:"HD"` + SD resolution `json:"SD"` + } `json:"playlist_v2"` +} + +// minimum field +type resolution struct { + Size int64 `json:"size"` + Format string `json:"format"` + PlayURL string `json:"play_url"` +} diff --git a/extractors/zhihu/zhihu.go b/extractors/zhihu/zhihu.go new file mode 100644 index 000000000..45a2a7e8c --- /dev/null +++ b/extractors/zhihu/zhihu.go @@ -0,0 +1,90 @@ +package zhihu + +import ( + "encoding/json" + "fmt" + "github.com/iawia002/lux/extractors" + "github.com/iawia002/lux/request" + "github.com/iawia002/lux/utils" + "github.com/pkg/errors" + "strings" +) + +const ( + videoURL = "www.zhihu.com/zvideo" + api = "https://lens.zhihu.com/api/v4/videos/" +) + +func init() { + extractors.Register("zhihu", New()) +} + +type extractor struct{} + +func New() extractors.Extractor { + return &extractor{} +} + +func (e *extractor) Extract(url string, option extractors.Options) ([]*extractors.Data, error) { + if !strings.Contains(url, videoURL) { + return nil, errors.WithStack(extractors.ErrURLParseFailed) + } + + var err error + html, err := request.Get(url, url, nil) + if err != nil { + return nil, errors.WithStack(err) + } + + videoID := utils.MatchOneOf(html, `"videoId":"(\d+)"`) + titleMatch := utils.MatchOneOf(html, `(.*?)`) + + if len(videoID) <= 1 { + return nil, errors.New("zhihu video id extract failed") + } + + title := "Unknown" + if len(titleMatch) > 1 { + title = titleMatch[1] + } + + resp, err := request.GetByte(fmt.Sprintf("%s%s", api, videoID[1]), url, nil) + if err != nil { + return nil, errors.WithStack(err) + } + var data video + if err = json.Unmarshal(resp, &data); err != nil { + return nil, errors.WithStack(err) + } + + streams := make(map[string]*extractors.Stream) + resolutions := map[string]resolution{ + "FHD": data.PlayList.FHD, + "HD": data.PlayList.HD, + "SD": data.PlayList.SD, + } + + for k, v := range resolutions { + stream := &extractors.Stream{ + Parts: []*extractors.Part{ + { + URL: v.PlayURL, + Size: v.Size, + Ext: v.Format, + }, + }, + Size: v.Size, + } + streams[k] = stream + } + + return []*extractors.Data{ + { + Site: "知乎 zhihu.com", + Title: title, + Streams: streams, + Type: extractors.DataTypeVideo, + URL: url, + }, + }, nil +} diff --git a/extractors/zhihu/zhihu_test.go b/extractors/zhihu/zhihu_test.go new file mode 100644 index 000000000..7c128f1d5 --- /dev/null +++ b/extractors/zhihu/zhihu_test.go @@ -0,0 +1,29 @@ +package zhihu + +import ( + "github.com/iawia002/lux/extractors" + "github.com/iawia002/lux/test" + "testing" +) + +func TestDownload(t *testing.T) { + tests := []struct { + name string + args test.Args + }{ + { + name: "video test", + args: test.Args{ + URL: "https://www.zhihu.com/zvideo/1620162752064061440", + Title: `Cursor, GPT-4 驱动的强大代码编辑器 - 知乎`, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + data, err := New().Extract(tt.args.URL, extractors.Options{}) + test.CheckError(t, err) + test.Check(t, tt.args, data[0]) + }) + } +}