feat(github_releases): concurrently request the GitHub API (#9211)

pull/9197/merge
Sakana 2025-07-24 15:30:12 +08:00 committed by GitHub
parent 2bdc5bef9e
commit 5e15a360b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 128 additions and 84 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"strings"
"sync"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
@ -36,88 +37,130 @@ func (d *GithubReleases) Drop(ctx context.Context) error {
return nil
}
// processPoint 处理单个挂载点的文件列表
func (d *GithubReleases) processPoint(point *MountPoint, path string, args model.ListArgs) []File {
var pointFiles []File
if !d.Addition.ShowAllVersion { // latest
point.RequestLatestRelease(d.GetRequest, args.Refresh)
pointFiles = d.processLatestVersion(point, path)
} else { // all version
point.RequestReleases(d.GetRequest, args.Refresh)
pointFiles = d.processAllVersions(point, path)
}
return pointFiles
}
// processLatestVersion 处理最新版本的逻辑
func (d *GithubReleases) processLatestVersion(point *MountPoint, path string) []File {
var pointFiles []File
if point.Point == path { // 与仓库路径相同
pointFiles = append(pointFiles, point.GetLatestRelease()...)
if d.Addition.ShowReadme {
files := point.GetOtherFile(d.GetRequest, false)
pointFiles = append(pointFiles, files...)
}
} else if strings.HasPrefix(point.Point, path) { // 仓库目录的父目录
nextDir := GetNextDir(point.Point, path)
if nextDir != "" {
dirFile := File{
Path: path + "/" + nextDir,
FileName: nextDir,
Size: point.GetLatestSize(),
UpdateAt: point.Release.PublishedAt,
CreateAt: point.Release.CreatedAt,
Type: "dir",
Url: "",
}
pointFiles = append(pointFiles, dirFile)
}
}
return pointFiles
}
// processAllVersions 处理所有版本的逻辑
func (d *GithubReleases) processAllVersions(point *MountPoint, path string) []File {
var pointFiles []File
if point.Point == path { // 与仓库路径相同
pointFiles = append(pointFiles, point.GetAllVersion()...)
if d.Addition.ShowReadme {
files := point.GetOtherFile(d.GetRequest, false)
pointFiles = append(pointFiles, files...)
}
} else if strings.HasPrefix(point.Point, path) { // 仓库目录的父目录
nextDir := GetNextDir(point.Point, path)
if nextDir != "" {
dirFile := File{
FileName: nextDir,
Path: path + "/" + nextDir,
Size: point.GetAllVersionSize(),
UpdateAt: (*point.Releases)[0].PublishedAt,
CreateAt: (*point.Releases)[0].CreatedAt,
Type: "dir",
Url: "",
}
pointFiles = append(pointFiles, dirFile)
}
} else if strings.HasPrefix(path, point.Point) { // 仓库目录的子目录
tagName := GetNextDir(path, point.Point)
if tagName != "" {
pointFiles = append(pointFiles, point.GetReleaseByTagName(tagName)...)
}
}
return pointFiles
}
// mergeFiles 合并文件列表,处理重复目录
func (d *GithubReleases) mergeFiles(files *[]File, newFiles []File) {
for _, newFile := range newFiles {
if newFile.Type == "dir" {
hasSameDir := false
for index := range *files {
if (*files)[index].GetName() == newFile.GetName() && (*files)[index].Type == "dir" {
hasSameDir = true
(*files)[index].Size += newFile.Size
break
}
}
if !hasSameDir {
*files = append(*files, newFile)
}
} else {
*files = append(*files, newFile)
}
}
}
func (d *GithubReleases) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files := make([]File, 0)
path := fmt.Sprintf("/%s", strings.Trim(dir.GetPath(), "/"))
for i := range d.points {
point := &d.points[i]
if d.Addition.ConcurrentRequests && d.Addition.Token != "" { // 并发处理
var mu sync.Mutex
var wg sync.WaitGroup
if !d.Addition.ShowAllVersion { // latest
point.RequestRelease(d.GetRequest, args.Refresh)
for i := range d.points {
wg.Add(1)
go func(point *MountPoint) {
defer wg.Done()
pointFiles := d.processPoint(point, path, args)
if point.Point == path { // 与仓库路径相同
files = append(files, point.GetLatestRelease()...)
if d.Addition.ShowReadme {
files = append(files, point.GetOtherFile(d.GetRequest, args.Refresh)...)
}
} else if strings.HasPrefix(point.Point, path) { // 仓库目录的父目录
nextDir := GetNextDir(point.Point, path)
if nextDir == "" {
continue
}
hasSameDir := false
for index := range files {
if files[index].GetName() == nextDir {
hasSameDir = true
files[index].Size += point.GetLatestSize()
break
}
}
if !hasSameDir {
files = append(files, File{
Path: path + "/" + nextDir,
FileName: nextDir,
Size: point.GetLatestSize(),
UpdateAt: point.Release.PublishedAt,
CreateAt: point.Release.CreatedAt,
Type: "dir",
Url: "",
})
}
}
} else { // all version
point.RequestReleases(d.GetRequest, args.Refresh)
if point.Point == path { // 与仓库路径相同
files = append(files, point.GetAllVersion()...)
if d.Addition.ShowReadme {
files = append(files, point.GetOtherFile(d.GetRequest, args.Refresh)...)
}
} else if strings.HasPrefix(point.Point, path) { // 仓库目录的父目录
nextDir := GetNextDir(point.Point, path)
if nextDir == "" {
continue
}
hasSameDir := false
for index := range files {
if files[index].GetName() == nextDir {
hasSameDir = true
files[index].Size += point.GetAllVersionSize()
break
}
}
if !hasSameDir {
files = append(files, File{
FileName: nextDir,
Path: path + "/" + nextDir,
Size: point.GetAllVersionSize(),
UpdateAt: (*point.Releases)[0].PublishedAt,
CreateAt: (*point.Releases)[0].CreatedAt,
Type: "dir",
Url: "",
})
}
} else if strings.HasPrefix(path, point.Point) { // 仓库目录的子目录
tagName := GetNextDir(path, point.Point)
if tagName == "" {
continue
}
files = append(files, point.GetReleaseByTagName(tagName)...)
}
mu.Lock()
d.mergeFiles(&files, pointFiles)
mu.Unlock()
}(&d.points[i])
}
wg.Wait()
} else { // 串行处理
for i := range d.points {
point := &d.points[i]
pointFiles := d.processPoint(point, path, args)
d.mergeFiles(&files, pointFiles)
}
}

View File

@ -7,11 +7,12 @@ import (
type Addition struct {
driver.RootID
RepoStructure string `json:"repo_structure" type:"text" required:"true" default:"alistGo/alist" help:"structure:[path:]org/repo"`
ShowReadme bool `json:"show_readme" type:"bool" default:"true" help:"show README、LICENSE file"`
Token string `json:"token" type:"string" required:"false" help:"GitHub token, if you want to access private repositories or increase the rate limit"`
ShowAllVersion bool `json:"show_all_version" type:"bool" default:"false" help:"show all versions"`
GitHubProxy string `json:"gh_proxy" type:"string" default:"" help:"GitHub proxy, e.g. https://ghproxy.net/github.com or https://gh-proxy.com/github.com "`
RepoStructure string `json:"repo_structure" type:"text" required:"true" default:"alistGo/alist" help:"structure:[path:]org/repo"`
ShowReadme bool `json:"show_readme" type:"bool" default:"true" help:"show README、LICENSE file"`
Token string `json:"token" type:"string" required:"false" help:"GitHub token, if you want to access private repositories or increase the rate limit"`
ShowAllVersion bool `json:"show_all_version" type:"bool" default:"false" help:"show all versions"`
ConcurrentRequests bool `json:"concurrent_requests" type:"bool" default:"false" help:"To concurrently request the GitHub API, you must enter a GitHub token"`
GitHubProxy string `json:"gh_proxy" type:"string" default:"" help:"GitHub proxy, e.g. https://ghproxy.net/github.com or https://gh-proxy.com/github.com "`
}
var config = driver.Config{

View File

@ -18,7 +18,7 @@ type MountPoint struct {
}
// 请求最新版本
func (m *MountPoint) RequestRelease(get func(url string) (*resty.Response, error), refresh bool) {
func (m *MountPoint) RequestLatestRelease(get func(url string) (*resty.Response, error), refresh bool) {
if m.Repo == "" {
return
}

View File

@ -6,8 +6,8 @@ import (
"strings"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
)
// 发送 GET 请求
@ -23,7 +23,7 @@ func (d *GithubReleases) GetRequest(url string) (*resty.Response, error) {
return nil, err
}
if res.StatusCode() != 200 {
log.Warn("failed to get request: ", res.StatusCode(), res.String())
utils.Log.Warnf("failed to get request: %s %d %s", url, res.StatusCode(), res.String())
}
return res, nil
}