From 25a2d07cbf167e27d6a7bbb082eca24cdcbf64ca Mon Sep 17 00:00:00 2001 From: goodenough Date: Wed, 24 Jan 2024 14:24:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=B0=8F=E7=BA=A2?= =?UTF-8?q?=E4=B9=A6=E7=9B=B4=E6=92=AD=E5=BD=95=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cmd/bililive/internal/init.go | 1 + src/live/xiaohongshu/xiaohongshu.go | 115 ++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/live/xiaohongshu/xiaohongshu.go diff --git a/src/cmd/bililive/internal/init.go b/src/cmd/bililive/internal/init.go index cf023559..2a9dd292 100644 --- a/src/cmd/bililive/internal/init.go +++ b/src/cmd/bililive/internal/init.go @@ -20,6 +20,7 @@ import ( _ "github.com/hr3lxphr6j/bililive-go/src/live/system" _ "github.com/hr3lxphr6j/bililive-go/src/live/twitch" _ "github.com/hr3lxphr6j/bililive-go/src/live/weibolive" + _ "github.com/hr3lxphr6j/bililive-go/src/live/xiaohongshu" _ "github.com/hr3lxphr6j/bililive-go/src/live/yizhibo" _ "github.com/hr3lxphr6j/bililive-go/src/live/yy" _ "github.com/hr3lxphr6j/bililive-go/src/live/zhanqi" diff --git a/src/live/xiaohongshu/xiaohongshu.go b/src/live/xiaohongshu/xiaohongshu.go new file mode 100644 index 00000000..fcfff14e --- /dev/null +++ b/src/live/xiaohongshu/xiaohongshu.go @@ -0,0 +1,115 @@ +package xiaohongshu + +import ( + "fmt" + "github.com/hr3lxphr6j/bililive-go/src/live" + "github.com/hr3lxphr6j/bililive-go/src/live/internal" + "github.com/hr3lxphr6j/requests" + "github.com/tidwall/gjson" + "net/http" + "net/url" + "strings" +) + +const ( + domain = "www.xiaohongshu.com" + cnName = "小红书" + + roomApiUrl = "https://www.xiaohongshu.com/api/sns/red/live/app/v1/ecology/outside/share_info" + streamUrl = "http://live-play.xhscdn.com/live" + + userAgent = "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/14.2 Chrome/87.0.4280.141 Mobile Safari/537.36" +) + +func init() { + live.Register(domain, new(builder)) +} + +type builder struct{} + +func (b *builder) Build(url *url.URL, opt ...live.Option) (live.Live, error) { + return &Live{ + BaseLive: internal.NewBaseLive(url, opt...), + }, nil +} + +type Live struct { + internal.BaseLive +} + +func (l *Live) GetInfo() (info *live.Info, err error) { + headers := map[string]interface{}{ + "User-Agent": userAgent, + "Accept": "application/json, text/plain, */*", + "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", + "Referer": "https://www.xiaohongshu.com/hina/livestream/568979931846654360", + } + cookies := l.Options.Cookies.Cookies(l.Url) + cookieKVs := make(map[string]string) + for _, item := range cookies { + cookieKVs[item.Name] = item.Value + } + + roomUrl := l.Url.String() + urlParts := strings.Split(roomUrl, "?") + pathParts := strings.Split(urlParts[0], "/") + roomId := pathParts[len(pathParts)-1] + + resp, err := requests.Get( + roomApiUrl, + live.CommonUserAgent, + requests.Query("room_id", roomId), + requests.Cookies(cookieKVs), + requests.Headers(headers), + ) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, live.ErrRoomNotExist + } + + body, err := resp.Bytes() + if err != nil { + return nil, err + } + if gjson.GetBytes(body, "code").Int() != 0 { + return nil, live.ErrRoomNotExist + } + + info = &live.Info{ + Live: l, + HostName: gjson.GetBytes(body, "data.host_info.nickname").String(), + RoomName: gjson.GetBytes(body, "data.room.name").String(), + Status: gjson.GetBytes(body, "data.room.status").Int() == 0, + } + + return +} + +func (l *Live) GetStreamUrls() (us []*url.URL, err error) { + roomUrl := l.Url.String() + urlParts := strings.Split(roomUrl, "?") + pathParts := strings.Split(urlParts[0], "/") + roomId := pathParts[len(pathParts)-1] + + // 不用appuid也能播 + //appUIDRegex := regexp.MustCompile(`appuid=(.*?)&`) + //appUIDMatch := appUIDRegex.FindStringSubmatch(roomUrl) + //if len(appUIDMatch) < 2 { + // return nil, live.ErrRoomUrlIncorrect + //} + //appuid := appUIDMatch[1] + //flvURL := fmt.Sprintf("http://live-play.xhscdn.com/live/%s.flv?uid=%s", roomId, appuid) + mobileUrl := fmt.Sprintf("%s/%s.flv", streamUrl, roomId) + u, err := url.Parse(mobileUrl) + if err != nil { + return nil, err + } + + return []*url.URL{u}, nil +} + +func (l *Live) GetPlatformCNName() string { + return cnName +}