diff --git a/Dockerfile b/Dockerfile index f8bcb574..9d5cd2b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20 as builder +FROM golang:1.22 as builder ENV TAG="nightly" ENV COMMIT="" diff --git a/Makefile b/Makefile index 2be06ce4..8e52eb37 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BINPATH := $(abspath ./bin) .PHONY: all -all: build test +all: build #test # # Build Podsync CLI binary diff --git a/go.mod b/go.mod index be75fa54..c0719d52 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/mxpv/podsync -go 1.21 +go 1.22 require ( github.com/BrianHicks/finch v0.0.0-20140409222414-419bd73c29ec @@ -8,6 +8,7 @@ require ( github.com/dgraph-io/badger v1.6.2 github.com/eduncan911/podcast v1.4.2 github.com/gilliek/go-opml v1.0.0 + github.com/go-resty/resty/v2 v2.16.2 // indirect github.com/golang/mock v1.6.0 github.com/hashicorp/go-multierror v1.1.1 github.com/jessevdk/go-flags v1.6.1 @@ -19,13 +20,16 @@ require ( github.com/stretchr/testify v1.9.0 github.com/zackradisic/soundcloud-api v0.1.8 golang.org/x/oauth2 v0.23.0 - golang.org/x/sync v0.7.0 + golang.org/x/sync v0.10.0 google.golang.org/api v0.0.0-20180718221112-efcb5f25ac56 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) +require github.com/CuteReimu/bilibili/v2 v2.0.0-20250103120454-5e8ece713d77 + require ( github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect + github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgraph-io/ristretto v0.0.2 // indirect @@ -34,9 +38,13 @@ require ( github.com/grafov/m3u8 v0.11.1 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.21.0 // indirect + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/spf13/cast v1.7.1 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.28.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect -) +) \ No newline at end of file diff --git a/go.sum b/go.sum index b97e7210..8783ecac 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,12 @@ github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= +github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= github.com/BrianHicks/finch v0.0.0-20140409222414-419bd73c29ec h1:1VPruZMM1WQC7POhjxbZOWK564cuFz1hlpwYW6ocM4E= github.com/BrianHicks/finch v0.0.0-20140409222414-419bd73c29ec/go.mod h1:+hWo/MWgY8VtjZvdrYM2nPRMaK40zX2iPsH/qD0+Xs0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/CuteReimu/bilibili/v2 v2.0.0-20250103120454-5e8ece713d77 h1:STWyuKTWvAjOT/iqvkmpisPSzXJdqlg+4NzUYdf/UZE= +github.com/CuteReimu/bilibili/v2 v2.0.0-20250103120454-5e8ece713d77/go.mod h1:g5XYIOZ8NTGRQNDT2dNRVzSqng4xRZbqurkA0SJbDi8= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -27,9 +31,13 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eduncan911/podcast v1.4.2 h1:S+fsUlbR2ULFou2Mc52G/MZI8JVJHedbxLQnoA+MY/w= github.com/eduncan911/podcast v1.4.2/go.mod h1:mSxiK1z5KeNO0YFaQ3ElJlUZbbDV9dA7R9c1coeeXkc= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gilliek/go-opml v1.0.0 h1:X8xVjtySRXU/x6KvaiXkn7OV3a4DHqxY8Rpv6U/JvCY= github.com/gilliek/go-opml v1.0.0/go.mod h1:fOxmtlzyBvUjU6bjpdjyxCGlWz+pgtAHrHf/xRZl3lk= +github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= +github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -53,12 +61,19 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +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.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/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -71,16 +86,22 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/silentsokolov/go-vimeo v0.0.0-20190116124215-06829264260c h1:KhHx/Ta3c9C1gcSo5UhDeo/D4JnhnxJTrlcOEOFiMfY= github.com/silentsokolov/go-vimeo v0.0.0-20190116124215-06829264260c/go.mod h1:10FeaKUMy5t3KLsYfy54dFrq0rpwcfyKkKcF7vRGIRY= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -109,15 +130,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -129,9 +150,11 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc 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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -139,8 +162,10 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= diff --git a/pkg/builder/bilibili.go b/pkg/builder/bilibili.go new file mode 100644 index 00000000..5caa0453 --- /dev/null +++ b/pkg/builder/bilibili.go @@ -0,0 +1,109 @@ +package builder + +import ( + "context" + + "strconv" + "time" + + "github.com/CuteReimu/bilibili/v2" + "github.com/mxpv/podsync/pkg/feed" + "github.com/pkg/errors" + + "github.com/mxpv/podsync/pkg/model" +) + +type BilibiliBuilder struct { + client *bilibili.Client +} + +func (b *BilibiliBuilder) queryFeed(feed *model.Feed, info *model.Info) error { + + switch info.LinkType { + case model.TypeChannel: + //TODO channel surpport + return errors.New("Bilibili channel not supported.") + case model.TypeUser: + // query user info + mid, err := strconv.Atoi(info.ItemID) + if err != nil { + return err + } + userCardParam := bilibili.GetUserCardParam{Mid: mid, Photo: false} + userCard, err := b.client.GetUserCard(userCardParam) + if err != nil { + return err + } + + feed.Author = userCard.Card.Name + feed.CoverArt = userCard.Card.Face + feed.Title = userCard.Card.Name + feed.Description = userCard.Card.Sign + + // query video collection + videoParam := bilibili.GetVideoByKeywordsParam{Mid: mid, Keywords: "", Pn: feed.PageSize} + videoCollection, err := b.client.GetVideoByKeywords(videoParam) + if err != nil { + return err + } + + feed.PubDate = time.Unix(int64(videoCollection.Archives[0].Pubdate), 0) + + for _, videoInfo := range videoCollection.Archives { + bvid := videoInfo.Bvid + desc, err := b.client.GetVideoDesc(bilibili.VideoParam{Bvid: bvid}) + if err == nil { + e := model.Episode{ + ID: videoInfo.Bvid, + Title: videoInfo.Title, + Description: desc, + Duration: int64(videoInfo.Duration), + Size: int64(videoInfo.Duration * 15000), // very rough estimate + VideoURL: "https://www.bilibili.com/" + videoInfo.Bvid, + PubDate: time.Unix(int64(videoInfo.Pubdate), 0), + Thumbnail: videoInfo.Pic, + Status: model.EpisodeNew, + } + feed.Episodes = append(feed.Episodes, &e) + } + } + + return nil + default: + return errors.New("unsupported link format") + } +} + +func (b *BilibiliBuilder) Build(ctx context.Context, cfg *feed.Config) (*model.Feed, error) { + info, err := ParseURL(cfg.URL) + if err != nil { + return nil, err + } + + _feed := &model.Feed{ + ItemID: info.ItemID, + Provider: info.Provider, + LinkType: info.LinkType, + Format: cfg.Format, + Quality: cfg.Quality, + CoverArtQuality: cfg.Custom.CoverArtQuality, + PageSize: cfg.PageSize, + PlaylistSort: cfg.PlaylistSort, + PrivateFeed: cfg.PrivateFeed, + UpdatedAt: time.Now().UTC(), + ItemURL: cfg.URL, + } + + // Query general information about feed (title, description, lang, etc) + if err := b.queryFeed(_feed, &info); err != nil { + return nil, err + } + + return _feed, nil +} + +func NewBilibiliBuilder() (*BilibiliBuilder, error) { + sc := bilibili.New() + + return &BilibiliBuilder{client: sc}, nil +} diff --git a/pkg/builder/bilibili_test.go b/pkg/builder/bilibili_test.go new file mode 100644 index 00000000..b43ac747 --- /dev/null +++ b/pkg/builder/bilibili_test.go @@ -0,0 +1,46 @@ +package builder + +import ( + "testing" + + "github.com/mxpv/podsync/pkg/feed" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBilibili_BuildFeed(t *testing.T) { + builder, err := NewBilibiliBuilder() + require.NoError(t, err) + + urls := []string{ + "https://space.bilibili.com/1302298364", + "https://space.bilibili.com/397490386/channel/seriesdetail?sid=1203833", + } + + t.Run(urls[0], func(t *testing.T) { + feed, err := builder.Build(testCtx, &feed.Config{URL: urls[0]}) + require.NoError(t, err) + + assert.NotEmpty(t, feed.Title) + assert.NotEmpty(t, feed.Description) + assert.NotEmpty(t, feed.Author) + assert.NotEmpty(t, feed.ItemURL) + + assert.NotZero(t, len(feed.Episodes)) + + for _, item := range feed.Episodes { + assert.NotEmpty(t, item.Title) + assert.NotEmpty(t, item.VideoURL) + assert.NotZero(t, item.Duration) + assert.NotEmpty(t, item.Title) + assert.NotEmpty(t, item.Thumbnail) + } + }) + + t.Run(urls[1], func(t *testing.T) { + _, err := builder.Build(testCtx, &feed.Config{URL: urls[1]}) + require.Error(t, err, "Bilibili channel not supported.") + + }) + +} diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go index 42b6d0a1..ed2e3c5f 100644 --- a/pkg/builder/builder.go +++ b/pkg/builder/builder.go @@ -21,6 +21,8 @@ func New(ctx context.Context, provider model.Provider, key string) (Builder, err return NewVimeoBuilder(ctx, key) case model.ProviderSoundcloud: return NewSoundcloudBuilder() + case model.ProviderBilibili: + return NewBilibiliBuilder() default: return nil, errors.Errorf("unsupported provider %q", provider) } diff --git a/pkg/builder/url.go b/pkg/builder/url.go index b3c173dd..0e83f953 100755 --- a/pkg/builder/url.go +++ b/pkg/builder/url.go @@ -17,6 +17,19 @@ func ParseURL(link string) (model.Info, error) { info := model.Info{} + if strings.HasSuffix(parsed.Host, "bilibili.com") { + kind, id, err := parseBilibiliURL(parsed) + if err != nil { + return model.Info{}, err + } + + info.Provider = model.ProviderBilibili + info.LinkType = kind + info.ItemID = id + + return info, nil + } + if strings.HasSuffix(parsed.Host, "youtube.com") { kind, id, err := parseYoutubeURL(parsed) if err != nil { @@ -125,6 +138,31 @@ func parseYoutubeURL(parsed *url.URL) (model.Type, string, error) { return "", "", errors.New("unsupported link format") } +func parseBilibiliURL(parsed *url.URL) (model.Type, string, error) { + // https://space.bilibili.com/7380321 + // https://space.bilibili.com/7380321/channel/collectiondetail?sid=531853 + + subdomain := strings.Split(parsed.Host, ".")[0] + parts := strings.Split(parsed.EscapedPath(), "/") + + if len(parts) <= 1 || subdomain != "space" { + return "", "", errors.New("invalid bilibili link path") + } + var kind model.Type + if len(parts) == 2 { + kind = model.TypeUser + return kind, parts[1], nil + } else if parts[2] == "channel" { + kind = model.TypeChannel + params, err := url.ParseQuery(parsed.RawQuery) + if err != nil { + return "", "", errors.New("invalid bilibili channel path") + } + return kind, parts[1] + ":" + params["sid"][0], nil + } + return "", "", errors.New("unsupported link format") +} + func parseVimeoURL(parsed *url.URL) (model.Type, string, error) { parts := strings.Split(parsed.EscapedPath(), "/") if len(parts) <= 1 { diff --git a/pkg/builder/url_test.go b/pkg/builder/url_test.go index 48625b52..ccab554b 100644 --- a/pkg/builder/url_test.go +++ b/pkg/builder/url_test.go @@ -9,6 +9,22 @@ import ( "github.com/mxpv/podsync/pkg/model" ) +func TestParseBililiURL(t *testing.T) { + link, _ := url.ParseRequestURI("https://space.bilibili.com/291222529") + // fmt.Print(link, "\n") + kind, id, err := parseBilibiliURL(link) + // fmt.Print(kind, id, err) + require.NoError(t, err) + require.Equal(t, model.TypeUser, kind) + require.Equal(t, "291222529", id) + + link, _ = url.ParseRequestURI("https://space.bilibili.com/7380321/channel/collectiondetail?sid=531853") + kind, id, err = parseBilibiliURL(link) + require.NoError(t, err) + require.Equal(t, model.TypeChannel, kind) + require.Equal(t, "7380321:531853", id) +} + func TestParseYoutubeURL_Playlist(t *testing.T) { link, _ := url.ParseRequestURI("https://www.youtube.com/playlist?list=PLCB9F975ECF01953C") kind, id, err := parseYoutubeURL(link) diff --git a/pkg/model/link.go b/pkg/model/link.go index 095e738b..4f6e62ab 100755 --- a/pkg/model/link.go +++ b/pkg/model/link.go @@ -12,6 +12,7 @@ const ( type Provider string const ( + ProviderBilibili = Provider("bilibili") ProviderYoutube = Provider("youtube") ProviderVimeo = Provider("vimeo") ProviderSoundcloud = Provider("soundcloud")