Feat: micro vendor plugin

pull/31/head
zijiren233 3 years ago
parent 1208d29a2e
commit cd66601529

@ -33,6 +33,7 @@ var ServerCmd = &cobra.Command{
bootstrap.InitProvider,
bootstrap.InitOp,
bootstrap.InitRtmp,
bootstrap.InitVendor,
)
if !flags.DisableUpdateCheck {
boot.Add(bootstrap.InitCheckUpdate)

@ -9,12 +9,18 @@ require (
github.com/gin-contrib/cors v1.4.0
github.com/gin-gonic/gin v1.9.1
github.com/glebarez/sqlite v1.9.0
github.com/go-kratos/aegis v0.2.0
github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20231109033548-702f96c13e7f
github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20231109033548-702f96c13e7f
github.com/go-kratos/kratos/v2 v2.7.1
github.com/go-resty/resty/v2 v2.10.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/google/go-github/v56 v56.0.0
github.com/google/uuid v1.3.1
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/go-hclog v0.14.1
github.com/hashicorp/consul/api v1.26.1
github.com/hashicorp/go-hclog v1.5.0
github.com/hashicorp/go-plugin v1.5.2
github.com/json-iterator/go v1.1.12
github.com/mitchellh/go-homedir v1.1.0
@ -23,17 +29,19 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/soheilhy/cmux v0.1.5
github.com/spf13/cobra v1.7.0
github.com/synctv-org/vendors v0.1.1-0.20231109155917-3cb85668100d
github.com/ulule/limiter/v3 v3.11.2
github.com/zencoder/go-dash/v3 v3.0.3
github.com/zijiren233/gencontainer v0.0.0-20230930135658-e410015e13cc
github.com/zijiren233/gencontainer v0.0.0-20231108115721-8d4bb333a5bc
github.com/zijiren233/go-colorable v0.0.0-20230930131441-997304c961cb
github.com/zijiren233/livelib v0.2.3-0.20231103145812-58de2ae7f423
github.com/zijiren233/stream v0.5.1
github.com/zijiren233/yaml-comment v0.2.1
go.etcd.io/etcd/client/v3 v3.5.10
golang.org/x/crypto v0.14.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/oauth2 v0.13.0
google.golang.org/grpc v1.56.3
google.golang.org/grpc v1.58.3
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.2
@ -45,23 +53,37 @@ require (
cloud.google.com/go/compute v1.23.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/fatih/color v1.14.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-playground/form/v4 v4.2.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.15.5 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
github.com/google/wire v0.5.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@ -75,6 +97,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/run v1.0.0 // indirect
@ -87,7 +110,12 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
go.etcd.io/etcd/api/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/mock v0.3.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/arch v0.5.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.17.0 // indirect
@ -95,7 +123,9 @@ require (
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
modernc.org/libc v1.24.1 // indirect
modernc.org/mathutil v1.6.0 // indirect

247
go.sum

@ -4,6 +4,21 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
@ -15,6 +30,8 @@ github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
@ -22,15 +39,27 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/envoyproxy/go-control-plane v0.11.2-0.20230627204322-7d0032219fcb h1:kxNVXsNro/lpR5WD+P1FI/yUHn2G03Glber3k8cQL2Y=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g=
@ -44,9 +73,23 @@ github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9g
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.9.0 h1:Aj6bPA12ZEx5GbSF6XADmCkYXlljPNUY+Zf1EQxynXs=
github.com/glebarez/sqlite v1.9.0/go.mod h1:YBYCoyupOao60lzp1MVBLEjZfgkq0tdB1voAQ09K9zw=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ=
github.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI=
github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20231109033548-702f96c13e7f h1:Ijc6NpakrJ+9HNpk3M6vTY02Nsi/HTShs7+17HDmx2w=
github.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20231109033548-702f96c13e7f/go.mod h1:Z0S93CabLk3OdvQUoV1eTnwjHLAY8bZa+oOOU9sLQc8=
github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20231109033548-702f96c13e7f h1:EoXhe77tecPQg/MlbxwgpQD0bPhmKecbPo9Z6VtbIGM=
github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20231109033548-702f96c13e7f/go.mod h1:0R3j0l9DqSw731za6012NzrnFYcmPYM2oUtLq3dQPbM=
github.com/go-kratos/kratos/v2 v2.7.1 h1:PNMUaWxS5ZGDp1EyID5ZosJb1OA/YiHnBxB0yUmocnc=
github.com/go-kratos/kratos/v2 v2.7.1/go.mod h1:CPn82O93OLHjtnbuyOKhAG5TkSvw+mFnL32c4lZFDwU=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=
github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
@ -61,17 +104,32 @@ github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
@ -82,14 +140,56 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=
github.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A=
github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y=
github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@ -105,16 +205,23 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -124,24 +231,41 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
@ -149,14 +273,32 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
@ -166,9 +308,14 @@ github.com/quic-go/quic-go v0.39.1/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
@ -178,18 +325,25 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/synctv-org/vendors v0.1.1-0.20231109155917-3cb85668100d h1:Ase8cgQjugVu+tKWKorEjGP5sBLJ0SNwfp+VDLH7qr0=
github.com/synctv-org/vendors v0.1.1-0.20231109155917-3cb85668100d/go.mod h1:Pe9Fx+mF2V9hQ6vIYKYPNb9eeIeEscRfZgpFG9w9bww=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
@ -198,11 +352,13 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA=
github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zencoder/go-dash/v3 v3.0.3 h1:xqwGJ2fJCSArwONGx6sY26Z1lxQ7zTURoxdRjCpuodM=
github.com/zencoder/go-dash/v3 v3.0.3/go.mod h1:30R5bKy1aUYY45yesjtZ9l8trNc2TwNqbS17WVQmCzk=
github.com/zijiren233/gencontainer v0.0.0-20230930135658-e410015e13cc h1:qEYdClJZG4GHT7pG+scIkN36u5/n1uj5bAPt8UeLkO4=
github.com/zijiren233/gencontainer v0.0.0-20230930135658-e410015e13cc/go.mod h1:V5oL7PrZxgisuLCblFWd89Jg99O8vM1n58llcxZ2hDY=
github.com/zijiren233/gencontainer v0.0.0-20231108115721-8d4bb333a5bc h1:uKhMt523JSG72nIoFb2LZWLRtCH2JGWlKhIWez6ciwY=
github.com/zijiren233/gencontainer v0.0.0-20231108115721-8d4bb333a5bc/go.mod h1:V5oL7PrZxgisuLCblFWd89Jg99O8vM1n58llcxZ2hDY=
github.com/zijiren233/go-colorable v0.0.0-20230930131441-997304c961cb h1:0DyOxf/TbbGodHhOVHNoPk+7v/YBJACs22gKpKlatWw=
github.com/zijiren233/go-colorable v0.0.0-20230930131441-997304c961cb/go.mod h1:6TCzjDiQ8+5gWZiwsC3pnA5M0vUy2jV2Y7ciHJh729g=
github.com/zijiren233/livelib v0.2.3-0.20231103145812-58de2ae7f423 h1:6febr/evRs52lo2lHSpcc6e7+yVPI04ba9eEl26tl+Y=
@ -211,12 +367,27 @@ github.com/zijiren233/stream v0.5.1 h1:9SUwM/fpET6frtBRT5WZBHnan0Hyzkezk/P8N78cg
github.com/zijiren233/stream v0.5.1/go.mod h1:iIrOm3qgIepQFmptD/HDY+YzamSSzQOtPjpVcK7FCOw=
github.com/zijiren233/yaml-comment v0.2.1 h1:/ymMfauuR6zPme+c59FvGNmvxmjOS+BRZSU9YEM82g4=
github.com/zijiren233/yaml-comment v0.2.1/go.mod h1:Cps/tIAJVNoG6TpNN1Aaht/O1QGthZzOgUMQXXTxlBs=
go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k=
go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI=
go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0=
go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U=
go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao=
go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@ -224,14 +395,23 @@ golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
@ -239,21 +419,40 @@ golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -266,6 +465,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@ -277,31 +477,48 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -0,0 +1,12 @@
package bootstrap
import (
"context"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/internal/vendor"
)
func InitVendor(ctx context.Context) error {
return vendor.Init(&conf.Conf.Vendor)
}

@ -25,6 +25,9 @@ type Config struct {
// RateLimit
RateLimit RateLimitConfig `yaml:"rate_limit"`
// Vendor
Vendor VendorConfig `yaml:"vendor"`
}
func (c *Config) Save(file string) error {
@ -53,5 +56,8 @@ func DefaultConfig() *Config {
// RateLimit
RateLimit: DefaultRateLimitConfig(),
// Vendor
Vendor: DefaultVendorConfig(),
}
}

@ -0,0 +1,38 @@
package conf
type VendorConfig struct {
Bilibili Bilibili `yaml:"bilibili"`
}
func DefaultVendorConfig() VendorConfig {
return VendorConfig{
Bilibili: DefaultBilibiliConfig(),
}
}
type Consul struct {
Endpoint string `yaml:"endpoint"`
}
type Etcd struct {
Endpoints []string `yaml:"endpoints"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
type Bilibili struct {
ServerName string `yaml:"server_name" env:"BILIBILI_SERVER_NAME"`
Endpoint string `yaml:"endpoint" env:"BILIBILI_ENDPOINT"`
JwtSecret string `yaml:"jwt_secret" env:"BILIBILI_JWT_SECRET"`
Scheme string `yaml:"scheme" lc:"grpc | http" env:"BILIBILI_SCHEME"`
Consul Consul `yaml:"consul,omitempty"`
Etcd Etcd `yaml:"etcd,omitempty"`
}
func DefaultBilibiliConfig() Bilibili {
return Bilibili{
ServerName: "bilibili",
Scheme: "grpc",
}
}

@ -9,7 +9,7 @@ import (
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/utils"
refreshcache "github.com/synctv-org/synctv/utils/refreshCache"
"github.com/zijiren233/gencontainer/refreshcache"
"github.com/zijiren233/gencontainer/rwmap"
"gorm.io/gorm"
)
@ -153,9 +153,9 @@ type VendorInfo struct {
type BilibiliVendorInfo struct {
Bvid string `json:"bvid,omitempty"`
Cid uint `json:"cid,omitempty"`
Epid uint `json:"epid,omitempty"`
Quality uint `json:"quality,omitempty"`
Cid uint64 `json:"cid,omitempty"`
Epid uint64 `json:"epid,omitempty"`
Quality uint64 `json:"quality,omitempty"`
Cache BilibiliVendorCache `gorm:"-:all" json:"-"`
}

@ -4,7 +4,7 @@ import (
"time"
"github.com/bluele/gcache"
synccache "github.com/synctv-org/synctv/utils/syncCache"
"github.com/zijiren233/gencontainer/synccache"
)
func Init(size int) error {

@ -8,7 +8,7 @@ import (
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/settings"
synccache "github.com/synctv-org/synctv/utils/syncCache"
"github.com/zijiren233/gencontainer/synccache"
"github.com/zijiren233/gencontainer/vec"
)

@ -8,7 +8,7 @@ import (
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/provider"
synccache "github.com/synctv-org/synctv/utils/syncCache"
"github.com/zijiren233/gencontainer/synccache"
)
var userCache gcache.Cache

@ -0,0 +1,456 @@
package vendor
import (
"context"
"errors"
"fmt"
"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"
"github.com/go-kratos/kratos/v2/selector"
"github.com/go-kratos/kratos/v2/selector/wrr"
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() Bilibili {
return bilibiliClient
}
var (
bilibiliClient Bilibili
bilibiliDefaultClient = bilibiliService.NewBilibiliService(nil)
)
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)
}
var (
b = sre.NewBreaker(
sre.WithRequest(25),
sre.WithWindow(time.Second*15),
)
circuitBreaker = kcircuitbreaker.Client(kcircuitbreaker.WithCircuitBreaker(func() circuitbreaker.CircuitBreaker {
return b
}))
)
func InitBilibili(conf *conf.Bilibili) error {
key := []byte(conf.JwtSecret)
selector.SetGlobalSelector(wrr.NewBuilder())
if conf.Endpoint != "" {
switch conf.Scheme {
case "grpc":
con, err := ggrpc.DialInsecure(
context.Background(),
ggrpc.WithEndpoint(conf.Endpoint),
ggrpc.WithMiddleware(
jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
return key, nil
}, jwt.WithSigningMethod(jwtv4.SigningMethodHS256)),
circuitBreaker,
),
)
if err != nil {
return err
}
bilibiliClient = newGrpcBilibili(bilibili.NewBilibiliClient(con))
log.Infof("bilibili client init success with endpoint: %s", conf.Endpoint)
case "http":
cli, err := http.NewClient(
context.Background(),
http.WithEndpoint(conf.Endpoint),
http.WithMiddleware(
jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
return key, nil
}, jwt.WithSigningMethod(jwtv4.SigningMethodHS256)),
circuitBreaker,
),
)
if err != nil {
return err
}
bilibiliClient = newHTTPBilibili(bilibili.NewBilibiliHTTPClient(cli))
default:
return errors.New("unknow bilibili scheme")
}
} else if conf.Consul.Endpoint != "" {
if conf.ServerName == "" {
return errors.New("bilibili server name is empty")
}
c := api.DefaultConfig()
c.Address = conf.Consul.Endpoint
client, err := api.NewClient(c)
if err != nil {
panic(err)
}
endpoint := fmt.Sprintf("discovery:///%s", conf.ServerName)
dis := consul.New(client)
switch conf.Scheme {
case "grpc":
con, err := ggrpc.DialInsecure(
context.Background(),
ggrpc.WithEndpoint(endpoint),
ggrpc.WithDiscovery(dis),
ggrpc.WithMiddleware(
jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
return key, nil
}, jwt.WithSigningMethod(jwtv4.SigningMethodHS256)),
circuitBreaker,
),
)
if err != nil {
return err
}
bilibiliClient = newGrpcBilibili(bilibili.NewBilibiliClient(con))
case "http":
cli, err := http.NewClient(
context.Background(),
http.WithEndpoint(endpoint),
http.WithDiscovery(dis),
http.WithMiddleware(
jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
return key, nil
}, jwt.WithSigningMethod(jwtv4.SigningMethodHS256)),
circuitBreaker,
),
)
if err != nil {
return err
}
bilibiliClient = newHTTPBilibili(bilibili.NewBilibiliHTTPClient(cli))
default:
return errors.New("unknow bilibili scheme")
}
log.Infof("bilibili client init success with consul: %s", conf.Consul.Endpoint)
} else if len(conf.Etcd.Endpoints) > 0 {
if conf.ServerName == "" {
return 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 err
}
dis := etcd.New(cli)
switch conf.Scheme {
case "grpc":
con, err := ggrpc.DialInsecure(
context.Background(),
ggrpc.WithEndpoint(endpoint),
ggrpc.WithDiscovery(dis),
ggrpc.WithMiddleware(
jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
return key, nil
}, jwt.WithSigningMethod(jwtv4.SigningMethodHS256)),
circuitBreaker,
),
)
if err != nil {
return err
}
bilibiliClient = newGrpcBilibili(bilibili.NewBilibiliClient(con))
case "http":
cli, err := http.NewClient(
context.Background(),
http.WithEndpoint(endpoint),
http.WithDiscovery(dis),
http.WithMiddleware(
jwt.Client(func(token *jwtv4.Token) (interface{}, error) {
return key, nil
}, jwt.WithSigningMethod(jwtv4.SigningMethodHS256)),
circuitBreaker,
),
)
if err != nil {
return err
}
bilibiliClient = newHTTPBilibili(bilibili.NewBilibiliHTTPClient(cli))
default:
return errors.New("unknow bilibili scheme")
}
log.Infof("bilibili client init success with etcd: %v", conf.Etcd.Endpoints)
} else {
bilibiliClient = bilibiliDefaultClient
}
return nil
}
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) {
resp, err := g.client.NewQRCode(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.NewQRCode(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) LoginWithQRCode(ctx context.Context, in *bilibili.LoginWithQRCodeReq) (*bilibili.LoginWithQRCodeResp, error) {
resp, err := g.client.LoginWithQRCode(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.LoginWithQRCode(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) NewCaptcha(ctx context.Context, in *bilibili.Empty) (*bilibili.NewCaptchaResp, error) {
resp, err := g.client.NewCaptcha(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.NewCaptcha(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) NewSMS(ctx context.Context, in *bilibili.NewSMSReq) (*bilibili.NewSMSResp, error) {
resp, err := g.client.NewSMS(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.NewSMS(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) LoginWithSMS(ctx context.Context, in *bilibili.LoginWithSMSReq) (*bilibili.LoginWithSMSResp, error) {
resp, err := g.client.LoginWithSMS(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.LoginWithSMS(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) ParseVideoPage(ctx context.Context, in *bilibili.ParseVideoPageReq) (*bilibili.VideoPageInfo, error) {
resp, err := g.client.ParseVideoPage(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.ParseVideoPage(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) GetVideoURL(ctx context.Context, in *bilibili.GetVideoURLReq) (*bilibili.VideoURL, error) {
resp, err := g.client.GetVideoURL(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetVideoURL(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) GetDashVideoURL(ctx context.Context, in *bilibili.GetDashVideoURLReq) (*bilibili.GetDashVideoURLResp, error) {
resp, err := g.client.GetDashVideoURL(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetDashVideoURL(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) GetSubtitles(ctx context.Context, in *bilibili.GetSubtitlesReq) (*bilibili.GetSubtitlesResp, error) {
resp, err := g.client.GetSubtitles(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetSubtitles(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) ParsePGCPage(ctx context.Context, in *bilibili.ParsePGCPageReq) (*bilibili.VideoPageInfo, error) {
resp, err := g.client.ParsePGCPage(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.ParsePGCPage(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) GetPGCURL(ctx context.Context, in *bilibili.GetPGCURLReq) (*bilibili.VideoURL, error) {
resp, err := g.client.GetPGCURL(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetPGCURL(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) GetDashPGCURL(ctx context.Context, in *bilibili.GetDashPGCURLReq) (*bilibili.GetDashPGCURLResp, error) {
resp, err := g.client.GetDashPGCURL(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetDashPGCURL(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) UserInfo(ctx context.Context, in *bilibili.UserInfoReq) (*bilibili.UserInfoResp, error) {
resp, err := g.client.UserInfo(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.UserInfo(ctx, in)
}
return resp, err
}
func (g *grpcBilibili) Match(ctx context.Context, in *bilibili.MatchReq) (*bilibili.MatchResp, error) {
resp, err := g.client.Match(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.Match(ctx, in)
}
return resp, err
}
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) {
resp, err := h.client.NewQRCode(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.NewQRCode(ctx, in)
}
return resp, err
}
func (h *httpBilibili) LoginWithQRCode(ctx context.Context, in *bilibili.LoginWithQRCodeReq) (*bilibili.LoginWithQRCodeResp, error) {
resp, err := h.client.LoginWithQRCode(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.LoginWithQRCode(ctx, in)
}
return resp, err
}
func (h *httpBilibili) NewCaptcha(ctx context.Context, in *bilibili.Empty) (*bilibili.NewCaptchaResp, error) {
resp, err := h.client.NewCaptcha(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.NewCaptcha(ctx, in)
}
return resp, err
}
func (h *httpBilibili) NewSMS(ctx context.Context, in *bilibili.NewSMSReq) (*bilibili.NewSMSResp, error) {
resp, err := h.client.NewSMS(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.NewSMS(ctx, in)
}
return resp, err
}
func (h *httpBilibili) LoginWithSMS(ctx context.Context, in *bilibili.LoginWithSMSReq) (*bilibili.LoginWithSMSResp, error) {
resp, err := h.client.LoginWithSMS(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.LoginWithSMS(ctx, in)
}
return resp, err
}
func (h *httpBilibili) ParseVideoPage(ctx context.Context, in *bilibili.ParseVideoPageReq) (*bilibili.VideoPageInfo, error) {
resp, err := h.client.ParseVideoPage(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.ParseVideoPage(ctx, in)
}
return resp, err
}
func (h *httpBilibili) GetVideoURL(ctx context.Context, in *bilibili.GetVideoURLReq) (*bilibili.VideoURL, error) {
resp, err := h.client.GetVideoURL(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetVideoURL(ctx, in)
}
return resp, err
}
func (h *httpBilibili) GetDashVideoURL(ctx context.Context, in *bilibili.GetDashVideoURLReq) (*bilibili.GetDashVideoURLResp, error) {
resp, err := h.client.GetDashVideoURL(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetDashVideoURL(ctx, in)
}
return resp, err
}
func (h *httpBilibili) GetSubtitles(ctx context.Context, in *bilibili.GetSubtitlesReq) (*bilibili.GetSubtitlesResp, error) {
resp, err := h.client.GetSubtitles(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetSubtitles(ctx, in)
}
return resp, err
}
func (h *httpBilibili) ParsePGCPage(ctx context.Context, in *bilibili.ParsePGCPageReq) (*bilibili.VideoPageInfo, error) {
resp, err := h.client.ParsePGCPage(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.ParsePGCPage(ctx, in)
}
return resp, err
}
func (h *httpBilibili) GetPGCURL(ctx context.Context, in *bilibili.GetPGCURLReq) (*bilibili.VideoURL, error) {
resp, err := h.client.GetPGCURL(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetPGCURL(ctx, in)
}
return resp, err
}
func (h *httpBilibili) GetDashPGCURL(ctx context.Context, in *bilibili.GetDashPGCURLReq) (*bilibili.GetDashPGCURLResp, error) {
resp, err := h.client.GetDashPGCURL(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.GetDashPGCURL(ctx, in)
}
return resp, err
}
func (h *httpBilibili) UserInfo(ctx context.Context, in *bilibili.UserInfoReq) (*bilibili.UserInfoResp, error) {
resp, err := h.client.UserInfo(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.UserInfo(ctx, in)
}
return resp, err
}
func (h *httpBilibili) Match(ctx context.Context, in *bilibili.MatchReq) (*bilibili.MatchResp, error) {
resp, err := h.client.Match(ctx, in)
if err != nil && errors.Is(err, kcircuitbreaker.ErrNotAllowed) {
return bilibiliDefaultClient.Match(ctx, in)
}
return resp, err
}

@ -0,0 +1,15 @@
package vendor
import (
klog "github.com/go-kratos/kratos/v2/log"
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/conf"
)
func Init(conf *conf.VendorConfig) error {
klog.SetLogger(klog.NewStdLogger(log.StandardLogger().Writer()))
if err := InitBilibili(&conf.Bilibili); err != nil {
return err
}
return nil
}

@ -2,6 +2,7 @@ package handlers
import (
"bytes"
"context"
"errors"
"fmt"
"image"
@ -16,18 +17,18 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/internal/db"
dbModel "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/internal/rtmp"
"github.com/synctv-org/synctv/internal/vendor"
pb "github.com/synctv-org/synctv/proto/message"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
refreshcache "github.com/synctv-org/synctv/utils/refreshCache"
"github.com/synctv-org/synctv/vendors/bilibili"
"github.com/synctv-org/vendors/api/bilibili"
"github.com/zencoder/go-dash/v3/mpd"
"github.com/zijiren233/gencontainer/refreshcache"
"github.com/zijiren233/livelib/protocol/hls"
"github.com/zijiren233/livelib/protocol/httpflv"
)
@ -65,7 +66,7 @@ func MovieList(ctx *gin.Context) {
m := room.GetMoviesWithPage(page, max)
current, err := genCurrent(room.Current(), user.ID)
current, err := genCurrent(ctx, room.Current(), user.ID)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -93,9 +94,9 @@ func MovieList(ctx *gin.Context) {
}))
}
func genCurrent(current *op.Current, userID string) (*op.Current, error) {
func genCurrent(ctx context.Context, current *op.Current, userID string) (*op.Current, error) {
if current.Movie.Base.VendorInfo.Vendor != "" {
return current, parse2VendorMovie(userID, &current.Movie)
return current, parse2VendorMovie(ctx, userID, &current.Movie)
}
return current, nil
}
@ -120,7 +121,7 @@ func CurrentMovie(ctx *gin.Context) {
room := ctx.MustGet("room").(*op.Room)
user := ctx.MustGet("user").(*op.User)
current, err := genCurrent(room.Current(), user.ID)
current, err := genCurrent(ctx, room.Current(), user.ID)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -147,7 +148,6 @@ func Movies(ctx *gin.Context) {
mresp := make([]*model.MoviesResp, len(m))
for i, v := range m {
logrus.Info(m[i].Base.Headers)
mresp[i] = &model.MoviesResp{
Id: v.ID,
Base: v.Base,
@ -409,7 +409,7 @@ func ChangeCurrentMovie(ctx *gin.Context) {
return
}
current, err := genCurrent(room.Current(), user.ID)
current, err := genCurrent(ctx, room.Current(), user.ID)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -446,7 +446,7 @@ func ChangeCurrentMovie(ctx *gin.Context) {
if err := room.Broadcast(&op.ElementMessage{
ElementMessage: m,
BeforeSendFunc: func(sendTo *op.User) error {
current, err := genCurrent(room.Current(), sendTo.ID)
current, err := genCurrent(ctx, room.Current(), sendTo.ID)
if err != nil {
return err
}
@ -608,32 +608,48 @@ func JoinLive(ctx *gin.Context) {
}
}
func initBilibiliMPDCache(cookies []*http.Cookie, bvid string, cid, epid uint, roomID, movieID string) *refreshcache.RefreshCache[*dbModel.MPDCache] {
func initBilibiliMPDCache(ctx context.Context, cookies []*http.Cookie, bvid string, cid, epid uint64, roomID, movieID string) *refreshcache.RefreshCache[*dbModel.MPDCache] {
return refreshcache.NewRefreshCache[*dbModel.MPDCache](func() (*dbModel.MPDCache, error) {
cli, err := bilibili.NewClient(cookies)
if err != nil {
return nil, err
}
var m *mpd.MPD
if bvid != "" && cid != 0 {
m, err = cli.GetDashVideoURL(0, bvid, cid)
resp, err := vendor.BilibiliClient().GetDashVideoURL(ctx, &bilibili.GetDashVideoURLReq{
Cookies: utils.HttpCookieToMap(cookies),
Bvid: bvid,
Cid: cid,
})
if err != nil {
return nil, err
}
m, err = mpd.ReadFromString(resp.Mpd)
if err != nil {
return nil, err
}
} else if epid != 0 {
m, err = cli.GetDashPGCURL(epid, 0)
resp, err := vendor.BilibiliClient().GetDashPGCURL(ctx, &bilibili.GetDashPGCURLReq{
Cookies: utils.HttpCookieToMap(cookies),
Epid: epid,
})
if err != nil {
return nil, err
}
m, err = mpd.ReadFromString(resp.Mpd)
if err != nil {
return nil, err
}
} else {
return nil, errors.New("bvid and epid are empty")
}
if err != nil {
return nil, err
}
m.BaseURL = append(m.BaseURL, fmt.Sprintf("/api/movie/proxy/%s/", roomID))
id := 0
movies := []string{}
for _, as := range m.GetCurrentPeriod().AdaptationSets {
for _, r := range as.Representations {
for i := range r.BaseURL {
movies = append(movies, r.BaseURL[i])
r.BaseURL[i] = fmt.Sprintf("%s?id=%d", movieID, id)
id++
for _, p := range m.Periods {
for _, as := range p.AdaptationSets {
for _, r := range as.Representations {
for i := range r.BaseURL {
movies = append(movies, r.BaseURL[i])
r.BaseURL[i] = fmt.Sprintf("%s?id=%d", movieID, id)
id++
}
}
}
}
@ -648,24 +664,32 @@ func initBilibiliMPDCache(cookies []*http.Cookie, bvid string, cid, epid uint, r
}, time.Minute*119)
}
func initBilibiliShareCache(cookies []*http.Cookie, bvid string, cid, epid uint) *refreshcache.RefreshCache[string] {
func initBilibiliShareCache(ctx context.Context, cookies []*http.Cookie, bvid string, cid, epid uint64) *refreshcache.RefreshCache[string] {
return refreshcache.NewRefreshCache[string](func() (string, error) {
cli, err := bilibili.NewClient(cookies)
if err != nil {
return "", err
}
var mu *bilibili.VideoURL
var u string
if bvid != "" {
mu, err = cli.GetVideoURL(0, bvid, cid)
resp, err := vendor.BilibiliClient().GetVideoURL(ctx, &bilibili.GetVideoURLReq{
Cookies: utils.HttpCookieToMap(cookies),
Bvid: bvid,
Cid: cid,
})
if err != nil {
return "", err
}
u = resp.Url
} else if epid != 0 {
mu, err = cli.GetPGCURL(epid, 0)
resp, err := vendor.BilibiliClient().GetPGCURL(ctx, &bilibili.GetPGCURLReq{
Cookies: utils.HttpCookieToMap(cookies),
Epid: epid,
})
if err != nil {
return "", err
}
u = resp.Url
} else {
err = errors.New("bvid and epid are empty")
}
if err != nil {
return "", err
return "", errors.New("bvid and epid are empty")
}
return mu.URL, nil
return u, nil
}, time.Minute*119)
}
@ -677,7 +701,7 @@ func proxyVendorMovie(ctx *gin.Context, movie *dbModel.Movie) {
if err != nil {
return nil, err
}
return initBilibiliMPDCache(vendor.Cookies, info.Bvid, info.Cid, info.Epid, movie.RoomID, movie.ID), nil
return initBilibiliMPDCache(ctx, vendor.Cookies, info.Bvid, info.Cid, info.Epid, movie.RoomID, movie.ID), nil
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
@ -712,7 +736,7 @@ func proxyVendorMovie(ctx *gin.Context, movie *dbModel.Movie) {
}
}
func parse2VendorMovie(userID string, movie *dbModel.Movie) (err error) {
func parse2VendorMovie(ctx context.Context, userID string, movie *dbModel.Movie) (err error) {
if movie.Base.VendorInfo.Shared {
userID = movie.CreatorID
}
@ -727,7 +751,7 @@ func parse2VendorMovie(userID string, movie *dbModel.Movie) (err error) {
if err != nil {
return nil, err
}
return initBilibiliShareCache(vendor.Cookies, info.Bvid, info.Cid, info.Epid), nil
return initBilibiliShareCache(ctx, vendor.Cookies, info.Bvid, info.Cid, info.Epid), nil
})
if err != nil {
return err

@ -14,7 +14,7 @@ import (
"github.com/synctv-org/synctv/server/middlewares"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
refreshcache "github.com/synctv-org/synctv/utils/refreshCache"
"github.com/zijiren233/gencontainer/refreshcache"
"gorm.io/gorm"
)

@ -7,11 +7,14 @@ import (
"github.com/gin-gonic/gin"
json "github.com/json-iterator/go"
"github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/db"
dbModel "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/internal/vendor"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/vendors/bilibili"
"github.com/synctv-org/synctv/utils"
"github.com/synctv-org/vendors/api/bilibili"
)
type ParseReq struct {
@ -38,67 +41,79 @@ func Parse(ctx *gin.Context) {
return
}
matchType, id, err := bilibili.Match(req.URL)
resp, err := vendor.BilibiliClient().Match(ctx, &bilibili.MatchReq{
Url: req.URL,
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
vendor, err := db.FirstOrCreateVendorByUserIDAndVendor(user.ID, dbModel.StreamingVendorBilibili)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
cli, err := bilibili.NewClient(vendor.Cookies)
v, err := db.FirstOrCreateVendorByUserIDAndVendor(user.ID, dbModel.StreamingVendorBilibili)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
switch matchType {
switch resp.Type {
case "bv":
mpis, err := cli.ParseVideoPage(0, id, bilibili.WithGetSections(ctx.DefaultQuery("sections", "false") == "true"))
resp, err := vendor.BilibiliClient().ParseVideoPage(ctx, &bilibili.ParseVideoPageReq{
Cookies: utils.HttpCookieToMap(v.Cookies),
Bvid: resp.Id,
Sections: ctx.DefaultQuery("sections", "false") == "true",
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(mpis))
ctx.JSON(http.StatusOK, model.NewApiDataResp(resp))
case "av":
aid, err := strconv.Atoi(id)
aid, err := strconv.ParseUint(resp.Id, 10, 64)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
mpis, err := cli.ParseVideoPage(uint(aid), "", bilibili.WithGetSections(ctx.DefaultQuery("sections", "false") == "true"))
resp, err := vendor.BilibiliClient().ParseVideoPage(ctx, &bilibili.ParseVideoPageReq{
Cookies: utils.HttpCookieToMap(v.Cookies),
Aid: aid,
Sections: ctx.DefaultQuery("sections", "false") == "true",
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(mpis))
ctx.JSON(http.StatusOK, model.NewApiDataResp(resp))
case "ep":
epId, err := strconv.Atoi(id)
epid, err := strconv.ParseUint(resp.Id, 10, 64)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
mpis, err := cli.ParsePGCPage(uint(epId), 0)
resp, err := vendor.BilibiliClient().ParsePGCPage(ctx, &bilibili.ParsePGCPageReq{
Cookies: utils.HttpCookieToMap(v.Cookies),
Epid: epid,
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(mpis))
logrus.Info(resp)
ctx.JSON(http.StatusOK, model.NewApiDataResp(resp))
case "ss":
seasonId, err := strconv.Atoi(id)
ssid, err := strconv.ParseUint(resp.Id, 10, 64)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
mpis, err := cli.ParsePGCPage(0, uint(seasonId))
resp, err := vendor.BilibiliClient().ParsePGCPage(ctx, &bilibili.ParsePGCPageReq{
Cookies: utils.HttpCookieToMap(v.Cookies),
Ssid: ssid,
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(mpis))
ctx.JSON(http.StatusOK, model.NewApiDataResp(resp))
default:
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp("unknown match type"))
return

@ -9,12 +9,14 @@ import (
"github.com/synctv-org/synctv/internal/db"
dbModel "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/internal/vendor"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/vendors/bilibili"
"github.com/synctv-org/synctv/utils"
"github.com/synctv-org/vendors/api/bilibili"
)
func NewQRCode(ctx *gin.Context) {
r, err := bilibili.NewQRCode(ctx)
r, err := vendor.BilibiliClient().NewQRCode(ctx, &bilibili.Empty{})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -46,40 +48,47 @@ func LoginWithQR(ctx *gin.Context) {
return
}
cookie, err := bilibili.LoginWithQRCode(ctx, req.Key)
resp, err := vendor.BilibiliClient().LoginWithQRCode(ctx, &bilibili.LoginWithQRCodeReq{
Key: req.Key,
})
if err != nil {
switch err {
case bilibili.ErrQRCodeExpired:
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": "expired",
}))
case bilibili.ErrQRCodeScanned:
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": "scanned",
}))
case bilibili.ErrQRCodeNotScanned:
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": "notScanned",
}))
default:
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
}
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
_, err = db.AssignFirstOrCreateVendorByUserIDAndVendor(user.ID, dbModel.StreamingVendorBilibili, db.WithCookie([]*http.Cookie{cookie}))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
switch resp.Status {
case bilibili.QRCodeStatus_EXPIRED:
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": "expired",
}))
return
case bilibili.QRCodeStatus_SCANNED:
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": "scanned",
}))
return
case bilibili.QRCodeStatus_NOTSCANNED:
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": "notScanned",
}))
return
case bilibili.QRCodeStatus_SUCCESS:
_, err = db.AssignFirstOrCreateVendorByUserIDAndVendor(user.ID, dbModel.StreamingVendorBilibili, db.WithCookie(utils.MapToHttpCookie(resp.Cookies)))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": "success",
}))
default:
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp("unknown status"))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": "success",
}))
}
func NewCaptcha(ctx *gin.Context) {
r, err := bilibili.NewCaptcha(ctx)
r, err := vendor.BilibiliClient().NewCaptcha(ctx, &bilibili.Empty{})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
@ -120,13 +129,19 @@ func NewSMS(ctx *gin.Context) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
r, err := bilibili.NewSMS(ctx, req.Telephone, req.Token, req.Challenge, req.Validate_)
r, err := vendor.BilibiliClient().NewSMS(ctx, &bilibili.NewSMSReq{
Phone: req.Telephone,
Token: req.Token,
Challenge: req.Challenge,
Validate: req.Validate_,
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"captchaKey": r,
"captchaKey": r.CaptchaKey,
}))
}
@ -159,13 +174,18 @@ func LoginWithSMS(ctx *gin.Context) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
c, err := bilibili.LoginWithSMS(ctx, req.Telephone, req.Code, req.CaptchaKey)
c, err := vendor.BilibiliClient().LoginWithSMS(ctx, &bilibili.LoginWithSMSReq{
Phone: req.Telephone,
CaptchaKey: req.CaptchaKey,
Code: req.Code,
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
user := ctx.MustGet("user").(*op.User)
_, err = db.AssignFirstOrCreateVendorByUserIDAndVendor(user.ID, dbModel.StreamingVendorBilibili, db.WithCookie([]*http.Cookie{c}))
_, err = db.AssignFirstOrCreateVendorByUserIDAndVendor(user.ID, dbModel.StreamingVendorBilibili, db.WithCookie(utils.MapToHttpCookie(c.Cookies)))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return

@ -7,45 +7,32 @@ import (
"github.com/synctv-org/synctv/internal/db"
dbModel "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/internal/vendor"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/vendors/bilibili"
"github.com/synctv-org/synctv/utils"
"github.com/synctv-org/vendors/api/bilibili"
)
type MeResp struct {
IsLogin bool `json:"isLogin"`
Username string `json:"username"`
Face string `json:"face"`
IsVip bool `json:"isVip"`
}
func Me(ctx *gin.Context) {
user := ctx.MustGet("user").(*op.User)
vendor, err := db.FirstOrCreateVendorByUserIDAndVendor(user.ID, dbModel.StreamingVendorBilibili)
v, err := db.FirstOrCreateVendorByUserIDAndVendor(user.ID, dbModel.StreamingVendorBilibili)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
if len(vendor.Cookies) == 0 {
ctx.JSON(http.StatusOK, model.NewApiDataResp(&MeResp{
if len(v.Cookies) == 0 {
ctx.JSON(http.StatusOK, model.NewApiDataResp(&bilibili.UserInfoResp{
IsLogin: false,
}))
return
}
cli, err := bilibili.NewClient(vendor.Cookies)
resp, err := vendor.BilibiliClient().UserInfo(ctx, &bilibili.UserInfoReq{
Cookies: utils.HttpCookieToMap(v.Cookies),
})
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
nav, err := cli.UserInfo()
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(&MeResp{
IsLogin: nav.Data.IsLogin,
Username: nav.Data.Uname,
Face: nav.Data.Face,
IsVip: nav.Data.VipStatus == 1,
}))
ctx.JSON(http.StatusOK, model.NewApiDataResp(resp))
}

@ -6,7 +6,7 @@ import (
"time"
"github.com/gin-gonic/gin"
synccache "github.com/synctv-org/synctv/utils/syncCache"
"github.com/zijiren233/gencontainer/synccache"
)
//go:embed templates/*.html

@ -1,58 +0,0 @@
package refreshcache
import (
"sync"
"sync/atomic"
"time"
)
type RefreshCache[T any] struct {
lock sync.Mutex
last int64
maxAge int64
refreshFunc func() (T, error)
data atomic.Pointer[T]
}
func NewRefreshCache[T any](refreshFunc func() (T, error), maxAge time.Duration) *RefreshCache[T] {
if refreshFunc == nil {
panic("refreshFunc cannot be nil")
}
if maxAge <= 0 {
panic("maxAge must be positive")
}
return &RefreshCache[T]{
refreshFunc: refreshFunc,
maxAge: int64(maxAge),
}
}
func (r *RefreshCache[T]) Get() (data T, err error) {
if time.Now().UnixNano()-atomic.LoadInt64(&r.last) < r.maxAge {
return *r.data.Load(), nil
}
r.lock.Lock()
defer r.lock.Unlock()
if time.Now().UnixNano()-r.last < r.maxAge {
return *r.data.Load(), nil
}
defer func() {
if err == nil {
r.data.Store(&data)
atomic.StoreInt64(&r.last, time.Now().UnixNano())
}
}()
return r.refreshFunc()
}
func (r *RefreshCache[T]) Refresh() (data T, err error) {
r.lock.Lock()
defer r.lock.Unlock()
defer func() {
if err == nil {
r.data.Store(&data)
atomic.StoreInt64(&r.last, time.Now().UnixNano())
}
}()
return r.refreshFunc()
}

@ -1,101 +0,0 @@
package synccache
import (
"time"
"github.com/zijiren233/gencontainer/rwmap"
)
type SyncCache[K comparable, V any] struct {
cache rwmap.RWMap[K, *Entry[V]]
deletedCallback func(v V)
ticker *time.Ticker
}
type SyncCacheConfig[K comparable, V any] func(sc *SyncCache[K, V])
func WithDeletedCallback[K comparable, V any](callback func(v V)) SyncCacheConfig[K, V] {
return func(sc *SyncCache[K, V]) {
sc.deletedCallback = callback
}
}
func NewSyncCache[K comparable, V any](trimTime time.Duration, conf ...SyncCacheConfig[K, V]) *SyncCache[K, V] {
sc := &SyncCache[K, V]{
ticker: time.NewTicker(trimTime),
}
for _, c := range conf {
c(sc)
}
go func() {
for range sc.ticker.C {
sc.trim()
}
}()
return sc
}
func (sc *SyncCache[K, V]) trim() {
sc.cache.Range(func(key K, value *Entry[V]) bool {
if value.IsExpired() {
sc.CompareAndDelete(key, value)
}
return true
})
}
func (sc *SyncCache[K, V]) Store(key K, value V, expire time.Duration) {
sc.LoadOrStore(key, value, expire)
}
func (sc *SyncCache[K, V]) Load(key K) (value *Entry[V], loaded bool) {
e, ok := sc.cache.Load(key)
if ok && !e.IsExpired() {
return e, ok
}
sc.CompareAndDelete(key, e)
return
}
func (sc *SyncCache[K, V]) LoadOrStore(key K, value V, expire time.Duration) (actual *Entry[V], loaded bool) {
e, loaded := sc.cache.LoadOrStore(key, NewEntry[V](value, expire))
if loaded && e.IsExpired() {
sc.CompareAndDelete(key, e)
return sc.LoadOrStore(key, value, expire)
}
return e, loaded
}
func (sc *SyncCache[K, V]) Delete(key K) {
sc.LoadAndDelete(key)
}
func (sc *SyncCache[K, V]) LoadAndDelete(key K) (value *Entry[V], loaded bool) {
e, loaded := sc.cache.LoadAndDelete(key)
if loaded && !e.IsExpired() {
return e, loaded
}
return
}
func (sc *SyncCache[K, V]) CompareAndDelete(key K, oldEntry *Entry[V]) (success bool) {
b := sc.cache.CompareAndDelete(key, oldEntry)
if b && sc.deletedCallback != nil {
sc.deletedCallback(oldEntry.value)
}
return b
}
func (sc *SyncCache[K, V]) Clear() {
sc.cache.Clear()
}
func (sc *SyncCache[K, V]) Range(f func(key K, value *Entry[V]) bool) {
sc.cache.Range(func(key K, value *Entry[V]) bool {
if !value.IsExpired() {
return f(key, value)
}
sc.cache.CompareAndDelete(key, value)
return true
})
}

@ -1,34 +0,0 @@
package synccache
import (
"sync/atomic"
"time"
)
type Entry[V any] struct {
expiration int64
value V
}
func NewEntry[V any](value V, expire time.Duration) *Entry[V] {
return &Entry[V]{
expiration: time.Now().Add(expire).UnixMilli(),
value: value,
}
}
func (e *Entry[V]) Value() V {
return e.value
}
func (e *Entry[V]) IsExpired() bool {
return time.Now().After(time.UnixMilli(atomic.LoadInt64(&e.expiration)))
}
func (e *Entry[V]) AddExpiration(d time.Duration) {
atomic.AddInt64(&e.expiration, int64(d))
}
func (e *Entry[V]) SetExpiration(t time.Time) {
atomic.StoreInt64(&e.expiration, t.UnixMilli())
}

@ -279,3 +279,22 @@ func SortUUID() string {
hex.Encode(dst, src[:])
return stream.BytesToString(dst)
}
func HttpCookieToMap(c []*http.Cookie) map[string]string {
m := make(map[string]string)
for _, v := range c {
m[v.Name] = v.Value
}
return m
}
func MapToHttpCookie(m map[string]string) []*http.Cookie {
var c []*http.Cookie
for k, v := range m {
c = append(c, &http.Cookie{
Name: k,
Value: v,
})
}
return c
}

File diff suppressed because it is too large Load Diff

@ -1,123 +0,0 @@
package bilibili_test
import (
"fmt"
"testing"
"github.com/synctv-org/synctv/vendors/bilibili"
)
func TestMatch(t *testing.T) {
t.Parallel()
tests := []struct {
name string
url string
wantT string
wantID string
wantErr bool
}{
{
name: "bv",
url: "https://www.bilibili.com/video/BV1i5411y7fB",
wantT: "bv",
wantID: "BV1i5411y7fB",
wantErr: false,
},
{
name: "av",
url: "https://www.bilibili.com/video/av1",
wantT: "av",
wantID: "1",
wantErr: false,
},
{
name: "ss",
url: "https://www.bilibili.com/bangumi/play/ss1",
wantT: "ss",
wantID: "1",
wantErr: false,
},
{
name: "ep",
url: "https://www.bilibili.com/bangumi/play/ep1",
wantT: "ep",
wantID: "1",
wantErr: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
gotT, gotID, err := bilibili.Match(tt.url)
if (err != nil) != tt.wantErr {
t.Errorf("Match() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotT != tt.wantT {
t.Errorf("Match() gotT = %v, want %v", gotT, tt.wantT)
}
if gotID != tt.wantID {
t.Errorf("Match() gotID = %v, want %v", gotID, tt.wantID)
}
})
}
}
func TestGetDashVideoURL(t *testing.T) {
c, err := bilibili.NewClient(nil)
if err != nil {
t.Fatal(err)
}
m, err := c.GetDashVideoURL(0, "BV1y7411Q7Eq", 171776208)
if err != nil {
t.Fatal(err)
}
for _, as := range m.GetCurrentPeriod().AdaptationSets {
for _, r := range as.Representations {
t.Log(r.BaseURL)
}
}
}
func TestGetDashVideoURLMPDFile(t *testing.T) {
c, err := bilibili.NewClient(nil)
if err != nil {
t.Fatal(err)
}
m, err := c.GetDashVideoURL(0, "BV1y7411Q7Eq", 171776208)
if err != nil {
t.Fatal(err)
}
s, err := m.WriteToString()
if err != nil {
t.Fatal(err)
}
t.Log(s)
}
func TestEditAndGetDashVideoURLMPDFile(t *testing.T) {
c, err := bilibili.NewClient(nil)
if err != nil {
t.Fatal(err)
}
m, err := c.GetDashVideoURL(0, "BV1y7411Q7Eq", 171776208)
if err != nil {
t.Fatal(err)
}
m.BaseURL = append(m.BaseURL, "/")
id := 0
for _, as := range m.GetCurrentPeriod().AdaptationSets {
for _, r := range as.Representations {
for i := range r.BaseURL {
r.BaseURL[i] = fmt.Sprintf("/api/movie/proxy/roomid/movieid?id=%d", id)
id++
}
}
}
s, err := m.WriteToString()
if err != nil {
t.Fatal(err)
}
t.Log(s)
}

@ -1,75 +0,0 @@
package bilibili
import (
"errors"
"net/http"
"time"
json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/utils"
refreshcache "github.com/synctv-org/synctv/utils/refreshCache"
)
type buvid struct {
b3, b4 string
}
var buvidCache = refreshcache.NewRefreshCache[buvid](func() (buvid, error) {
b3, b4, err := newBuvid()
if err != nil {
return buvid{}, err
}
return buvid{
b3: b3,
b4: b4,
}, nil
}, time.Hour)
func getBuvidCookies() ([]*http.Cookie, error) {
buvid, err := buvidCache.Get()
if err != nil {
return nil, err
}
return []*http.Cookie{
{
Name: "buvid3",
Value: buvid.b3,
},
{
Name: "buvid4",
Value: buvid.b4,
},
}, nil
}
type spiResp struct {
Code int `json:"code"`
Data struct {
B3 string `json:"b_3"`
B4 string `json:"b_4"`
} `json:"data"`
Message string `json:"message"`
}
func newBuvid() (string, string, error) {
r, err := http.NewRequest(http.MethodGet, "https://api.bilibili.com/x/frontend/finger/spi", nil)
if err != nil {
return "", "", err
}
r.Header.Set("User-Agent", utils.UA)
r.Header.Set("Referer", "https://www.bilibili.com")
resp, err := http.DefaultClient.Do(r)
if err != nil {
return "", "", err
}
defer resp.Body.Close()
var b spiResp
err = json.NewDecoder(resp.Body).Decode(&b)
if err != nil {
return "", "", err
}
if b.Code != 0 {
return "", "", errors.New(b.Message)
}
return b.Data.B3, b.Data.B4, nil
}

@ -1,95 +0,0 @@
package bilibili
import (
"context"
"io"
"net/http"
"github.com/synctv-org/synctv/utils"
)
type Client struct {
httpClient *http.Client
cookies []*http.Cookie
buvid []*http.Cookie
ctx context.Context
}
type ClientConfig func(*Client)
func WithHttpClient(httpClient *http.Client) ClientConfig {
return func(c *Client) {
c.httpClient = httpClient
}
}
func WithContext(ctx context.Context) ClientConfig {
return func(c *Client) {
c.ctx = ctx
}
}
func NewClient(cookies []*http.Cookie, conf ...ClientConfig) (*Client, error) {
b, err := getBuvidCookies()
if err != nil {
return nil, err
}
cli := &Client{
httpClient: http.DefaultClient,
cookies: cookies,
ctx: context.Background(),
buvid: b,
}
for _, v := range conf {
v(cli)
}
return cli, nil
}
func (c *Client) SetCookies(cookies []*http.Cookie) {
c.cookies = cookies
}
type RequestConfig struct {
wbi bool
}
func defaultRequestConfig() *RequestConfig {
return &RequestConfig{
wbi: true,
}
}
type RequestOption func(*RequestConfig)
func WithoutWbi() RequestOption {
return func(c *RequestConfig) {
c.wbi = false
}
}
func (c *Client) NewRequest(method, url string, body io.Reader, conf ...RequestOption) (req *http.Request, err error) {
config := defaultRequestConfig()
for _, v := range conf {
v(config)
}
if config.wbi {
url, err = signAndGenerateURL(url)
if err != nil {
return nil, err
}
}
req, err = http.NewRequestWithContext(c.ctx, method, url, body)
if err != nil {
return nil, err
}
for _, cookie := range c.buvid {
req.AddCookie(cookie)
}
for _, cookie := range c.cookies {
req.AddCookie(cookie)
}
req.Header.Set("User-Agent", utils.UA)
req.Header.Set("Referer", "https://www.bilibili.com")
return req, nil
}

@ -1,263 +0,0 @@
package bilibili
import (
"context"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/utils"
)
type RQCode struct {
URL string `json:"url"`
Key string `json:"key"`
}
func NewQRCode(ctx context.Context) (*RQCode, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://passport.bilibili.com/x/passport-login/web/qrcode/generate", nil)
if err != nil {
return nil, err
}
req.Header.Set("Referer", "https://passport.bilibili.com/login")
req.Header.Set("User-Agent", utils.UA)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
qr := qrcodeResp{}
err = json.NewDecoder(resp.Body).Decode(&qr)
if err != nil {
return nil, err
}
if qr.Code != 0 {
return nil, errors.New(qr.Message)
}
return &RQCode{
URL: qr.Data.URL,
Key: qr.Data.QrcodeKey,
}, nil
}
type loginQRResp struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int `json:"ttl"`
Data struct {
URL string `json:"url"`
RefreshToken string `json:"refresh_token"`
Timestamp int `json:"timestamp"`
Code uint `json:"code"`
Message string `json:"message"`
} `json:"data"`
}
const (
qrStatusSuccess uint = 0
qrStatusExpired uint = 86038
qrStatusScanned uint = 86090
qrStatusNotScanned uint = 86101
)
var (
ErrQRCodeExpired = errors.New("qr code expired")
ErrQRCodeScanned = errors.New("qr code scanned")
ErrQRCodeNotScanned = errors.New("qr code not scanned")
)
func LoginWithQRCode(ctx context.Context, key string) (*http.Cookie, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=%s", key), nil)
if err != nil {
return nil, err
}
req.Header.Set("Referer", "https://passport.bilibili.com/login")
req.Header.Set("User-Agent", utils.UA)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var loginQRResp loginQRResp
err = json.NewDecoder(resp.Body).Decode(&loginQRResp)
if err != nil {
return nil, err
}
if loginQRResp.Code != 0 {
return nil, errors.New(loginQRResp.Message)
}
switch loginQRResp.Data.Code {
case qrStatusSuccess:
for _, cookie := range resp.Cookies() {
if cookie.Name == "SESSDATA" {
return cookie, nil
}
}
return nil, fmt.Errorf("no cookie")
case qrStatusExpired:
return nil, ErrQRCodeExpired
case qrStatusScanned:
return nil, ErrQRCodeScanned
case qrStatusNotScanned:
return nil, ErrQRCodeNotScanned
default:
return nil, fmt.Errorf("unknown qr code status: %d", loginQRResp.Data.Code)
}
}
type CaptchaResp struct {
Token string `json:"token"`
Gt string `json:"gt"`
Challenge string `json:"challenge"`
}
func NewCaptcha(ctx context.Context) (*CaptchaResp, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://passport.bilibili.com/x/passport-login/captcha", nil)
if err != nil {
return nil, err
}
req.Header.Set("Referer", "https://passport.bilibili.com/login")
req.Header.Set("User-Agent", utils.UA)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
var captcha captcha
err = json.NewDecoder(resp.Body).Decode(&captcha)
if err != nil {
return nil, err
}
if captcha.Code != 0 {
return nil, errors.New(captcha.Message)
}
return &CaptchaResp{
Token: captcha.Data.Token,
Gt: captcha.Data.Geetest.Gt,
Challenge: captcha.Data.Geetest.Challenge,
}, nil
}
type captcha struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int `json:"ttl"`
Data struct {
Type string `json:"type"`
Token string `json:"token"`
Geetest struct {
Challenge string `json:"challenge"`
Gt string `json:"gt"`
} `json:"geetest"`
Tencent struct {
Appid string `json:"appid"`
} `json:"tencent"`
} `json:"data"`
}
type sms struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
CaptchaKey string `json:"captcha_key"`
} `json:"data"`
}
const (
smsStatusSuccess int = 0
smsStatusBadReq int = -400
smsStatusTelFormatErr int = 1002
smsStatusRateLimit int = 86203
smsStatusSended int = 1003
smsStatusBanned int = 1025
smsStatusTokenErr int = 2400
smsStatusGeetestErr int = 2406
)
func NewSMS(ctx context.Context, tel, token, challenge, validate string) (captchaKey string, err error) {
b, err := getBuvidCookies()
if err != nil {
return "", err
}
data := url.Values{}
data.Set("cid", "86")
data.Set("tel", tel)
data.Set("source", "main-fe-header")
data.Set("token", token)
data.Set("challenge", challenge)
data.Set("validate", validate)
data.Set("seccode", fmt.Sprintf("%s|jordan", validate))
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://passport.bilibili.com/x/passport-login/web/sms/send", strings.NewReader(data.Encode()))
if err != nil {
return "", err
}
req.Header.Set("Referer", "https://passport.bilibili.com/login")
req.Header.Set("User-Agent", utils.UA)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
for _, c := range b {
req.AddCookie(c)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
var sms sms
err = json.NewDecoder(resp.Body).Decode(&sms)
if err != nil {
return "", err
}
if sms.Code != smsStatusSuccess {
return "", errors.New(sms.Message)
}
return sms.Data.CaptchaKey, nil
}
type loginSMSResp struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
IsNew bool `json:"is_new"`
Status int `json:"status"`
URL string `json:"url"`
} `json:"data"`
}
func LoginWithSMS(ctx context.Context, tel, code, captchaKey string) (*http.Cookie, error) {
data := url.Values{}
data.Set("cid", "86")
data.Set("tel", tel)
data.Set("code", code)
data.Set("source", "main-fe-header")
data.Set("captcha_key", captchaKey)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://passport.bilibili.com/x/passport-login/web/login/sms", strings.NewReader(data.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Referer", "https://passport.bilibili.com/login")
req.Header.Set("User-Agent", utils.UA)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
var loginSMSResp loginSMSResp
err = json.NewDecoder(resp.Body).Decode(&loginSMSResp)
if err != nil {
return nil, err
}
if loginSMSResp.Code != 0 {
return nil, errors.New(loginSMSResp.Message)
}
for _, cookie := range resp.Cookies() {
if cookie.Name == "SESSDATA" {
return cookie, nil
}
}
return nil, fmt.Errorf("no cookie")
}

@ -1,557 +0,0 @@
package bilibili
import (
"errors"
"fmt"
"net/http"
"time"
json "github.com/json-iterator/go"
"github.com/zencoder/go-dash/v3/mpd"
)
type VideoPageInfo struct {
Title string `json:"title"`
Actors string `json:"actors"`
VideoInfos []*VideoInfo `json:"videoInfos"`
}
type VideoInfo struct {
Bvid string `json:"bvid,omitempty"`
Cid uint `json:"cid,omitempty"`
Epid uint `json:"epid,omitempty"`
Name string `json:"name"`
CoverImage string `json:"coverImage"`
}
type ParseVideoPageConf struct {
GetSections bool
}
type ParseVideoPageConfig func(*ParseVideoPageConf)
func WithGetSections(GetSections bool) ParseVideoPageConfig {
return func(c *ParseVideoPageConf) {
c.GetSections = GetSections
}
}
func (c *Client) ParseVideoPage(aid uint, bvid string, conf ...ParseVideoPageConfig) (*VideoPageInfo, error) {
config := &ParseVideoPageConf{}
for _, v := range conf {
v(config)
}
var url string
if aid != 0 {
url = fmt.Sprintf("https://api.bilibili.com/x/web-interface/view?aid=%d", aid)
} else if bvid != "" {
url = fmt.Sprintf("https://api.bilibili.com/x/web-interface/view?bvid=%s", bvid)
} else {
return nil, fmt.Errorf("aid and bvid are both empty")
}
req, err := c.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
info := videoPageInfo{}
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return nil, err
}
if info.Code != 0 {
return nil, errors.New(info.Message)
}
r := &VideoPageInfo{
Title: info.Data.Title,
Actors: info.Data.Owner.Name,
}
if config.GetSections && len(info.Data.UgcSeason.Sections) != 0 {
r.Title = info.Data.UgcSeason.Title
for _, v := range info.Data.UgcSeason.Sections {
for _, episode := range v.Episodes {
r.VideoInfos = append(r.VideoInfos, &VideoInfo{
Bvid: episode.Bvid,
Cid: episode.Cid,
Name: episode.Title,
CoverImage: episode.Arc.Pic,
})
}
}
} else {
r.VideoInfos = make([]*VideoInfo, len(info.Data.Pages))
if len(info.Data.Pages) == 1 {
info.Data.Pages[0].Part = info.Data.Title
}
for i, page := range info.Data.Pages {
r.VideoInfos[i] = &VideoInfo{
Bvid: info.Data.Bvid,
Cid: page.Cid,
Name: page.Part,
CoverImage: info.Data.Pic,
}
}
}
return r, nil
}
const (
Q240P uint = 6
Q360P uint = 16
Q480P uint = 32
Q720P uint = 64
Q1080P uint = 80
Q1080PP uint = 112
Q1080P60 uint = 116
Q4K uint = 120
QHDR uint = 124
QDOLBY uint = 126
Q8K uint = 127
)
type VideoURL struct {
AcceptDescription []string `json:"acceptDescription"`
AcceptQuality []uint `json:"acceptQuality"`
CurrentQuality uint `json:"currentQuality"`
URL string `json:"url"`
}
type GetVideoURLConf struct {
Quality uint
}
func (c *GetVideoURLConf) fix() {
if c.Quality == 0 {
c.Quality = Q1080PP
}
}
type GetVideoURLConfig func(*GetVideoURLConf)
func WithQuality(q uint) GetVideoURLConfig {
return func(c *GetVideoURLConf) {
c.Quality = q
}
}
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/videostream_url.md
func (c *Client) GetVideoURL(aid uint, bvid string, cid uint, conf ...GetVideoURLConfig) (*VideoURL, error) {
config := &GetVideoURLConf{
Quality: Q1080PP,
}
for _, v := range conf {
v(config)
}
config.fix()
var url string
if aid != 0 {
url = fmt.Sprintf("https://api.bilibili.com/x/player/wbi/playurl?aid=%d&cid=%d&qn=%d&platform=html5&high_quality=1", aid, cid, config.Quality)
} else if bvid != "" {
url = fmt.Sprintf("https://api.bilibili.com/x/player/wbi/playurl?bvid=%s&cid=%d&qn=%d&platform=html5&high_quality=1", bvid, cid, config.Quality)
} else {
return nil, fmt.Errorf("aid and bvid are both empty")
}
req, err := c.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
info := videoInfo{}
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return nil, err
}
if info.Code != 0 {
return nil, errors.New(info.Message)
}
return &VideoURL{
AcceptDescription: info.Data.AcceptDescription,
AcceptQuality: info.Data.AcceptQuality,
CurrentQuality: info.Data.Quality,
URL: info.Data.Durl[0].URL,
}, nil
}
type GetDashVideoURLConf struct {
HDR bool
Need4K bool
NeedDOLBY bool
NeedDOLBYAudio bool
Need8K bool
NeedAV1 bool
}
type GetDashVideoURLConfig func(*GetDashVideoURLConf)
func WithHDR(hdr bool) GetDashVideoURLConfig {
return func(c *GetDashVideoURLConf) {
c.HDR = hdr
}
}
func WithNeed4K(need4k bool) GetDashVideoURLConfig {
return func(c *GetDashVideoURLConf) {
c.Need4K = need4k
}
}
func WithNeedDOLBY(needDOLBY bool) GetDashVideoURLConfig {
return func(c *GetDashVideoURLConf) {
c.NeedDOLBY = needDOLBY
}
}
func WithNeedDOLBYAudio(needDOLBYAudio bool) GetDashVideoURLConfig {
return func(c *GetDashVideoURLConf) {
c.NeedDOLBYAudio = needDOLBYAudio
}
}
func WithNeed8K(need8k bool) GetDashVideoURLConfig {
return func(c *GetDashVideoURLConf) {
c.Need8K = need8k
}
}
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/videostream_url.md
func (c *Client) GetDashVideoURL(aid uint, bvid string, cid uint, conf ...GetDashVideoURLConfig) (*mpd.MPD, error) {
config := &GetDashVideoURLConf{}
for _, v := range conf {
v(config)
}
var (
fnval uint = 16
extQuery string
)
if config.Need4K {
fnval = 128
extQuery = "&fourk=1"
} else if config.Need8K {
fnval = 1024
}
if config.HDR {
fnval |= 64
}
if config.NeedDOLBY {
fnval |= 512
}
if config.NeedDOLBYAudio {
fnval |= 256
}
if config.NeedAV1 {
fnval |= 2048
}
var url string
if aid != 0 {
url = fmt.Sprintf("https://api.bilibili.com/x/player/wbi/playurl?aid=%d&cid=%d&fnver=0&platform=pc&fnval=%d%s", aid, cid, fnval, extQuery)
} else if bvid != "" {
url = fmt.Sprintf("https://api.bilibili.com/x/player/wbi/playurl?bvid=%s&cid=%d&fnver=0&platform=pc&fnval=%d%s", bvid, cid, fnval, extQuery)
} else {
return nil, fmt.Errorf("aid and bvid are both empty")
}
req, err := c.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
info := dashResp{}
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return nil, err
}
if info.Code != 0 {
return nil, errors.New(info.Message)
}
m := mpd.NewMPD(mpd.DASH_PROFILE_ONDEMAND, fmt.Sprintf("PT%.2fS", info.Data.Dash.Duration), fmt.Sprintf("PT%.2fS", info.Data.Dash.MinBufferTime))
var as *mpd.AdaptationSet
for _, v := range info.Data.Dash.Video {
as, err = m.AddNewAdaptationSetVideo(v.MimeType, "progressive", true, v.StartWithSap)
if err != nil {
return nil, err
}
video, err := as.AddNewRepresentationVideo(v.Bandwidth, v.Codecs, fmt.Sprint(time.Now().UnixMicro()), v.FrameRate, v.Width, v.Height)
if err != nil {
return nil, err
}
video.Sar = &v.Sar
err = video.AddNewBaseURL(v.BaseURL)
if err != nil {
return nil, err
}
_, err = video.AddNewSegmentBase(v.SegmentBase.IndexRange, v.SegmentBase.Initialization)
if err != nil {
return nil, err
}
}
as = nil
for _, a := range info.Data.Dash.Audio {
as, err = m.AddNewAdaptationSetAudio(a.MimeType, true, a.StartWithSap, "und")
if err != nil {
return nil, err
}
audio, err := as.AddNewRepresentationAudio(44100, a.Bandwidth, a.Codecs, fmt.Sprint(time.Now().UnixMicro()))
if err != nil {
return nil, err
}
audio.Sar = &a.Sar
err = audio.AddNewBaseURL(a.BaseURL)
if err != nil {
return nil, err
}
_, err = audio.AddNewSegmentBase(a.SegmentBase.IndexRange, a.SegmentBase.Initialization)
if err != nil {
return nil, err
}
}
return m, nil
}
type Subtitle struct {
Name string `json:"name"`
URL string `json:"url"`
}
func (c *Client) GetSubtitles(aid uint, bvid string, cid uint) ([]*Subtitle, error) {
var url string
if aid != 0 {
url = fmt.Sprintf("https://api.bilibili.com/x/player/v2?aid=%d&cid=%d", aid, cid)
} else if bvid != "" {
url = fmt.Sprintf("https://api.bilibili.com/x/player/v2?bvid=%s&cid=%d", bvid, cid)
} else {
return nil, fmt.Errorf("aid and bvid are both empty")
}
req, err := c.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
info := playerV2Info{}
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return nil, err
}
if info.Code != 0 {
return nil, errors.New(info.Message)
}
r := make([]*Subtitle, len(info.Data.Subtitle.Subtitles))
for i, s := range info.Data.Subtitle.Subtitles {
r[i] = &Subtitle{
Name: s.LanDoc,
URL: s.SubtitleURL,
}
}
return r, nil
}
func (c *Client) ParsePGCPage(epId, season_id uint) (*VideoPageInfo, error) {
var url string
if epId != 0 {
url = fmt.Sprintf("https://api.bilibili.com/pgc/view/web/season?ep_id=%d", epId)
} else if season_id != 0 {
url = fmt.Sprintf("https://api.bilibili.com/pgc/view/web/season?season_id=%d", season_id)
} else {
return nil, fmt.Errorf("edId and season_id are both empty")
}
req, err := c.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
info := seasonInfo{}
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return nil, err
}
if info.Code != 0 {
return nil, errors.New(info.Message)
}
r := &VideoPageInfo{
Title: info.Result.Title,
Actors: info.Result.Actors,
VideoInfos: make([]*VideoInfo, len(info.Result.Episodes)),
}
for i, v := range info.Result.Episodes {
r.VideoInfos[i] = &VideoInfo{
Epid: v.EpID,
Name: v.ShareCopy,
CoverImage: v.Cover,
}
}
return r, nil
}
func (c *Client) GetPGCURL(ep_id, cid uint, conf ...GetVideoURLConfig) (*VideoURL, error) {
config := &GetVideoURLConf{
Quality: Q1080PP,
}
for _, v := range conf {
v(config)
}
config.fix()
var url string
if ep_id != 0 {
url = fmt.Sprintf("https://api.bilibili.com/pgc/player/web/playurl?ep_id=%d&qn=%d&fourk=1&fnval=0", ep_id, config.Quality)
} else if cid != 0 {
url = fmt.Sprintf("https://api.bilibili.com/pgc/player/web/playurl?cid=%d&qn=%d&fourk=1&fnval=0", cid, config.Quality)
} else {
return nil, fmt.Errorf("edId and season_id are both empty")
}
req, err := c.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
info := pgcURLInfo{}
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return nil, err
}
if info.Code != 0 {
return nil, errors.New(info.Message)
}
return &VideoURL{
AcceptDescription: info.Result.AcceptDescription,
AcceptQuality: info.Result.AcceptQuality,
CurrentQuality: info.Result.Quality,
URL: info.Result.Durl[0].URL,
}, nil
}
func (c *Client) GetDashPGCURL(ep_id, cid uint, conf ...GetDashVideoURLConfig) (*mpd.MPD, error) {
config := &GetDashVideoURLConf{}
for _, v := range conf {
v(config)
}
var (
fnval uint = 16
extQuery string
)
if config.Need4K {
fnval = 128
extQuery = "&fourk=1"
} else if config.Need8K {
fnval = 1024
}
if config.HDR {
fnval |= 64
}
if config.NeedDOLBY {
fnval |= 512
}
if config.NeedDOLBYAudio {
fnval |= 256
}
if config.NeedAV1 {
fnval |= 2048
}
var url string
if ep_id != 0 {
url = fmt.Sprintf("https://api.bilibili.com/pgc/player/web/playurl?ep_id=%d&fnval=%d%s", ep_id, fnval, extQuery)
} else if cid != 0 {
url = fmt.Sprintf("https://api.bilibili.com/pgc/player/web/playurl?cid=%d&fnval=%d%s", ep_id, fnval, extQuery)
} else {
return nil, fmt.Errorf("edId and season_id are both empty")
}
req, err := c.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
info := dashPGCResp{}
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return nil, err
}
if info.Code != 0 {
return nil, errors.New(info.Message)
}
m := mpd.NewMPD(mpd.DASH_PROFILE_ONDEMAND, fmt.Sprintf("PT%.2fS", info.Result.Dash.Duration), fmt.Sprintf("PT%.2fS", info.Result.Dash.MinBufferTime))
var as *mpd.AdaptationSet
for _, v := range info.Result.Dash.Video {
as, err = m.AddNewAdaptationSetVideo(v.MimeType, "progressive", true, v.StartWithSap)
if err != nil {
return nil, err
}
video, err := as.AddNewRepresentationVideo(v.Bandwidth, v.Codecs, fmt.Sprint(time.Now().UnixMicro()), v.FrameRate, v.Width, v.Height)
if err != nil {
return nil, err
}
video.Sar = &v.Sar
err = video.AddNewBaseURL(v.BaseURL)
if err != nil {
return nil, err
}
_, err = video.AddNewSegmentBase(v.SegmentBase.IndexRange, v.SegmentBase.Initialization)
if err != nil {
return nil, err
}
}
as = nil
for _, a := range info.Result.Dash.Audio {
as, err = m.AddNewAdaptationSetAudio(a.MimeType, true, a.StartWithSap, "und")
if err != nil {
return nil, err
}
audio, err := as.AddNewRepresentationAudio(44100, a.Bandwidth, a.Codecs, fmt.Sprint(time.Now().UnixMicro()))
if err != nil {
return nil, err
}
audio.Sar = &a.Sar
err = audio.AddNewBaseURL(a.BaseURL)
if err != nil {
return nil, err
}
_, err = audio.AddNewSegmentBase(a.SegmentBase.IndexRange, a.SegmentBase.Initialization)
if err != nil {
return nil, err
}
}
return m, nil
}

@ -1,28 +0,0 @@
package bilibili
import (
"errors"
"net/http"
json "github.com/json-iterator/go"
)
func (c *Client) UserInfo() (*Nav, error) {
req, err := c.NewRequest(http.MethodGet, "https://api.bilibili.com/x/web-interface/nav", nil, WithoutWbi())
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
var nav Nav
err = json.NewDecoder(resp.Body).Decode(&nav)
if err != nil {
return nil, err
}
if nav.Code != 0 {
return nil, errors.New(nav.Message)
}
return &nav, nil
}

@ -1,40 +0,0 @@
package bilibili
import (
"errors"
"regexp"
"github.com/synctv-org/synctv/utils"
)
var (
BVRegex = regexp.MustCompile(`^(?:https://www\.bilibili\.com/video/)?((?:bv|bV|Bv|BV)\w+)(?:[/\?].*)?$`)
ARegex = regexp.MustCompile(`^(?:https://www\.bilibili\.com/video/)?(?:av|aV|Av|AV)(\d+)(?:[/\?].*)?$`)
SSRegex = regexp.MustCompile(`^(?:https://www\.bilibili\.com/bangumi/play/)?(?:ss|sS|Ss|SS)(\d+)(?:[/\?].*)?$`)
EPRegex = regexp.MustCompile(`^(?:https://www\.bilibili\.com/bangumi/play/)?(?:ep|eP|Ep|EP)(\d+)(?:[/\?].*)?$`)
B23Regex = regexp.MustCompile(`^(https://)?b23\.tv/(\w+)$`)
)
func Match(url string) (t string, id string, err error) {
if B23Regex.MatchString(url) {
resp, err := utils.NoRedirectHttpClient().Get(url)
if err != nil {
return "", "", err
}
resp.Body.Close()
return Match(resp.Header.Get("Location"))
}
if m := BVRegex.FindStringSubmatch(url); m != nil {
return "bv", m[1], nil
}
if m := ARegex.FindStringSubmatch(url); m != nil {
return "av", m[1], nil
}
if m := SSRegex.FindStringSubmatch(url); m != nil {
return "ss", m[1], nil
}
if m := EPRegex.FindStringSubmatch(url); m != nil {
return "ep", m[1], nil
}
return "", "", errors.New("match failed")
}

@ -1,130 +0,0 @@
package bilibili
import (
"crypto/md5"
"encoding/hex"
"errors"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/utils"
refreshcache "github.com/synctv-org/synctv/utils/refreshCache"
)
var (
mixinKeyEncTab = []int{
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
36, 20, 34, 44, 52,
}
wbiCache = refreshcache.NewRefreshCache[key](func() (key, error) {
imgKey, subKey, err := getWbiKeys()
if err != nil {
return key{}, err
}
return key{imgKey, subKey}, nil
}, time.Minute*10)
)
type key struct {
imgKey, subKey string
}
func signAndGenerateURL(urlStr string) (string, error) {
urlObj, err := url.Parse(urlStr)
if err != nil {
return "", err
}
key, err := wbiCache.Get()
if err != nil {
return "", err
}
query := urlObj.Query()
params := map[string]string{}
for k, v := range query {
params[k] = v[0]
}
newParams := encWbi(params, key.imgKey, key.subKey)
for k, v := range newParams {
query.Set(k, v)
}
urlObj.RawQuery = query.Encode()
return urlObj.String(), nil
}
func encWbi(params map[string]string, imgKey, subKey string) map[string]string {
mixinKey := getMixinKey(imgKey + subKey)
currTime := strconv.FormatInt(time.Now().Unix(), 10)
params["wts"] = currTime
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
for k, v := range params {
v = sanitizeString(v)
params[k] = v
}
query := url.Values{}
for _, k := range keys {
query.Set(k, params[k])
}
queryStr := query.Encode()
hash := md5.Sum([]byte(queryStr + mixinKey))
params["w_rid"] = hex.EncodeToString(hash[:])
return params
}
func getMixinKey(orig string) string {
var str strings.Builder
for _, v := range mixinKeyEncTab {
if v < len(orig) {
str.WriteByte(orig[v])
}
}
return str.String()[:32]
}
func sanitizeString(s string) string {
unwantedChars := []string{"!", "'", "(", ")", "*"}
for _, char := range unwantedChars {
s = strings.ReplaceAll(s, char, "")
}
return s
}
func getWbiKeys() (string, string, error) {
req, err := http.NewRequest(http.MethodGet, "https://api.bilibili.com/x/web-interface/nav", nil)
if err != nil {
return "", "", err
}
req.Header.Set("User-Agent", utils.UA)
req.Header.Set("Referer", "https://www.bilibili.com")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", "", err
}
defer resp.Body.Close()
info := Nav{}
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
return "", "", err
}
if info.Data.WbiImg.ImgURL == "" || info.Data.WbiImg.SubURL == "" {
return "", "", errors.New(info.Message)
}
imgKey := strings.Split(strings.Split(info.Data.WbiImg.ImgURL, "/")[len(strings.Split(info.Data.WbiImg.ImgURL, "/"))-1], ".")[0]
subKey := strings.Split(strings.Split(info.Data.WbiImg.SubURL, "/")[len(strings.Split(info.Data.WbiImg.SubURL, "/"))-1], ".")[0]
return imgKey, subKey, nil
}
Loading…
Cancel
Save