feat: add qBittorrent API key setup support

Expose qBittorrent WebUI API Key fields in settings and setup so 5.2 users can connect without requiring username/password.

Refs jxxghp/MoviePilot#5724
This commit is contained in:
jxxghp
2026-05-07 07:41:05 +08:00
parent e3e22aebd9
commit f3dbc4afad
6 changed files with 49 additions and 5 deletions

View File

@@ -346,11 +346,23 @@ onUnmounted(() => {
prepend-inner-icon="mdi-server" prepend-inner-icon="mdi-server"
/> />
</VCol> </VCol>
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.config.apikey"
type="password"
:label="t('downloader.apiKey')"
:hint="t('downloader.qbittorrentApiKeyHint')"
persistent-hint
active
prepend-inner-icon="mdi-key-variant"
/>
</VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VTextField
v-model="downloaderInfo.config.username" v-model="downloaderInfo.config.username"
:label="t('downloader.username')" :label="t('downloader.username')"
:hint="t('downloader.username')" :hint="t('downloader.username')"
:disabled="!!downloaderInfo.config.apikey"
persistent-hint persistent-hint
active active
prepend-inner-icon="mdi-account" prepend-inner-icon="mdi-account"
@@ -362,6 +374,7 @@ onUnmounted(() => {
type="password" type="password"
:label="t('downloader.password')" :label="t('downloader.password')"
:hint="t('downloader.password')" :hint="t('downloader.password')"
:disabled="!!downloaderInfo.config.apikey"
persistent-hint persistent-hint
active active
prepend-inner-icon="mdi-lock" prepend-inner-icon="mdi-lock"

View File

@@ -107,6 +107,7 @@ export interface ValidationErrorState {
downloader: { downloader: {
name: boolean name: boolean
host: boolean host: boolean
apikey: boolean
username: boolean username: boolean
password: boolean password: boolean
} }
@@ -277,6 +278,7 @@ const validationErrors = ref<ValidationErrorState>({
downloader: { downloader: {
name: false, name: false,
host: false, host: false,
apikey: false,
username: false, username: false,
password: false, password: false,
}, },
@@ -466,6 +468,7 @@ export function useSetupWizard() {
validationErrors.value.downloader = { validationErrors.value.downloader = {
name: false, name: false,
host: false, host: false,
apikey: false,
username: false, username: false,
password: false, password: false,
} }
@@ -548,9 +551,18 @@ export function useSetupWizard() {
} }
// 根据下载器类型验证其他必输项 // 根据下载器类型验证其他必输项
if ( if (wizardData.value.downloader.type === 'qbittorrent') {
wizardData.value.downloader.type === 'qbittorrent' const hasApiKey = !!wizardData.value.downloader.config?.apikey?.trim()
|| wizardData.value.downloader.type === 'transmission' if (!hasApiKey && !wizardData.value.downloader.config?.username?.trim()) {
errors.push(t('downloader.usernameRequired'))
validationErrors.value.downloader.username = true
}
if (!hasApiKey && !wizardData.value.downloader.config?.password?.trim()) {
errors.push(t('downloader.passwordRequired'))
validationErrors.value.downloader.password = true
}
} else if (
wizardData.value.downloader.type === 'transmission'
|| wizardData.value.downloader.type === 'rtorrent' || wizardData.value.downloader.type === 'rtorrent'
) { ) {
if (!wizardData.value.downloader.config?.username?.trim()) { if (!wizardData.value.downloader.config?.username?.trim()) {

View File

@@ -2933,8 +2933,10 @@ export default {
rtorrentHostHint: 'HTTP: http://ip:port/RPC2 or SCGI: scgi://ip:port', rtorrentHostHint: 'HTTP: http://ip:port/RPC2 or SCGI: scgi://ip:port',
default: 'Default', default: 'Default',
host: 'Host', host: 'Host',
apiKey: 'API Key',
username: 'Username', username: 'Username',
password: 'Password', password: 'Password',
qbittorrentApiKeyHint: 'For qBittorrent 5.2+, you can use the WebUI API Key directly. When set, API Key auth is preferred.',
category: 'Auto Category Management', category: 'Auto Category Management',
sequentail: 'Sequential Download', sequentail: 'Sequential Download',
force_resume: 'Force Resume', force_resume: 'Force Resume',

View File

@@ -2886,8 +2886,10 @@ export default {
rtorrentHostHint: 'HTTP: http://ip:port/RPC2 或 SCGI: scgi://ip:port', rtorrentHostHint: 'HTTP: http://ip:port/RPC2 或 SCGI: scgi://ip:port',
default: '默认', default: '默认',
host: '地址', host: '地址',
apiKey: 'API Key',
username: '用户名', username: '用户名',
password: '密码', password: '密码',
qbittorrentApiKeyHint: 'qBittorrent 5.2+ 可直接使用 WebUI API Key填写后将优先使用 API Key 登录。',
category: '自动分类管理', category: '自动分类管理',
sequentail: '顺序下载', sequentail: '顺序下载',
force_resume: '强制继续', force_resume: '强制继续',

View File

@@ -2888,8 +2888,10 @@ export default {
enabled: '啟用', enabled: '啟用',
default: '預設', default: '預設',
host: '地址', host: '地址',
apiKey: 'API Key',
username: '用戶名', username: '用戶名',
password: '密碼', password: '密碼',
qbittorrentApiKeyHint: 'qBittorrent 5.2+ 可直接使用 WebUI API Key填寫後將優先使用 API Key 登入。',
category: '自動分類管理', category: '自動分類管理',
sequentail: '順序下載', sequentail: '順序下載',
force_resume: '強制繼續', force_resume: '強制繼續',

View File

@@ -104,6 +104,17 @@ const { wizardData, selectDownloader, validationErrors } = useSetupWizard()
required required
/> />
</VCol> </VCol>
<VCol cols="12" md="6">
<VTextField
v-model="wizardData.downloader.config.apikey"
type="password"
:label="t('downloader.apiKey')"
:hint="t('downloader.qbittorrentApiKeyHint')"
persistent-hint
active
prepend-inner-icon="mdi-key-variant"
/>
</VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VTextField
v-model="wizardData.downloader.config.username" v-model="wizardData.downloader.config.username"
@@ -111,10 +122,11 @@ const { wizardData, selectDownloader, validationErrors } = useSetupWizard()
:hint="t('downloader.username')" :hint="t('downloader.username')"
:error="validationErrors.downloader.username" :error="validationErrors.downloader.username"
:error-messages="validationErrors.downloader.username ? [t('downloader.usernameRequired')] : []" :error-messages="validationErrors.downloader.username ? [t('downloader.usernameRequired')] : []"
:disabled="!!wizardData.downloader.config.apikey"
persistent-hint persistent-hint
active active
prepend-inner-icon="mdi-account" prepend-inner-icon="mdi-account"
required :required="!wizardData.downloader.config.apikey"
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
@@ -125,10 +137,11 @@ const { wizardData, selectDownloader, validationErrors } = useSetupWizard()
:hint="t('downloader.password')" :hint="t('downloader.password')"
:error="validationErrors.downloader.password" :error="validationErrors.downloader.password"
:error-messages="validationErrors.downloader.password ? [t('downloader.passwordRequired')] : []" :error-messages="validationErrors.downloader.password ? [t('downloader.passwordRequired')] : []"
:disabled="!!wizardData.downloader.config.apikey"
persistent-hint persistent-hint
active active
prepend-inner-icon="mdi-lock" prepend-inner-icon="mdi-lock"
required :required="!wizardData.downloader.config.apikey"
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">