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" "fmt"
"net/http" "net/http"
"strings" "strings"
"sync"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
@ -36,37 +37,35 @@ func (d *GithubReleases) Drop(ctx context.Context) error {
return nil return nil
} }
func (d *GithubReleases) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { // processPoint 处理单个挂载点的文件列表
files := make([]File, 0) func (d *GithubReleases) processPoint(point *MountPoint, path string, args model.ListArgs) []File {
path := fmt.Sprintf("/%s", strings.Trim(dir.GetPath(), "/")) var pointFiles []File
for i := range d.points {
point := &d.points[i]
if !d.Addition.ShowAllVersion { // latest if !d.Addition.ShowAllVersion { // latest
point.RequestRelease(d.GetRequest, args.Refresh) 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 { // 与仓库路径相同 if point.Point == path { // 与仓库路径相同
files = append(files, point.GetLatestRelease()...) pointFiles = append(pointFiles, point.GetLatestRelease()...)
if d.Addition.ShowReadme { if d.Addition.ShowReadme {
files = append(files, point.GetOtherFile(d.GetRequest, args.Refresh)...) files := point.GetOtherFile(d.GetRequest, false)
pointFiles = append(pointFiles, files...)
} }
} else if strings.HasPrefix(point.Point, path) { // 仓库目录的父目录 } else if strings.HasPrefix(point.Point, path) { // 仓库目录的父目录
nextDir := GetNextDir(point.Point, path) nextDir := GetNextDir(point.Point, path)
if nextDir == "" { if nextDir != "" {
continue dirFile := File{
}
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, Path: path + "/" + nextDir,
FileName: nextDir, FileName: nextDir,
Size: point.GetLatestSize(), Size: point.GetLatestSize(),
@ -74,33 +73,28 @@ func (d *GithubReleases) List(ctx context.Context, dir model.Obj, args model.Lis
CreateAt: point.Release.CreatedAt, CreateAt: point.Release.CreatedAt,
Type: "dir", Type: "dir",
Url: "", Url: "",
}) }
pointFiles = append(pointFiles, dirFile)
} }
} }
} else { // all version
point.RequestReleases(d.GetRequest, args.Refresh) return pointFiles
}
// processAllVersions 处理所有版本的逻辑
func (d *GithubReleases) processAllVersions(point *MountPoint, path string) []File {
var pointFiles []File
if point.Point == path { // 与仓库路径相同 if point.Point == path { // 与仓库路径相同
files = append(files, point.GetAllVersion()...) pointFiles = append(pointFiles, point.GetAllVersion()...)
if d.Addition.ShowReadme { if d.Addition.ShowReadme {
files = append(files, point.GetOtherFile(d.GetRequest, args.Refresh)...) files := point.GetOtherFile(d.GetRequest, false)
pointFiles = append(pointFiles, files...)
} }
} else if strings.HasPrefix(point.Point, path) { // 仓库目录的父目录 } else if strings.HasPrefix(point.Point, path) { // 仓库目录的父目录
nextDir := GetNextDir(point.Point, path) nextDir := GetNextDir(point.Point, path)
if nextDir == "" { if nextDir != "" {
continue dirFile := File{
}
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, FileName: nextDir,
Path: path + "/" + nextDir, Path: path + "/" + nextDir,
Size: point.GetAllVersionSize(), Size: point.GetAllVersionSize(),
@ -108,17 +102,66 @@ func (d *GithubReleases) List(ctx context.Context, dir model.Obj, args model.Lis
CreateAt: (*point.Releases)[0].CreatedAt, CreateAt: (*point.Releases)[0].CreatedAt,
Type: "dir", Type: "dir",
Url: "", Url: "",
}) }
pointFiles = append(pointFiles, dirFile)
} }
} else if strings.HasPrefix(path, point.Point) { // 仓库目录的子目录 } else if strings.HasPrefix(path, point.Point) { // 仓库目录的子目录
tagName := GetNextDir(path, point.Point) tagName := GetNextDir(path, point.Point)
if tagName == "" { if tagName != "" {
continue pointFiles = append(pointFiles, point.GetReleaseByTagName(tagName)...)
}
} }
files = append(files, 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(), "/"))
if d.Addition.ConcurrentRequests && d.Addition.Token != "" { // 并发处理
var mu sync.Mutex
var wg sync.WaitGroup
for i := range d.points {
wg.Add(1)
go func(point *MountPoint) {
defer wg.Done()
pointFiles := d.processPoint(point, path, args)
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)
}
} }
return utils.SliceConvert(files, func(src File) (model.Obj, error) { return utils.SliceConvert(files, func(src File) (model.Obj, error) {

View File

@ -11,6 +11,7 @@ type Addition struct {
ShowReadme bool `json:"show_readme" type:"bool" default:"true" help:"show README、LICENSE file"` 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"` 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"` 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 "` 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 "`
} }

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 == "" { if m.Repo == "" {
return return
} }

View File

@ -6,8 +6,8 @@ import (
"strings" "strings"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
) )
// 发送 GET 请求 // 发送 GET 请求
@ -23,7 +23,7 @@ func (d *GithubReleases) GetRequest(url string) (*resty.Response, error) {
return nil, err return nil, err
} }
if res.StatusCode() != 200 { 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 return res, nil
} }