添加自定义下载器和媒体服务器选项

This commit is contained in:
jxxghp
2025-05-01 13:41:02 +08:00
parent f1835dd46c
commit 5327c04e7e
10 changed files with 109 additions and 14 deletions
+1 -3
View File
@@ -328,7 +328,7 @@ declare global {
// for type re-export // for type re-export
declare global { declare global {
// @ts-ignore // @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue') import('vue')
} }
@@ -356,7 +356,6 @@ declare module 'vue' {
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']> readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
readonly createProjection: UnwrapRef<typeof import('@vueuse/math')['createProjection']> readonly createProjection: UnwrapRef<typeof import('@vueuse/math')['createProjection']>
readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']> readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']>
readonly createRef: UnwrapRef<typeof import('@vueuse/core')['createRef']>
readonly createReusableTemplate: UnwrapRef<typeof import('@vueuse/core')['createReusableTemplate']> readonly createReusableTemplate: UnwrapRef<typeof import('@vueuse/core')['createReusableTemplate']>
readonly createSharedComposable: UnwrapRef<typeof import('@vueuse/core')['createSharedComposable']> readonly createSharedComposable: UnwrapRef<typeof import('@vueuse/core')['createSharedComposable']>
readonly createTemplatePromise: UnwrapRef<typeof import('@vueuse/core')['createTemplatePromise']> readonly createTemplatePromise: UnwrapRef<typeof import('@vueuse/core')['createTemplatePromise']>
@@ -491,7 +490,6 @@ declare module 'vue' {
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']> readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']> readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']> readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useCountdown: UnwrapRef<typeof import('@vueuse/core')['useCountdown']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']> readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']> readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
readonly useCssVar: UnwrapRef<typeof import('@vueuse/core')['useCssVar']> readonly useCssVar: UnwrapRef<typeof import('@vueuse/core')['useCssVar']>
-1
View File
@@ -2,7 +2,6 @@
// @ts-nocheck // @ts-nocheck
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {} export {}
/* prettier-ignore */ /* prettier-ignore */
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+19 -3
View File
@@ -6,6 +6,7 @@ import { useToast } from 'vue-toast-notification'
import type { DownloaderInfo } from '@/api/types' import type { DownloaderInfo } from '@/api/types'
import qbittorrent_image from '@images/logos/qbittorrent.png' import qbittorrent_image from '@images/logos/qbittorrent.png'
import transmission_image from '@images/logos/transmission.png' import transmission_image from '@images/logos/transmission.png'
import custom_image from '@images/logos/downloader.png'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@@ -126,7 +127,7 @@ const getIcon = computed(() => {
case 'transmission': case 'transmission':
return transmission_image return transmission_image
default: default:
return qbittorrent_image return custom_image
} }
}) })
@@ -172,10 +173,13 @@ onUnmounted(() => {
/> />
<span class="text-h6">{{ downloader.name }}</span> <span class="text-h6">{{ downloader.name }}</span>
</div> </div>
<div class="mt-1 flex flex-wrap text-sm" v-if="props.downloader.enabled"> <div v-if="downloader.type != 'custom' && props.downloader.enabled" class="mt-1 flex flex-wrap text-sm">
<span class="me-2">{{ `${formatFileSize(upload_rate, 1)}/s ` }}</span> <span class="me-2">{{ `${formatFileSize(upload_rate, 1)}/s ` }}</span>
<span>{{ `${formatFileSize(download_rate, 1)}/s` }}</span> <span>{{ `${formatFileSize(download_rate, 1)}/s` }}</span>
</div> </div>
<div v-else-if="downloader.type == 'custom'" class="mt-1 flex flex-wrap text-sm">
<span class="me-2">自定义下载器</span>
</div>
</div> </div>
<div class="h-20"> <div class="h-20">
<VImg :src="getIcon" cover class="mt-7 me-3" max-width="3rem" min-width="3rem" /> <VImg :src="getIcon" cover class="mt-7 me-3" max-width="3rem" min-width="3rem" />
@@ -278,7 +282,7 @@ onUnmounted(() => {
/> />
</VCol> </VCol>
</VRow> </VRow>
<VRow v-if="downloaderInfo.type == 'transmission'"> <VRow v-else-if="downloaderInfo.type == 'transmission'">
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VTextField
v-model="downloaderInfo.name" v-model="downloaderInfo.name"
@@ -319,6 +323,18 @@ onUnmounted(() => {
/> />
</VCol> </VCol>
</VRow> </VRow>
<VRow v-else>
<VCol cols="12" md="6">
<VTextField
v-model="downloaderInfo.name"
:label="t('downloader.name')"
:placeholder="t('downloader.nameRequired')"
:hint="t('downloader.name')"
persistent-hint
active
/>
</VCol>
</VRow>
</VForm> </VForm>
</VCardText> </VCardText>
<VCardActions class="pt-3"> <VCardActions class="pt-3">
+80 -7
View File
@@ -5,6 +5,7 @@ import emby_image from '@images/logos/emby.png'
import jellyfin_image from '@images/logos/jellyfin.png' import jellyfin_image from '@images/logos/jellyfin.png'
import plex_image from '@images/logos/plex.png' import plex_image from '@images/logos/plex.png'
import trimemedia_image from '@images/logos/trimemedia.png' import trimemedia_image from '@images/logos/trimemedia.png'
import custom_image from '@images/logos/mediaserver.png'
import api from '@/api' import api from '@/api'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@@ -108,8 +109,10 @@ const getIcon = computed(() => {
return jellyfin_image return jellyfin_image
case 'trimemedia': case 'trimemedia':
return trimemedia_image return trimemedia_image
default: case 'plex':
return plex_image return plex_image
default:
return custom_image
} }
}) })
@@ -183,11 +186,14 @@ onMounted(() => {
<VCardText class="flex justify-space-between align-center gap-3"> <VCardText class="flex justify-space-between align-center gap-3">
<div class="align-self-start flex-1"> <div class="align-self-start flex-1">
<div class="text-h6 mb-1">{{ mediaserver.name }}</div> <div class="text-h6 mb-1">{{ mediaserver.name }}</div>
<div class="text-sm mt-5 flex flex-wrap"> <div v-if="mediaserver.type != 'custom' && mediaserver.enabled" class="text-sm mt-5 flex flex-wrap">
<span v-for="item in infoItems" :key="item.title" class="me-2 mb-1"> <span v-for="item in infoItems" :key="item.title" class="me-2 mb-1">
<VIcon rounded :icon="item.avatar" class="me-1" />{{ item.amount }} <VIcon rounded :icon="item.avatar" class="me-1" />{{ item.amount }}
</span> </span>
</div> </div>
<div v-else-if="mediaserver.type == 'custom'" class="text-sm mt-5 flex flex-wrap">
<span class="me-2 mb-1">自定义媒体服务器</span>
</div>
</div> </div>
<VImg :src="getIcon" cover class="mt-7 me-3" max-width="3rem" min-width="3rem" /> <VImg :src="getIcon" cover class="mt-7 me-3" max-width="3rem" min-width="3rem" />
</VCardText> </VCardText>
@@ -243,8 +249,23 @@ onMounted(() => {
active active
/> />
</VCol> </VCol>
<VCol cols="12">
<VSelect
v-model="mediaServerInfo.sync_libraries"
:label="t('mediaserver.syncLibraries')"
:items="librariesOptions"
chips
multiple
clearable
:hint="t('mediaserver.syncLibrariesHint')"
persistent-hint
active
append-inner-icon="mdi-refresh"
@click:append-inner="loadLibrary(mediaServerInfo.name)"
/>
</VCol>
</VRow> </VRow>
<VRow v-if="mediaServerInfo.type == 'jellyfin'"> <VRow v-else-if="mediaServerInfo.type == 'jellyfin'">
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VTextField
v-model="mediaServerInfo.name" v-model="mediaServerInfo.name"
@@ -284,8 +305,23 @@ onMounted(() => {
active active
/> />
</VCol> </VCol>
<VCol cols="12">
<VSelect
v-model="mediaServerInfo.sync_libraries"
:label="t('mediaserver.syncLibraries')"
:items="librariesOptions"
chips
multiple
clearable
:hint="t('mediaserver.syncLibrariesHint')"
persistent-hint
active
append-inner-icon="mdi-refresh"
@click:append-inner="loadLibrary(mediaServerInfo.name)"
/>
</VCol>
</VRow> </VRow>
<VRow v-if="mediaServerInfo.type == 'trimemedia'"> <VRow v-else-if="mediaServerInfo.type == 'trimemedia'">
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VTextField
v-model="mediaServerInfo.name" v-model="mediaServerInfo.name"
@@ -327,8 +363,23 @@ onMounted(() => {
active active
/> />
</VCol> </VCol>
<VCol cols="12">
<VSelect
v-model="mediaServerInfo.sync_libraries"
:label="t('mediaserver.syncLibraries')"
:items="librariesOptions"
chips
multiple
clearable
:hint="t('mediaserver.syncLibrariesHint')"
persistent-hint
active
append-inner-icon="mdi-refresh"
@click:append-inner="loadLibrary(mediaServerInfo.name)"
/>
</VCol>
</VRow> </VRow>
<VRow v-if="mediaServerInfo.type == 'plex'"> <VRow v-else-if="mediaServerInfo.type == 'plex'">
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VTextField
v-model="mediaServerInfo.name" v-model="mediaServerInfo.name"
@@ -368,8 +419,21 @@ onMounted(() => {
active active
/> />
</VCol> </VCol>
</VRow> <VCol cols="12">
<VRow> <VSelect
v-model="mediaServerInfo.sync_libraries"
:label="t('mediaserver.syncLibraries')"
:items="librariesOptions"
chips
multiple
clearable
:hint="t('mediaserver.syncLibrariesHint')"
persistent-hint
active
append-inner-icon="mdi-refresh"
@click:append-inner="loadLibrary(mediaServerInfo.name)"
/>
</VCol>
<VCol cols="12"> <VCol cols="12">
<VSelect <VSelect
v-model="mediaServerInfo.sync_libraries" v-model="mediaServerInfo.sync_libraries"
@@ -386,6 +450,15 @@ onMounted(() => {
/> />
</VCol> </VCol>
</VRow> </VRow>
<VRow v-else>
<VCol cols="12" md="6">
<VTextField
v-model="mediaServerInfo.name"
:label="t('common.name')"
:placeholder="t('mediaserver.nameRequired')"
/>
</VCol>
</VRow>
</VForm> </VForm>
</VCardText> </VCardText>
<VCardActions class="pt-3"> <VCardActions class="pt-3">
+1
View File
@@ -911,6 +911,7 @@ export default {
dataDirectory: '/moviepilot', dataDirectory: '/moviepilot',
}, },
system: { system: {
custom: 'Custom',
basicSettings: 'Basic Settings', basicSettings: 'Basic Settings',
basicSettingsDesc: 'Configure server global functions.', basicSettingsDesc: 'Configure server global functions.',
appDomain: 'Access Domain', appDomain: 'Access Domain',
+1
View File
@@ -908,6 +908,7 @@ export default {
dataDirectory: '/moviepilot', dataDirectory: '/moviepilot',
}, },
system: { system: {
custom: '自定义',
basicSettings: '基础设置', basicSettings: '基础设置',
basicSettingsDesc: '设置服务器的全局功能。', basicSettingsDesc: '设置服务器的全局功能。',
appDomain: '访问域名', appDomain: '访问域名',
+1
View File
@@ -910,6 +910,7 @@ export default {
dataDirectory: '/moviepilot', dataDirectory: '/moviepilot',
}, },
system: { system: {
custom: '自定義',
basicSettings: '基礎設置', basicSettings: '基礎設置',
basicSettingsDesc: '設置服務器的全局功能。', basicSettingsDesc: '設置服務器的全局功能。',
appDomain: '訪問域名', appDomain: '訪問域名',
@@ -541,6 +541,9 @@ onDeactivated(() => {
<VListItem @click="addDownloader('transmission')"> <VListItem @click="addDownloader('transmission')">
<VListItemTitle>{{ t('setting.system.transmission') }}</VListItemTitle> <VListItemTitle>{{ t('setting.system.transmission') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addDownloader('custom')">
<VListItemTitle>{{ t('setting.system.custom') }}</VListItemTitle>
</VListItem>
</VList> </VList>
</VMenu> </VMenu>
</VBtn> </VBtn>
@@ -595,6 +598,9 @@ onDeactivated(() => {
<VListItem @click="addMediaServer('trimemedia')"> <VListItem @click="addMediaServer('trimemedia')">
<VListItemTitle>{{ t('setting.system.trimeMedia') }}</VListItemTitle> <VListItemTitle>{{ t('setting.system.trimeMedia') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addMediaServer('custom')">
<VListItemTitle>{{ t('setting.system.custom') }}</VListItemTitle>
</VListItem>
</VList> </VList>
</VMenu> </VMenu>
</VBtn> </VBtn>