fix(drivers/pikpak): resolve captcha token expiration issue#2241
fix(drivers/pikpak): resolve captcha token expiration issue#2241outlook84 wants to merge 5 commits intoOpenListTeam:mainfrom
Conversation
- 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.
There was a problem hiding this comment.
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
webto 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
RefreshCTokenCkcallsop.MustSaveDriverStorage(d), but it only mutatesd.Common.CaptchaToken.MustSaveDriverStoragepersists onlyd.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 intoAddition(withignore:"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.
drivers/pikpak_share/util.go
Outdated
| @@ -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)) | |||
| } | |||
There was a problem hiding this comment.
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.
drivers/pikpak_share/driver.go
Outdated
| RefreshCTokenCk: func(token string) { | ||
| d.Common.CaptchaToken = token | ||
| op.MustSaveDriverStorage(d) | ||
| }, |
There was a problem hiding this comment.
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.
| 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) | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| 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) | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
drivers/pikpak/util.go
Outdated
| 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)) | ||
| } |
There was a problem hiding this comment.
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.
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 / 检查清单
我已阅读 CONTRIBUTING 文档。
go fmtor prettier.我已使用
go fmt或 prettier 格式化提交的代码。我已为此 PR 添加了适当的标签(如无权限或需要的标签不存在,请在描述中说明,管理员将后续处理)。
我已在适当情况下使用"Request review"功能请求相关代码作者进行审查。
我已相应更新了相关仓库(若适用)。