Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions drivers/quark_uc/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ func (d *QuarkOrUC) getTempFolder() {
d.createTempFolder()
}

// EnsureTempDir 确保转存用的临时目录 ID 已初始化。Init 只跑一次,若当时 GetFiles 失败
// 会导致 TempDirId 为空(转存 to_pdir_fid 为空被夸克拒绝)。外部包转存前调用以兜底重试。
func (d *QuarkOrUC) EnsureTempDir() {
if d.TempDirId == "" {
log.Infof("[%d] %s temp dir empty, re-init", d.ID, d.config.Name)
d.getTempFolder()
}
}

func (d *QuarkOrUC) createTempFolder() {
data := base.Json{
"dir_init_lock": false,
Expand Down
27 changes: 24 additions & 3 deletions drivers/quark_uc_share/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,30 @@ func (d *QuarkUCShare) Link(ctx context.Context, file model.Obj, args model.Link
return link, nil
}

// 转存 + speedup token 为主(提速直链走 dl-c 通道,实测 ~14MB/s vs 免转存 ~1.7MB/s);
// speedup 必须先转存(聊天会话只认自己网盘的 fid),故转存是快速通道的前提。
// 免转存(share-direct)降为兜底:转存失败/无网盘账号时仍可播,但被限速。
// 多账号分片并行下载(开关开):直接对所有网盘账号 goroutine 并发取链,
// 跳过串行的 resolveQuarkUCShareLink(它失败才换下一个账号,慢;且会让主链账号被重复取)。
// 首个账号成功后只额外等 2s 收集更多源,不等最慢账号,启播≈首源+2s。
if multiSourceEnabled(d) {
links := collectMultiAccountLinks(ctx, d, file, args)
if len(links) > 0 {
link := links[0]
if len(links) > 1 {
link.MultiSource = sourcesFromLinks(links, d)
log.Infof("[multi-source] %s %s 用 %d 账号分片下载 file=%s", d.config.Name, file.GetName(), len(link.MultiSource), file.GetID())
}
quarkUCShareLinkCache.Set(key, link)
return link, nil
}
// 全部账号取链失败,回退免转存兜底。
log.Warnf("[multi-source] 全部账号取链失败,回退免转存")
link, err := resolveShareDirectLink(d, file)
if err == nil && link != nil {
quarkUCShareLinkCache.Set(key, link)
}
return link, err
}

// 开关关:转存 + speedup 为主(串行轮询账号,失败换下一个),免转存兜底。
var link *model.Link
var err error
link, err = resolveQuarkUCShareLink(ctx, d, file, args)
Expand Down
82 changes: 82 additions & 0 deletions drivers/quark_uc_share/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,28 @@ import (
"github.com/OpenListTeam/OpenList/v4/internal/model"
)

// stubMultiSourceDisabled 关闭多账号分片下载并还原,避免 Link() 读 setting 死锁(测试里 op 未初始化)。
func stubMultiSourceDisabled(t *testing.T) {
t.Helper()
origMS := multiSourceEnabled
origCollect := collectMultiAccountLinks
multiSourceEnabled = func(d *QuarkUCShare) bool { return false }
collectMultiAccountLinks = func(ctx context.Context, d *QuarkUCShare, file model.Obj, args model.LinkArgs) []*model.Link {
return nil
}
t.Cleanup(func() {
multiSourceEnabled = origMS
collectMultiAccountLinks = origCollect
})
}

func TestQuarkUCShareLink_CachesByFileID(t *testing.T) {
origCache := quarkUCShareLinkCache
origResolver := resolveQuarkUCShareLink
origDirect := resolveShareDirectLink
origSVIP := accountIsSVIP
origMS := multiSourceEnabled
origCollect := collectMultiAccountLinks
quarkUCShareLinkCache = cache.NewKeyedCache[*model.Link](time.Hour)
resolveCalls := 0
resolveQuarkUCShareLink = func(ctx context.Context, d *QuarkUCShare, file model.Obj, args model.LinkArgs) (*model.Link, error) {
Expand All @@ -28,11 +45,17 @@ func TestQuarkUCShareLink_CachesByFileID(t *testing.T) {
resolveShareDirectLink = func(d *QuarkUCShare, file model.Obj) (*model.Link, error) {
return nil, errors.New("share-direct stub disabled") // 置失败,流程落到 resolveQuarkUCShareLink
}
multiSourceEnabled = func(d *QuarkUCShare) bool { return false }
collectMultiAccountLinks = func(ctx context.Context, d *QuarkUCShare, file model.Obj, args model.LinkArgs) []*model.Link {
return nil
}
t.Cleanup(func() {
quarkUCShareLinkCache = origCache
resolveQuarkUCShareLink = origResolver
resolveShareDirectLink = origDirect
accountIsSVIP = origSVIP
multiSourceEnabled = origMS
collectMultiAccountLinks = origCollect
})

d := &QuarkUCShare{Addition: Addition{ShareToken: "share-token"}, config: driver.Config{Name: "QuarkShare"}}
Expand Down Expand Up @@ -69,6 +92,10 @@ func TestQuarkUCShareLink_DoesNotCacheErrors(t *testing.T) {
resolveShareDirectLink = func(d *QuarkUCShare, file model.Obj) (*model.Link, error) {
return nil, errors.New("share-direct stub disabled") // 失败 → 落到同样报错的 resolveQuarkUCShareLink
}
multiSourceEnabled = func(d *QuarkUCShare) bool { return false }
collectMultiAccountLinks = func(ctx context.Context, d *QuarkUCShare, file model.Obj, args model.LinkArgs) []*model.Link {
return nil
}
t.Cleanup(func() {
quarkUCShareLinkCache = origCache
resolveQuarkUCShareLink = origResolver
Expand All @@ -87,6 +114,7 @@ func TestQuarkUCShareLink_DoesNotCacheErrors(t *testing.T) {
}

func TestQuarkUCShareLink_DifferentFileIDsDoNotShareCache(t *testing.T) {
stubMultiSourceDisabled(t)
origCache := quarkUCShareLinkCache
origResolver := resolveQuarkUCShareLink
origDirect := resolveShareDirectLink
Expand Down Expand Up @@ -119,6 +147,7 @@ func TestQuarkUCShareLink_DifferentFileIDsDoNotShareCache(t *testing.T) {

func TestQuarkUCShareLink_FallbackToShareDirectOnSaveFail(t *testing.T) {
// 转存(save+speedup)为主;转存失败时回退免转存(share-direct)。
stubMultiSourceDisabled(t)
origCache := quarkUCShareLinkCache
origResolver := resolveQuarkUCShareLink
origDirect := resolveShareDirectLink
Expand Down Expand Up @@ -159,6 +188,7 @@ func TestQuarkUCShareLink_FallbackToShareDirectOnSaveFail(t *testing.T) {

func TestQuarkUCShareLink_PrefersSaveAndSpeedup(t *testing.T) {
// 转存(save+speedup)为主:成功时不调用免转存(免转存无 speedup,被限速,仅兜底)。
stubMultiSourceDisabled(t)
origCache := quarkUCShareLinkCache
origResolver := resolveQuarkUCShareLink
origDirect := resolveShareDirectLink
Expand Down Expand Up @@ -197,6 +227,58 @@ func TestQuarkUCShareLink_PrefersSaveAndSpeedup(t *testing.T) {
}
}

// 开关开启、collectMultiAccountLinks 返回多条链时,Link() 跳过串行主链,直接用首条为 primary、全部填 MultiSource。
func TestQuarkUCShareLink_PopulatesMultiSourceWhenEnabled(t *testing.T) {
stubMultiSourceDisabled(t)
origCache := quarkUCShareLinkCache
origResolver := resolveQuarkUCShareLink
origDirect := resolveShareDirectLink
quarkUCShareLinkCache = cache.NewKeyedCache[*model.Link](time.Hour)
// 开关开时 Link() 不再调 resolveQuarkUCShareLink(直接并发),这里设个哨兵确保不被调用。
resolveQuarkUCShareLink = func(ctx context.Context, d *QuarkUCShare, file model.Obj, args model.LinkArgs) (*model.Link, error) {
t.Fatalf("resolveQuarkUCShareLink should not be called when multi-source enabled")
return nil, nil
}
resolveShareDirectLink = func(d *QuarkUCShare, file model.Obj) (*model.Link, error) {
return nil, errors.New("share-direct stub disabled")
}
// 开启多账号分片:collectMultiAccountLinks 返回 3 条链(首条为 primary)。
multiSourceEnabled = func(d *QuarkUCShare) bool { return true }
collectMultiAccountLinks = func(ctx context.Context, d *QuarkUCShare, file model.Obj, args model.LinkArgs) []*model.Link {
return []*model.Link{
{URL: "https://cdn.example.com/account1/fid", Concurrency: 4, PartSize: 512 << 10},
{URL: "https://cdn.example.com/account2/fid", Concurrency: 4, PartSize: 512 << 10},
{URL: "https://cdn.example.com/account3/fid", Concurrency: 4, PartSize: 512 << 10},
}
}
t.Cleanup(func() {
quarkUCShareLinkCache = origCache
resolveQuarkUCShareLink = origResolver
resolveShareDirectLink = origDirect
})

d := &QuarkUCShare{Addition: Addition{ShareToken: "share-token"}, config: driver.Config{Name: "QuarkShare"}}
file := &model.Object{ID: "fid-fidtoken-pid", Name: "big.mp4"}

link, err := d.Link(context.Background(), file, model.LinkArgs{})
if err != nil {
t.Fatalf("link: %v", err)
}
if link == nil {
t.Fatal("nil link")
}
if link.URL != "https://cdn.example.com/account1/fid" {
t.Fatalf("expected primary=account1, got %s", link.URL)
}
if len(link.MultiSource) != 3 {
t.Fatalf("expected 3 MultiSource, got %d", len(link.MultiSource))
}
// Concurrency 不在 Link 放大(multiSourceRangeReader 内部按 N 放大且带上限),保持 primary 原值。
if link.Concurrency != 4 {
t.Fatalf("expected Concurrency unchanged=4, got %d", link.Concurrency)
}
}

func TestBindRequestDriverUsesSelectedStorageForRequestAndTempDir(t *testing.T) {
selected := &quark.QuarkOrUC{TempDirId: "temp-dir-a"}
other := &quark.QuarkOrUC{TempDirId: "temp-dir-b"}
Expand Down
Loading
Loading