This commit is contained in:
helloteemo 2024-05-25 06:30:53 +03:00 committed by GitHub
commit ce699c8069
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 167 additions and 1 deletions

View file

@ -21,6 +21,7 @@
- [Twitch Channels](#twitch-channels)
- [Twitch Top Games](#twitch-top-games)
- [iframe](#iframe)
- [bilibili](#bilibili)
## Intro
Configuration is done via a single YAML file and a server restart is required in order for any changes to take effect. Trying to start the server with an invalid config file will result in an error.
@ -1132,3 +1133,20 @@ The source of the iframe.
##### `height`
The height of the iframe. The minimum allowed height is 50.
## bilibili
Display a list of the latest videos from specific bilibili channels.
Example:
```yaml
- type: bilibili
uidList:
- 50329118 # LOL
- 578227337
```
Preview:
![](images/videos-widget-preview-bilibili.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

3
go.mod
View file

@ -4,6 +4,7 @@ go 1.22.0
require (
github.com/mmcdole/gofeed v1.3.0
github.com/stretchr/testify v1.8.1
golang.org/x/text v0.14.0
gopkg.in/yaml.v3 v3.0.1
)
@ -11,9 +12,11 @@ require (
require (
github.com/PuerkitoBio/goquery v1.9.1 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mmcdole/goxpp v1.1.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.24.0 // indirect
)

5
go.sum
View file

@ -20,7 +20,11 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
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/stretchr/objx v0.1.0/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/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@ -63,5 +67,6 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -1,5 +1,5 @@
{{ define "video-card-contents" }}
<img class="video-thumbnail thumbnail" loading="lazy" src="{{ .ThumbnailUrl }}" alt="">
<img referrerpolicy="no-referrer" class="video-thumbnail thumbnail" loading="lazy" src="{{ .ThumbnailUrl }}" alt="">
<div class="margin-top-10 margin-bottom-widget flex flex-column grow padding-inline-widget">
<a class="video-title color-primary-if-not-visited" href="{{ .Url }}" target="_blank" rel="noreferrer" title="{{ .Title }}">{{ .Title }}</a>
<ul class="list-horizontal-text flex-nowrap margin-top-7">

76
internal/feed/bilibili.go Normal file
View file

@ -0,0 +1,76 @@
package feed
import (
"fmt"
"log/slog"
"net/http"
"strconv"
"strings"
"time"
)
type bilibiliSpaceResponseJson struct {
Data struct {
Item []struct {
Title string `json:"title"`
Cover string `json:"cover"`
Ctime int64 `json:"ctime"`
Author string `json:"author"`
Bvid string `json:"bvid"`
} `json:"item"`
} `json:"data"`
}
func FetchBilibiliUploads(uidList []int) (Videos, error) {
requests := make([]*http.Request, 0, len(uidList))
u := "https://app.bilibili.com/x/v2/space/archive/cursor?vmid="
for i := range uidList {
request, _ := http.NewRequest("GET", u+strconv.Itoa(uidList[i]), nil)
request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
request.Header.Set("Referer", "https://www.bilibili.com/")
requests = append(requests, request)
}
job := newJob(decodeJsonFromRequestTask[bilibiliSpaceResponseJson](defaultClient), requests).withWorkers(30)
responses, errs, err := workerPoolDo(job)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrNoContent, err)
}
videos := make(Videos, 0, len(uidList)*15)
var failed int
for i := range responses {
if errs[i] != nil {
failed++
slog.Error("Failed to fetch bilibili feed", "uid", uidList[i], "error", errs[i])
continue
}
response := responses[i]
for j := range response.Data.Item {
video := &response.Data.Item[j]
videoUrl := `https://www.bilibili.com/video/` + video.Bvid
videos = append(videos, Video{
ThumbnailUrl: video.Cover,
Title: video.Title,
Url: strings.ReplaceAll(videoUrl, "http://", "https://"),
Author: video.Author,
AuthorUrl: `https://space.bilibili.com/` + strconv.Itoa(uidList[i]),
TimePosted: time.Unix(video.Ctime, 0),
})
}
}
if len(videos) == 0 {
return nil, ErrNoContent
}
videos.SortByNewest()
if failed > 0 {
return videos, fmt.Errorf("%w: missing videos from %d up", ErrPartialContent, failed)
}
return videos, nil
}

View file

@ -0,0 +1,12 @@
package feed
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestFetchBilibiliUploads(t *testing.T) {
videos, err := FetchBilibiliUploads([]int{50329118})
assert.Nil(t, err)
assert.True(t, len(videos) > 0)
}

View file

@ -0,0 +1,50 @@
package widget
import (
"context"
"github.com/glanceapp/glance/internal/assets"
"github.com/glanceapp/glance/internal/feed"
"html/template"
"time"
)
type Bilibili struct {
widgetBase `yaml:",inline"`
Videos feed.Videos `yaml:"-"`
Style string `yaml:"style"`
UidList []int `yaml:"uidList"`
Limit int `yaml:"limit"`
}
var _ Widget = (*Bilibili)(nil)
func (widget *Bilibili) Initialize() error {
widget.withTitle("Bilibili").withCacheDuration(time.Hour)
if widget.Limit <= 0 {
widget.Limit = 25
}
return nil
}
func (widget *Bilibili) Update(ctx context.Context) {
videos, err := feed.FetchBilibiliUploads(widget.UidList)
if !widget.canContinueUpdateAfterHandlingErr(err) {
return
}
if len(videos) > widget.Limit {
videos = videos[:widget.Limit]
}
widget.Videos = videos
}
func (widget *Bilibili) Render() template.HTML {
if widget.Style == "grid-cards" {
return widget.render(widget, assets.VideosGridTemplate)
}
return widget.render(widget, assets.VideosTemplate)
}

View file

@ -31,6 +31,8 @@ func New(widgetType string) (Widget, error) {
return &Releases{}, nil
case "videos":
return &Videos{}, nil
case "bilibili":
return &Bilibili{}, nil
case "stocks":
return &Stocks{}, nil
case "reddit":