Skip to content

fix(drivers/pikpak): resolve captcha token expiration issue#2241

Open
outlook84 wants to merge 5 commits intoOpenListTeam:mainfrom
outlook84:dev
Open

fix(drivers/pikpak): resolve captcha token expiration issue#2241
outlook84 wants to merge 5 commits intoOpenListTeam:mainfrom
outlook84:dev

Conversation

@outlook84
Copy link

@outlook84 outlook84 commented Mar 21, 2026

Description / 描述

主要参考 Rclone PikPak 驱动代码,重构了 token 生命周期管理。

一、 新增了 captcha token 的过期时间记录,并在每次请求前先检查 token 是否仍然有效。如果已经过期,就先刷新 token,再继续请求。对于服务端直接返回 “captcha token 已失效” 的场景,也不再只是简单重试,而是会先清空本地缓存的 token 和过期状态,再重新申请新的 token,避免旧状态残留。

二、移除了已无人维护的 android / web / pc 多平台分支,只保留当前实际使用的 Web 配置。对于旧配置,如果只是历史上的 web 值,会自动迁移清理;如果 android 或 pc 这样的旧 profile,则会明确报错,要求重新创建存储。

三、把一些原本暴露在配置里的内部状态字段改成了内部使用,比如 platform、refresh_token、captcha_token、device_id 这类字段不再作为正常用户配置项处理,用户目前只需填写用户名与密码。

四、默认开启 PreferProxy: true,解决播放器进度条拖动后视频卡住的问题。

Motivation and Context / 背景

这个 PR 主要修复了 pikpak 和 pikpak_share 两个驱动里 captcha token 的生命周期处理问题。

之前的问题是:驱动虽然会保存 captcha token,但没有明确跟踪它什么时候过期,结果在 token 失效后,后续请求仍可能继续带着旧 token 去访问接口,导致请求失败。

Closes #XXXX

Relates to #XXXX

#2108

How Has This Been Tested? / 测试

原来 captcha token 几分钟内就会失效,现在经过几个小时以及停止后间隔半小时再启动,都可以正常工作。

Checklist / 检查清单

  • I have read the CONTRIBUTING document.
    我已阅读 CONTRIBUTING 文档。
  • I have formatted my code with go fmt or prettier.
    我已使用 go fmtprettier 格式化提交的代码。
  • I have added appropriate labels to this PR (or mentioned needed labels in the description if lacking permissions).
    我已为此 PR 添加了适当的标签(如无权限或需要的标签不存在,请在描述中说明,管理员将后续处理)。
  • I have requested review from relevant code authors using the "Request review" feature when applicable.
    我已在适当情况下使用"Request review"功能请求相关代码作者进行审查。
  • I have updated the repository accordingly (If it’s needed).
    我已相应更新了相关仓库(若适用)。

  - Fix incorrect captcha token lifecycle handling in pikpak and pikpak_share, preventing requests from reusing
    expired tokens.
  - Track captcha token expiry explicitly and validate token freshness before sending requests.
  - Remove legacy multi-platform PikPak branches and keep the active Web profile only.
  - Enable PreferProxy for both pikpak and pikpak_share.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the PikPak and PikPakShare drivers’ captcha token lifecycle handling to avoid failures caused by using expired captcha tokens, while also simplifying legacy platform/profile handling and adjusting driver defaults to improve playback behavior.

Changes:

  • Track captcha token expiry and proactively refresh tokens (plus reset local token state when server reports token invalid).
  • Remove legacy android/pc/web profile branching (auto-migrate historical web to default; error out on other legacy profiles).
  • Hide internal state fields from user-facing config (platform/refresh_token/captcha_token/device_id) and default PreferProxy: true.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
drivers/pikpak_share/util.go Adds captcha expiry tracking + refresh locking; updates request flow and device ID generation.
drivers/pikpak_share/types.go Adds helper to compute captcha token expiry timestamp from expires_in.
drivers/pikpak_share/meta.go Hides legacy config fields and enables PreferProxy by default.
drivers/pikpak_share/driver.go Migrates/removes legacy platform profiles; switches to generated device IDs.
drivers/pikpak/util.go Adds captcha expiry tracking, refresh locking, authorization ensuring, and device ID generation.
drivers/pikpak/types.go Adds helper to compute captcha token expiry timestamp from expires_in.
drivers/pikpak/meta.go Hides internal config fields and enables PreferProxy by default.
drivers/pikpak/driver.go Removes legacy platform profiles; switches to generated device IDs and new auth flow.
Comments suppressed due to low confidence (1)

drivers/pikpak/driver.go:49

  • RefreshCTokenCk calls op.MustSaveDriverStorage(d), but it only mutates d.Common.CaptchaToken. MustSaveDriverStorage persists only d.Addition, so this write is effectively a no-op while still causing a DB update each captcha refresh. Either remove the save call, or also persist the token/expiry into Addition (with ignore:"true") before saving.
			RefreshCTokenCk: func(token string) {
				d.Common.CaptchaToken = token
				op.MustSaveDriverStorage(d)
			},

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +187 to +211
@@ -199,6 +188,10 @@ func (c *Common) SetCaptchaToken(captchaToken string) {
c.CaptchaToken = captchaToken
}

