mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-06 16:16:42 +08:00
添加自定义下载器和媒体服务器选项
This commit is contained in:
Vendored
+1
-3
@@ -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']>
|
||||||
|
|||||||
Vendored
-1
@@ -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 |
@@ -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">
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -908,6 +908,7 @@ export default {
|
|||||||
dataDirectory: '/moviepilot',
|
dataDirectory: '/moviepilot',
|
||||||
},
|
},
|
||||||
system: {
|
system: {
|
||||||
|
custom: '自定义',
|
||||||
basicSettings: '基础设置',
|
basicSettings: '基础设置',
|
||||||
basicSettingsDesc: '设置服务器的全局功能。',
|
basicSettingsDesc: '设置服务器的全局功能。',
|
||||||
appDomain: '访问域名',
|
appDomain: '访问域名',
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user