diff --git a/README.md b/README.md index 3816947..00116b4 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,11 @@ Bililive-go是一个支持多种直播平台的直播录制工具 www.huajiao.com 滋瓷 + + 抖音直播 + live.douyin.com + 滋瓷 + ## 依赖 diff --git a/src/cmd/bililive/internal/init.go b/src/cmd/bililive/internal/init.go index 3bd4e04..9040d44 100644 --- a/src/cmd/bililive/internal/init.go +++ b/src/cmd/bililive/internal/init.go @@ -5,6 +5,7 @@ import ( _ "github.com/hr3lxphr6j/bililive-go/src/live/acfun" _ "github.com/hr3lxphr6j/bililive-go/src/live/bilibili" _ "github.com/hr3lxphr6j/bililive-go/src/live/cc" + _ "github.com/hr3lxphr6j/bililive-go/src/live/douyin" _ "github.com/hr3lxphr6j/bililive-go/src/live/douyu" _ "github.com/hr3lxphr6j/bililive-go/src/live/huajiao" _ "github.com/hr3lxphr6j/bililive-go/src/live/huomao" diff --git a/src/live/douyin/douyin.go b/src/live/douyin/douyin.go new file mode 100644 index 0000000..4b25dcd --- /dev/null +++ b/src/live/douyin/douyin.go @@ -0,0 +1,97 @@ +package douyin + +import ( + "fmt" + "net/http" + "net/url" + + "github.com/hr3lxphr6j/requests" + "github.com/tidwall/gjson" + + "github.com/hr3lxphr6j/bililive-go/src/live" + "github.com/hr3lxphr6j/bililive-go/src/live/internal" + "github.com/hr3lxphr6j/bililive-go/src/pkg/utils" +) + +const ( + domain = "live.douyin.com" + cnName = "抖音" + + regRenderData = `` +) + +func init() { + live.Register(domain, new(builder)) +} + +type builder struct{} + +func (b *builder) Build(url *url.URL) (live.Live, error) { + return &Live{ + BaseLive: internal.NewBaseLive(url), + }, nil +} + +type Live struct { + internal.BaseLive +} + +func (l *Live) getData() (*gjson.Result, error) { + resp, err := requests.Get(l.Url.String(), live.CommonUserAgent) + if err != nil { + return nil, err + } + switch code := resp.StatusCode; code { + case http.StatusOK: + case http.StatusNotFound: + return nil, live.ErrRoomNotExist + default: + return nil, fmt.Errorf("failed to get page, code: %v, %w", code, live.ErrInternalError) + } + + body, err := resp.Text() + if err != nil { + return nil, err + } + rawData := utils.Match1(regRenderData, body) + if rawData == "" { + return nil, fmt.Errorf("failed to get RENDER_DATA from page, %w", live.ErrInternalError) + } + unescapedRawData, err := url.QueryUnescape(rawData) + if err != nil { + return nil, err + } + result := gjson.Parse(unescapedRawData) + return &result, nil +} + +func (l *Live) GetInfo() (info *live.Info, err error) { + data, err := l.getData() + if err != nil { + return nil, err + } + info = &live.Info{ + Live: l, + HostName: data.Get("routeInitialProps.roomInfo.room.owner.nickname").String(), + RoomName: data.Get("routeInitialProps.roomInfo.anchor.nickname").String(), + Status: data.Get("routeInitialProps.roomInfo.room.status").Int() == 2, + } + return +} + +func (l *Live) GetStreamUrls() (us []*url.URL, err error) { + data, err := l.getData() + if err != nil { + return nil, err + } + var urls []string + data.Get("routeInitialProps.roomInfo.room.stream_url.flv_pull_url").ForEach(func(key, value gjson.Result) bool { + urls = append(urls, value.String()) + return true + }) + return utils.GenUrls(urls...) +} + +func (l *Live) GetPlatformCNName() string { + return cnName +}