func (c *Common) SetCaptchaExpiry(expiry time.Time) {
c.CaptchaExpiry = expiry
}

func (c *Common) SetDeviceID(deviceID string) {
c.DeviceID = deviceID
}
@@ -207,6 +200,16 @@ func (c *Common) GetCaptchaToken() string {
return c.CaptchaToken
}

func (c *Common) HasValidCaptchaToken() bool {
if c.CaptchaToken == "" {
return false
}
if c.CaptchaExpiry.IsZero() {
return true
}
return time.Now().Before(c.CaptchaExpiry.Add(-10 * time.Second))
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Common introduces captchaMu, but CaptchaToken/CaptchaExpiry are still read and written without synchronization (request() checks HasValidCaptchaToken and sets X-Captcha-Token while refresh updates the fields). Because storage driver instances are shared across concurrent requests, this is a Go data race. Lock (or use atomics) around all token/expiry reads and writes; a RWMutex + locking inside GetCaptchaToken/HasValidCaptchaToken/setters is usually simplest.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed in cc3ddb3

Comment on lines 36 to 39
RefreshCTokenCk: func(token string) {
d.Common.CaptchaToken = token
op.MustSaveDriverStorage(d)
},
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RefreshCTokenCk triggers op.MustSaveDriverStorage(d) but only updates d.Common.CaptchaToken. Since MustSaveDriverStorage saves just Addition (and PikPakShare's Addition doesn’t include captcha fields), this causes unnecessary DB updates without persisting anything. Consider removing the save, or explicitly persisting needed internal fields into Addition before saving.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed in cc3ddb3

Comment on lines +60 to +75
func genDeviceID() string {
base := []byte("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
random := make([]byte, len(base))
if _, err := rand.Read(random); err != nil {
return utils.GetMD5EncodeStr(fmt.Sprintf("%d", time.Now().UnixNano()))
}
for i, char := range base {
switch char {
case 'x':
base[i] = "0123456789abcdef"[random[i]&0x0f]
case 'y':
base[i] = "0123456789abcdef"[random[i]&0x03|0x08]
}
}
return string(base)
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

genDeviceID() is duplicated (same implementation exists in drivers/pikpak_share/util.go). Duplicated generation logic risks drift over time; consider extracting a shared helper (e.g., under pkg/utils or drivers/base) and reusing it in both drivers.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked whether this should be deduplicated, but for now I think keeping it duplicated is the better tradeoff. The logic is small, only used in two stable places, and extracting it would make the control flow less direct.

Comment on lines +43 to +58
func genDeviceID() string {
base := []byte("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
random := make([]byte, len(base))
if _, err := rand.Read(random); err != nil {
return utils.GetMD5EncodeStr(fmt.Sprintf("%d", time.Now().UnixNano()))
}
for i, char := range base {
switch char {
case 'x':
base[i] = "0123456789abcdef"[random[i]&0x0f]
case 'y':
base[i] = "0123456789abcdef"[random[i]&0x03|0x08]
}
}
return string(base)
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

genDeviceID() duplicates the implementation in drivers/pikpak/util.go. To reduce maintenance burden and avoid subtle future differences, consider extracting this into a shared utility and calling it from both drivers.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked whether this should be deduplicated, but for now I think keeping it duplicated is the better tradeoff. The logic is small, only used in two stable places, and extracting it would make the control flow less direct.

Comment on lines +302 to +322
func (c *Common) SetCaptchaToken(captchaToken string) {
c.CaptchaToken = captchaToken
}

func (c *Common) SetCaptchaExpiry(expiry time.Time) {
c.CaptchaExpiry = expiry
}

func (c *Common) GetCaptchaToken() string {
return c.CaptchaToken
}

func (c *Common) HasValidCaptchaToken() bool {
if c.CaptchaToken == "" {
return false
}
if c.CaptchaExpiry.IsZero() {
return true
}
return time.Now().Before(c.CaptchaExpiry.Add(-10 * time.Second))
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Common adds captchaMu to serialize refresh, but reads/writes of CaptchaToken/CaptchaExpiry (e.g., in SetCaptchaToken, SetCaptchaExpiry, GetCaptchaToken, HasValidCaptchaToken) are not protected. Since driver instances are stored globally and can be used concurrently, this introduces a real Go data race between refresh/update and request/header reads. Consider guarding all accesses with a (RW)mutex (or atomics), and avoid double-locking inside refreshCaptchaToken by using internal unlocked helpers or an RWMutex with consistent lock ordering.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed in cc3ddb3

@xrgzs xrgzs changed the title fix: 修复 PikPak 驱动 captcha token 过期问题 fix(drivers/pikpak): resolve captcha token expiration issue Mar 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants