|
|
|
package vendor
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-kratos/aegis/circuitbreaker"
|
|
|
|
"github.com/go-kratos/aegis/circuitbreaker/sre"
|
|
|
|
consul "github.com/go-kratos/kratos/contrib/registry/consul/v2"
|
|
|
|
"github.com/go-kratos/kratos/contrib/registry/etcd/v2"
|
|
|
|
"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
|
|
|
|
kcircuitbreaker "github.com/go-kratos/kratos/v2/middleware/circuitbreaker"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
|
|
|
|
ggrpc "github.com/go-kratos/kratos/v2/transport/grpc"
|
|
|
|
"github.com/go-kratos/kratos/v2/transport/http"
|
|
|
|
jwtv4 "github.com/golang-jwt/jwt/v4"
|
|
|
|
"github.com/hashicorp/consul/api"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/synctv-org/synctv/internal/conf"
|
|
|
|
"github.com/synctv-org/vendors/api/bilibili"
|
|
|
|
bilibiliService "github.com/synctv-org/vendors/service/bilibili"
|
|
|
|
clientv3 "go.etcd.io/etcd/client/v3"
|
|
|
|
)
|
|
|
|
|
|
|
|
func BilibiliClient(name string) Bilibili {
|
|
|
|
if name != "" {
|
|
|
|
if cli, ok := bilibiliClients[name]; ok {
|
|
|
|
return cli
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bilibiliDefaultClient
|
|
|
|
}
|
|
|
|
|
|
|
|
func BilibiliClients() map[string]Bilibili {
|
|
|
|
return bilibiliClients
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
bilibiliClients map[string]Bilibili
|
|
|
|
bilibiliDefaultClient Bilibili
|
|
|
|
)
|
|
|
|
|
|
|
|
type Bilibili interface {
|
|
|
|
NewQRCode(ctx context.Context, in *bilibili.Empty) (*bilibili.NewQRCodeResp, error)
|
|
|
|
LoginWithQRCode(ctx context.Context, in *bilibili.LoginWithQRCodeReq) (*bilibili.LoginWithQRCodeResp, error)
|
|
|
|
NewCaptcha(ctx context.Context, in *bilibili.Empty) (*bilibili.NewCaptchaResp, error)
|
|
|
|
NewSMS(ctx context.Context, in *bilibili.NewSMSReq) (*bilibili.NewSMSResp, error)
|
|
|
|
LoginWithSMS(ctx context.Context, in *bilibili.LoginWithSMSReq) (*bilibili.LoginWithSMSResp, error)
|
|
|
|
ParseVideoPage(ctx context.Context, in *bilibili.ParseVideoPageReq) (*bilibili.VideoPageInfo, error)
|
|
|
|
GetVideoURL(ctx context.Context, in *bilibili.GetVideoURLReq) (*bilibili.VideoURL, error)
|
|
|
|
GetDashVideoURL(ctx context.Context, in *bilibili.GetDashVideoURLReq) (*bilibili.GetDashVideoURLResp, error)
|
|
|
|
GetSubtitles(ctx context.Context, in *bilibili.GetSubtitlesReq) (*bilibili.GetSubtitlesResp, error)
|
|
|
|
ParsePGCPage(ctx context.Context, in *bilibili.ParsePGCPageReq) (*bilibili.VideoPageInfo, error)
|
|
|
|
GetPGCURL(ctx context.Context, in *bilibili.GetPGCURLReq) (*bilibili.VideoURL, error)
|
|
|
|
GetDashPGCURL(ctx context.Context, in *bilibili.GetDashPGCURLReq) (*bilibili.GetDashPGCURLResp, error)
|
|
|
|
UserInfo(ctx context.Context, in *bilibili.UserInfoReq) (*bilibili.UserInfoResp, error)
|
|
|
|
Match(ctx context.Context, in *bilibili.MatchReq) (*bilibili.MatchResp, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
func InitBilibiliVendors(conf map[string]conf.VendorBilibili) error {
|
|
|
|
if bilibiliClients == nil {
|
|
|
|
bilibiliClients = make(map[string]Bilibili, len(conf))
|
|
|
|
}
|
|
|
|
for k, vb := range conf {
|
|
|
|
cli, err := InitBilibili(&vb)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if k == "" {
|
|
|
|
bilibiliDefaultClient = cli
|
|
|
|
} else {
|
|
|
|
bilibiliClients[k] = cli
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if bilibiliDefaultClient == nil {
|
|
|
|
bilibiliDefaultClient = bilibiliService.NewBilibiliService(nil)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func InitBilibili(conf *conf.VendorBilibili) (Bilibili, error) {
|
|
|
|
key := []byte(conf.JwtSecret)
|
|
|
|
switch conf.Scheme {
|
|
|
|
case "grpc":
|
|
|
|
opts := []ggrpc.ClientOption{}
|
|
|
|
|
|
|
|
if conf.JwtSecret != "" {
|
|
|
|
opts = append(opts, ggrpc.WithMiddleware(
|
|
|
|
jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
|
|
|
|
return key, nil
|
|
|
|
}, jwt.WithSigningMethod(jwtv4.SigningMethodHS256)),
|
|
|
|
kcircuitbreaker.Client(kcircuitbreaker.WithCircuitBreaker(func() circuitbreaker.CircuitBreaker {
|
|
|
|
return sre.NewBreaker(
|
|
|
|
sre.WithRequest(25),
|
|
|
|
sre.WithWindow(time.Second*15),
|
|
|
|
)
|
|
|
|
})),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
if conf.TimeOut != "" {
|
|
|
|
timeout, err := time.ParseDuration(conf.TimeOut)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
opts = append(opts, ggrpc.WithTimeout(timeout))
|
|
|
|
}
|
|
|
|
|
|
|
|
if conf.Endpoint != "" {
|
|
|
|
opts = append(opts, ggrpc.WithEndpoint(conf.Endpoint))
|
|
|
|
log.Infof("bilibili client init success with endpoint: %s", conf.Endpoint)
|
|
|
|
} else if conf.Consul.Endpoint != "" {
|
|
|
|
if conf.ServerName == "" {
|
|
|
|
return nil, errors.New("bilibili server name is empty")
|
|
|
|
}
|
|
|
|
c := api.DefaultConfig()
|
|
|
|
c.Address = conf.Consul.Endpoint
|
|
|
|
client, err := api.NewClient(c)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("discovery:///%s", conf.ServerName)
|
|
|
|
dis := consul.New(client)
|
|
|
|
opts = append(opts, ggrpc.WithEndpoint(endpoint), ggrpc.WithDiscovery(dis))
|
|
|
|
log.Infof("bilibili client init success with consul: %s", conf.Consul.Endpoint)
|
|
|
|
} else if len(conf.Etcd.Endpoints) > 0 {
|
|
|
|
if conf.ServerName == "" {
|
|
|
|
return nil, errors.New("bilibili server name is empty")
|
|
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("discovery:///%s", conf.ServerName)
|
|
|
|
cli, err := clientv3.New(clientv3.Config{
|
|
|
|
Endpoints: conf.Etcd.Endpoints,
|
|
|
|
Username: conf.Etcd.Username,
|
|
|
|
Password: conf.Etcd.Password,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dis := etcd.New(cli)
|
|
|
|
opts = append(opts, ggrpc.WithEndpoint(endpoint), ggrpc.WithDiscovery(dis))
|
|
|
|
log.Infof("bilibili client init success with etcd: %v", conf.Etcd.Endpoints)
|
|
|
|
} else {
|
|
|
|
return nil, errors.New("bilibili client init failed, endpoint is empty")
|
|
|
|
}
|
|
|
|
var (
|
|
|
|
con *grpc.ClientConn
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
if conf.Tls {
|
|
|
|
var rootCAs *x509.CertPool
|
|
|
|
rootCAs, err = x509.SystemCertPool()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if conf.CustomCAFile != "" {
|
|
|
|
b, err := os.ReadFile(conf.CustomCAFile)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
rootCAs.AppendCertsFromPEM(b)
|
|
|
|
}
|
|
|
|
opts = append(opts, ggrpc.WithTLSConfig(&tls.Config{
|
|
|
|
RootCAs: rootCAs,
|
|
|
|
}))
|
|
|
|
|
|
|
|
con, err = ggrpc.Dial(
|
|
|
|
context.Background(),
|
|
|
|
opts...,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
con, err = ggrpc.DialInsecure(
|
|
|
|
context.Background(),
|
|
|
|
opts...,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newGrpcBilibili(bilibili.NewBilibiliClient(con)), nil
|
|
|
|
case "http":
|
|
|
|
opts := []http.ClientOption{}
|
|
|
|
if conf.Tls {
|
|
|
|
rootCAs, err := x509.SystemCertPool()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if conf.CustomCAFile != "" {
|
|
|
|
b, err := os.ReadFile(conf.CustomCAFile)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
rootCAs.AppendCertsFromPEM(b)
|
|
|
|
}
|
|
|
|
opts = append(opts, http.WithTLSConfig(&tls.Config{
|
|
|
|
RootCAs: rootCAs,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
if conf.JwtSecret != "" {
|
|
|
|
opts = append(opts, http.WithMiddleware(
|
|
|
|
jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
|
|
|
|
return key, nil
|
|
|
|
}, jwt.WithSigningMethod(jwtv4.SigningMethodHS256)),
|
|
|
|
kcircuitbreaker.Client(kcircuitbreaker.WithCircuitBreaker(func() circuitbreaker.CircuitBreaker {
|
|
|
|
return sre.NewBreaker(
|
|
|
|
sre.WithRequest(25),
|
|
|
|
sre.WithWindow(time.Second*15),
|
|
|
|
)
|
|
|
|
})),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
if conf.TimeOut != "" {
|
|
|
|
timeout, err := time.ParseDuration(conf.TimeOut)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
opts = append(opts, http.WithTimeout(timeout))
|
|
|
|
}
|
|
|
|
|
|
|
|
if conf.Endpoint != "" {
|
|
|
|
opts = append(opts, http.WithEndpoint(conf.Endpoint))
|
|
|
|
log.Infof("bilibili client init success with endpoint: %s", conf.Endpoint)
|
|
|
|
} else if conf.Consul.Endpoint != "" {
|
|
|
|
if conf.ServerName == "" {
|
|
|
|
return nil, errors.New("bilibili server name is empty")
|
|
|
|
}
|
|
|
|
c := api.DefaultConfig()
|
|
|
|
c.Address = conf.Consul.Endpoint
|
|
|
|
client, err := api.NewClient(c)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
c.Token = conf.Consul.Token
|
|
|
|
c.TokenFile = conf.Consul.TokenFile
|
|
|
|
c.PathPrefix = conf.Consul.PathPrefix
|
|
|
|
c.Namespace = conf.Consul.Namespace
|
|
|
|
c.Partition = conf.Consul.Partition
|
|
|
|
endpoint := fmt.Sprintf("discovery:///%s", conf.ServerName)
|
|
|
|
dis := consul.New(client)
|
|
|
|
opts = append(opts, http.WithEndpoint(endpoint), http.WithDiscovery(dis))
|
|
|
|
log.Infof("bilibili client init success with consul: %s", conf.Consul.Endpoint)
|
|
|
|
} else if len(conf.Etcd.Endpoints) > 0 {
|
|
|
|
if conf.ServerName == "" {
|
|
|
|
return nil, errors.New("bilibili server name is empty")
|
|
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("discovery:///%s", conf.ServerName)
|
|
|
|
cli, err := clientv3.New(clientv3.Config{
|
|
|
|
Endpoints: conf.Etcd.Endpoints,
|
|
|
|
Username: conf.Etcd.Username,
|
|
|
|
Password: conf.Etcd.Password,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dis := etcd.New(cli)
|
|
|
|
opts = append(opts, http.WithEndpoint(endpoint), http.WithDiscovery(dis))
|
|
|
|
log.Infof("bilibili client init success with etcd: %v", conf.Etcd.Endpoints)
|
|
|
|
} else {
|
|
|
|
return nil, errors.New("bilibili client init failed, endpoint is empty")
|
|
|
|
}
|
|
|
|
con, err := http.NewClient(
|
|
|
|
context.Background(),
|
|
|
|
opts...,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newHTTPBilibili(bilibili.NewBilibiliHTTPClient(con)), nil
|
|
|
|
default:
|
|
|
|
return nil, errors.New("unknow bilibili scheme")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Bilibili = (*grpcBilibili)(nil)
|
|
|
|
|
|
|
|
type grpcBilibili struct {
|
|
|
|
client bilibili.BilibiliClient
|
|
|
|
}
|
|
|
|
|
|
|
|
func newGrpcBilibili(client bilibili.BilibiliClient) *grpcBilibili {
|
|
|
|
return &grpcBilibili{
|
|
|
|
client: client,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) NewQRCode(ctx context.Context, in *bilibili.Empty) (*bilibili.NewQRCodeResp, error) {
|
|
|
|
return g.client.NewQRCode(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) LoginWithQRCode(ctx context.Context, in *bilibili.LoginWithQRCodeReq) (*bilibili.LoginWithQRCodeResp, error) {
|
|
|
|
return g.client.LoginWithQRCode(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) NewCaptcha(ctx context.Context, in *bilibili.Empty) (*bilibili.NewCaptchaResp, error) {
|
|
|
|
return g.client.NewCaptcha(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) NewSMS(ctx context.Context, in *bilibili.NewSMSReq) (*bilibili.NewSMSResp, error) {
|
|
|
|
return g.client.NewSMS(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) LoginWithSMS(ctx context.Context, in *bilibili.LoginWithSMSReq) (*bilibili.LoginWithSMSResp, error) {
|
|
|
|
return g.client.LoginWithSMS(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) ParseVideoPage(ctx context.Context, in *bilibili.ParseVideoPageReq) (*bilibili.VideoPageInfo, error) {
|
|
|
|
return g.client.ParseVideoPage(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) GetVideoURL(ctx context.Context, in *bilibili.GetVideoURLReq) (*bilibili.VideoURL, error) {
|
|
|
|
return g.client.GetVideoURL(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) GetDashVideoURL(ctx context.Context, in *bilibili.GetDashVideoURLReq) (*bilibili.GetDashVideoURLResp, error) {
|
|
|
|
return g.client.GetDashVideoURL(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) GetSubtitles(ctx context.Context, in *bilibili.GetSubtitlesReq) (*bilibili.GetSubtitlesResp, error) {
|
|
|
|
return g.client.GetSubtitles(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) ParsePGCPage(ctx context.Context, in *bilibili.ParsePGCPageReq) (*bilibili.VideoPageInfo, error) {
|
|
|
|
return g.client.ParsePGCPage(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) GetPGCURL(ctx context.Context, in *bilibili.GetPGCURLReq) (*bilibili.VideoURL, error) {
|
|
|
|
return g.client.GetPGCURL(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) GetDashPGCURL(ctx context.Context, in *bilibili.GetDashPGCURLReq) (*bilibili.GetDashPGCURLResp, error) {
|
|
|
|
return g.client.GetDashPGCURL(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) UserInfo(ctx context.Context, in *bilibili.UserInfoReq) (*bilibili.UserInfoResp, error) {
|
|
|
|
return g.client.UserInfo(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *grpcBilibili) Match(ctx context.Context, in *bilibili.MatchReq) (*bilibili.MatchResp, error) {
|
|
|
|
return g.client.Match(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Bilibili = (*httpBilibili)(nil)
|
|
|
|
|
|
|
|
type httpBilibili struct {
|
|
|
|
client bilibili.BilibiliHTTPClient
|
|
|
|
}
|
|
|
|
|
|
|
|
func newHTTPBilibili(client bilibili.BilibiliHTTPClient) *httpBilibili {
|
|
|
|
return &httpBilibili{
|
|
|
|
client: client,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) NewQRCode(ctx context.Context, in *bilibili.Empty) (*bilibili.NewQRCodeResp, error) {
|
|
|
|
return h.client.NewQRCode(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) LoginWithQRCode(ctx context.Context, in *bilibili.LoginWithQRCodeReq) (*bilibili.LoginWithQRCodeResp, error) {
|
|
|
|
return h.client.LoginWithQRCode(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) NewCaptcha(ctx context.Context, in *bilibili.Empty) (*bilibili.NewCaptchaResp, error) {
|
|
|
|
return h.client.NewCaptcha(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) NewSMS(ctx context.Context, in *bilibili.NewSMSReq) (*bilibili.NewSMSResp, error) {
|
|
|
|
return h.client.NewSMS(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) LoginWithSMS(ctx context.Context, in *bilibili.LoginWithSMSReq) (*bilibili.LoginWithSMSResp, error) {
|
|
|
|
return h.client.LoginWithSMS(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) ParseVideoPage(ctx context.Context, in *bilibili.ParseVideoPageReq) (*bilibili.VideoPageInfo, error) {
|
|
|
|
return h.client.ParseVideoPage(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) GetVideoURL(ctx context.Context, in *bilibili.GetVideoURLReq) (*bilibili.VideoURL, error) {
|
|
|
|
return h.client.GetVideoURL(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) GetDashVideoURL(ctx context.Context, in *bilibili.GetDashVideoURLReq) (*bilibili.GetDashVideoURLResp, error) {
|
|
|
|
return h.client.GetDashVideoURL(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) GetSubtitles(ctx context.Context, in *bilibili.GetSubtitlesReq) (*bilibili.GetSubtitlesResp, error) {
|
|
|
|
return h.client.GetSubtitles(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) ParsePGCPage(ctx context.Context, in *bilibili.ParsePGCPageReq) (*bilibili.VideoPageInfo, error) {
|
|
|
|
return h.client.ParsePGCPage(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) GetPGCURL(ctx context.Context, in *bilibili.GetPGCURLReq) (*bilibili.VideoURL, error) {
|
|
|
|
return h.client.GetPGCURL(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) GetDashPGCURL(ctx context.Context, in *bilibili.GetDashPGCURLReq) (*bilibili.GetDashPGCURLResp, error) {
|
|
|
|
return h.client.GetDashPGCURL(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) UserInfo(ctx context.Context, in *bilibili.UserInfoReq) (*bilibili.UserInfoResp, error) {
|
|
|
|
return h.client.UserInfo(ctx, in)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpBilibili) Match(ctx context.Context, in *bilibili.MatchReq) (*bilibili.MatchResp, error) {
|
|
|
|
return h.client.Match(ctx, in)
|
|
|
|
}
|