mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-07 06:02:58 +08:00
feat: 完成绿联媒体服务前端接入与展示优化
This commit is contained in:
@@ -80,6 +80,10 @@ export const mediaServerOptions = [
|
||||
value: 'trimemedia',
|
||||
title: i18n.global.t('setting.system.trimeMedia'),
|
||||
},
|
||||
{
|
||||
value: 'ugreen',
|
||||
title: i18n.global.t('setting.system.ugreen'),
|
||||
},
|
||||
]
|
||||
|
||||
export const mediaServerDict = mediaServerOptions.reduce((dict, item) => {
|
||||
|
||||
@@ -885,8 +885,8 @@ export interface MediaStatistic {
|
||||
movie_count: number
|
||||
// 电视剧总数
|
||||
tv_count: number
|
||||
// 电视剧总集数
|
||||
episode_count: number
|
||||
// 电视剧总集数,未获取时为 null
|
||||
episode_count: number | null
|
||||
// 用户数量
|
||||
user_count: number
|
||||
}
|
||||
@@ -1134,7 +1134,7 @@ export interface StorageConf {
|
||||
export interface MediaServerConf {
|
||||
// 名称
|
||||
name: string
|
||||
// 类型 emby/jellyfin/plex
|
||||
// 类型 emby/jellyfin/plex/trimemedia/ugreen
|
||||
type: string
|
||||
// 配置
|
||||
config: { [key: string]: any }
|
||||
|
||||
BIN
src/assets/images/logos/ugreen.jpg
Normal file
BIN
src/assets/images/logos/ugreen.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MediaServerPlayItem } from '@/api/types'
|
||||
import noImage from '@images/no-image.jpeg'
|
||||
import { openMediaServerWithAutoDetect } from '@/utils/appDeepLink'
|
||||
// 输入参数
|
||||
const props = defineProps({
|
||||
@@ -10,12 +11,18 @@ const props = defineProps({
|
||||
|
||||
// 图片是否加载完成
|
||||
const imageLoaded = ref(false)
|
||||
const imageLoadError = ref(false)
|
||||
|
||||
// 图片加载完成响应
|
||||
function imageLoadHandler() {
|
||||
imageLoaded.value = true
|
||||
}
|
||||
|
||||
// 图片加载失败响应
|
||||
function imageErrorHandler() {
|
||||
imageLoadError.value = true
|
||||
}
|
||||
|
||||
// 跳转播放
|
||||
async function goPlay() {
|
||||
if (props.media?.link) {
|
||||
@@ -26,6 +33,7 @@ async function goPlay() {
|
||||
// 计算图片地址
|
||||
const getImgUrl = computed(() => {
|
||||
const image = props.media?.image || ''
|
||||
if (!image || imageLoadError.value) return noImage
|
||||
let url = `${import.meta.env.VITE_API_BASE_URL}system/img/0?imgurl=${encodeURIComponent(image)}`
|
||||
const use_cookies = props.media?.use_cookies
|
||||
if (use_cookies) {
|
||||
@@ -50,7 +58,7 @@ const getImgUrl = computed(() => {
|
||||
@click="goPlay"
|
||||
>
|
||||
<template #image>
|
||||
<VImg :src="getImgUrl" aspect-ratio="2/3" cover @load="imageLoadHandler">
|
||||
<VImg :src="getImgUrl" aspect-ratio="2/3" cover @load="imageLoadHandler" @error="imageErrorHandler">
|
||||
<template #placeholder>
|
||||
<div class="w-full h-full">
|
||||
<VSkeletonLoader class="object-cover aspect-w-3 aspect-h-2" />
|
||||
|
||||
@@ -33,6 +33,7 @@ function imageLoadHandler() {
|
||||
// 图片加载错误
|
||||
function imageErrorHandler() {
|
||||
imageError.value = true
|
||||
imgUrl.value = getDefaultImage()
|
||||
}
|
||||
|
||||
// 默认图片
|
||||
@@ -41,6 +42,7 @@ function getDefaultImage() {
|
||||
else if (props.media?.server_type === 'emby') return emby
|
||||
else if (props.media?.server_type === 'jellyfin') return jellyfin
|
||||
else if (props.media?.server_type === 'trimemedia') return getLogoUrl('trimemedia')
|
||||
else if (props.media?.server_type === 'ugreen') return getLogoUrl('ugreen')
|
||||
else return plex
|
||||
}
|
||||
|
||||
@@ -53,7 +55,7 @@ async function goPlay() {
|
||||
|
||||
// 生成图片代理路径
|
||||
function getImgUrl(url: string, use_cookies?: boolean) {
|
||||
if (!url) return getDefaultImage()
|
||||
if (!url || imageError.value) return getDefaultImage()
|
||||
let imgurl = `${import.meta.env.VITE_API_BASE_URL}system/img/0?imgurl=${encodeURIComponent(url)}`
|
||||
if (use_cookies) {
|
||||
imgurl += `&use_cookies=${encodeURIComponent(use_cookies)}`
|
||||
@@ -64,7 +66,7 @@ function getImgUrl(url: string, use_cookies?: boolean) {
|
||||
// 根据多张图片生成媒体库封面
|
||||
async function drawImages(imageList: string[], use_cookies?: boolean) {
|
||||
// 图片
|
||||
const IMAGES = imageList
|
||||
const IMAGES = [...imageList]
|
||||
if (IMAGES.length === 0) return getDefaultImage()
|
||||
|
||||
// 为所有图片添加system/img前缀
|
||||
|
||||
@@ -61,6 +61,12 @@ const librariesOptions = ref<{ title: string; value: string | undefined }[]>([
|
||||
},
|
||||
])
|
||||
|
||||
const ugreenScanModeOptions = computed(() => [
|
||||
{ title: t('mediaserver.scanModeOptions.newAndModified'), value: 'new_and_modified' },
|
||||
{ title: t('mediaserver.scanModeOptions.supplementMissing'), value: 'supplement_missing' },
|
||||
{ title: t('mediaserver.scanModeOptions.fullOverride'), value: 'full_override' },
|
||||
])
|
||||
|
||||
// 媒体服务器详情弹窗
|
||||
const mediaServerInfoDialog = ref(false)
|
||||
|
||||
@@ -77,6 +83,12 @@ function openMediaServerInfoDialog() {
|
||||
loadLibrary(props.mediaserver.name)
|
||||
// 深复制
|
||||
mediaServerInfo.value = cloneDeep(props.mediaserver)
|
||||
if (mediaServerInfo.value.type === 'ugreen') {
|
||||
mediaServerInfo.value.config = mediaServerInfo.value.config || {}
|
||||
}
|
||||
if (mediaServerInfo.value.type === 'ugreen' && !mediaServerInfo.value.config.scan_mode) {
|
||||
mediaServerInfo.value.config.scan_mode = 'supplement_missing'
|
||||
}
|
||||
mediaServerInfoDialog.value = true
|
||||
if (!props.mediaserver.sync_libraries) {
|
||||
mediaServerInfo.value.sync_libraries = ['all']
|
||||
@@ -110,6 +122,8 @@ const getIcon = computed(() => {
|
||||
return getLogoUrl('jellyfin')
|
||||
case 'trimemedia':
|
||||
return getLogoUrl('trimemedia')
|
||||
case 'ugreen':
|
||||
return getLogoUrl('ugreen')
|
||||
case 'plex':
|
||||
return getLogoUrl('plex')
|
||||
default:
|
||||
@@ -424,6 +438,85 @@ onMounted(() => {
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-else-if="mediaServerInfo.type == 'ugreen'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="mediaServerInfo.name"
|
||||
:label="t('common.name')"
|
||||
:placeholder="t('mediaserver.nameRequired')"
|
||||
:hint="t('mediaserver.serverAlias')"
|
||||
persistent-hint
|
||||
active
|
||||
prepend-inner-icon="mdi-label"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="mediaServerInfo.config.host"
|
||||
:label="t('mediaserver.host')"
|
||||
:placeholder="t('mediaserver.hostPlaceholder')"
|
||||
:hint="t('mediaserver.hostHint')"
|
||||
persistent-hint
|
||||
active
|
||||
prepend-inner-icon="mdi-server"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<VTextField
|
||||
v-model="mediaServerInfo.config.play_host"
|
||||
:label="t('mediaserver.playHost')"
|
||||
:placeholder="t('mediaserver.playHostPlaceholder')"
|
||||
:hint="t('mediaserver.playHostHint')"
|
||||
persistent-hint
|
||||
active
|
||||
prepend-inner-icon="mdi-play-network"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="mediaServerInfo.config.username"
|
||||
:label="t('mediaserver.username')"
|
||||
active
|
||||
prepend-inner-icon="mdi-account"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
type="password"
|
||||
v-model="mediaServerInfo.config.password"
|
||||
:label="t('mediaserver.password')"
|
||||
active
|
||||
prepend-inner-icon="mdi-lock"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<VAutocomplete
|
||||
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"
|
||||
prepend-inner-icon="mdi-library"
|
||||
@click:append-inner="loadLibrary(mediaServerInfo.name)"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="mediaServerInfo.config.scan_mode"
|
||||
:label="t('mediaserver.scanMode')"
|
||||
:items="ugreenScanModeOptions"
|
||||
:hint="t('mediaserver.scanModeHint')"
|
||||
persistent-hint
|
||||
active
|
||||
prepend-inner-icon="mdi-radar"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-else-if="mediaServerInfo.type == 'plex'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
|
||||
@@ -188,6 +188,7 @@ export function useSetupWizard() {
|
||||
'jellyfin': 'JellyfinModule',
|
||||
'plex': 'PlexModule',
|
||||
'trimemedia': 'TrimeMediaModule',
|
||||
'ugreen': 'UgreenModule',
|
||||
},
|
||||
// 通知映射
|
||||
notification: {
|
||||
@@ -405,7 +406,7 @@ export function useSetupWizard() {
|
||||
errors.push(t('mediaserver.tokenRequired'))
|
||||
validationErrors.value.mediaServer.token = true
|
||||
}
|
||||
} else if (wizardData.value.mediaServer.type === 'trimemedia') {
|
||||
} else if (wizardData.value.mediaServer.type === 'trimemedia' || wizardData.value.mediaServer.type === 'ugreen') {
|
||||
if (!wizardData.value.mediaServer.config?.username?.trim()) {
|
||||
errors.push(t('mediaserver.usernameRequired'))
|
||||
validationErrors.value.mediaServer.username = true
|
||||
|
||||
@@ -46,6 +46,7 @@ export default {
|
||||
unsubscribe: 'Unsubscribe',
|
||||
media: 'Media',
|
||||
unknown: 'Unknown',
|
||||
notFetched: 'Not Fetched',
|
||||
notice: 'Notice',
|
||||
itemsPerPage: 'Items per page',
|
||||
pageText: '{0}-{1} of {2}',
|
||||
@@ -314,7 +315,8 @@ export default {
|
||||
settingTabs: {
|
||||
system: {
|
||||
title: 'System',
|
||||
description: 'Basic settings, downloaders (Qbittorrent, Transmission), media servers (Emby, Jellyfin, Plex)',
|
||||
description:
|
||||
'Basic settings, downloaders (Qbittorrent, Transmission), media servers (Emby, Jellyfin, Plex, TrimeMedia, Ugreen)',
|
||||
},
|
||||
directory: {
|
||||
title: 'Storage & Directories',
|
||||
@@ -1341,6 +1343,7 @@ export default {
|
||||
emby: 'Emby',
|
||||
jellyfin: 'Jellyfin',
|
||||
plex: 'Plex',
|
||||
ugreen: 'Ugreen',
|
||||
reloadSuccess: 'System configuration has taken effect',
|
||||
reloadFailed: 'Failed to reload system!',
|
||||
auxAuthEnable: 'User Auxiliary Authentication',
|
||||
@@ -2853,6 +2856,13 @@ export default {
|
||||
password: 'Password',
|
||||
syncLibraries: 'Sync Libraries',
|
||||
syncLibrariesHint: 'Only selected libraries will be synchronized',
|
||||
scanMode: 'Scan Mode',
|
||||
scanModeHint: 'Applies to full-library and targeted refresh: New & Modified / Supplement Missing / Full Override',
|
||||
scanModeOptions: {
|
||||
newAndModified: 'New & Modified',
|
||||
supplementMissing: 'Supplement Missing',
|
||||
fullOverride: 'Full Override',
|
||||
},
|
||||
hostRequired: 'Host cannot be empty',
|
||||
apiKeyRequired: 'API Key cannot be empty',
|
||||
tokenRequired: 'Token cannot be empty',
|
||||
@@ -3160,7 +3170,8 @@ export default {
|
||||
title: 'Media Server',
|
||||
description: 'Configure media server',
|
||||
info: 'Media Server Configuration',
|
||||
infoDesc: 'Configure media server for media library management, can choose Emby, Jellyfin or Plex etc.',
|
||||
infoDesc:
|
||||
'Configure media server for media library management, can choose Emby, Jellyfin, Plex, TrimeMedia or Ugreen.',
|
||||
type: 'Media Server Type',
|
||||
typeHint: 'Select the type of media server to use',
|
||||
name: 'Server Name',
|
||||
|
||||
@@ -46,6 +46,7 @@ export default {
|
||||
unsubscribe: '取消订阅',
|
||||
media: '媒体',
|
||||
unknown: '未知',
|
||||
notFetched: '未获取',
|
||||
notice: '注意',
|
||||
itemsPerPage: '每页条数',
|
||||
pageText: '{0}-{1} 共 {2} 条',
|
||||
@@ -313,7 +314,7 @@ export default {
|
||||
settingTabs: {
|
||||
system: {
|
||||
title: '系统',
|
||||
description: '基础设置、下载器(Qbittorrent、Transmission)、媒体服务器(Emby、Jellyfin、Plex)',
|
||||
description: '基础设置、下载器(Qbittorrent、Transmission)、媒体服务器(Emby、Jellyfin、Plex、飞牛影视、绿联影视)',
|
||||
},
|
||||
directory: {
|
||||
title: '存储 & 目录',
|
||||
@@ -1336,6 +1337,7 @@ export default {
|
||||
emby: 'Emby',
|
||||
jellyfin: 'Jellyfin',
|
||||
plex: 'Plex',
|
||||
ugreen: '绿联影视',
|
||||
reloadSuccess: '系统配置已生效',
|
||||
reloadFailed: '重载系统失败!',
|
||||
auxAuthEnable: '用户辅助认证',
|
||||
@@ -2817,6 +2819,13 @@ export default {
|
||||
password: '密码',
|
||||
syncLibraries: '同步媒体库',
|
||||
syncLibrariesHint: '只有选中的媒体库才会被同步',
|
||||
scanMode: '扫描模式',
|
||||
scanModeHint: '用于全库刷新和按库刷新:新添加和修改 / 补充缺失 / 覆盖扫描',
|
||||
scanModeOptions: {
|
||||
newAndModified: '新添加和修改',
|
||||
supplementMissing: '补充缺失',
|
||||
fullOverride: '覆盖扫描',
|
||||
},
|
||||
nameExists: '【{name}】已存在,请替换为其他名称',
|
||||
hostRequired: '地址不能为空',
|
||||
apiKeyRequired: 'API密钥不能为空',
|
||||
@@ -3123,7 +3132,7 @@ export default {
|
||||
title: '媒体服务器',
|
||||
description: '配置媒体服务器',
|
||||
info: '媒体服务器配置说明',
|
||||
infoDesc: '配置媒体服务器用于媒体库管理,可选择Emby、Jellyfin或Plex等',
|
||||
infoDesc: '配置媒体服务器用于媒体库管理,可选择Emby、Jellyfin、Plex、飞牛影视或绿联影视',
|
||||
type: '媒体服务器类型',
|
||||
typeHint: '选择要使用的媒体服务器类型',
|
||||
name: '服务器名称',
|
||||
|
||||
@@ -46,6 +46,7 @@ export default {
|
||||
unsubscribe: '取消訂閱',
|
||||
media: '媒體',
|
||||
unknown: '未知',
|
||||
notFetched: '未獲取',
|
||||
notice: '注意',
|
||||
itemsPerPage: '每頁條數',
|
||||
pageText: '{0}-{1} 共 {2} 條',
|
||||
@@ -313,7 +314,7 @@ export default {
|
||||
settingTabs: {
|
||||
system: {
|
||||
title: '系統',
|
||||
description: '基礎設置、下載器(Qbittorrent、Transmission)、媒體服務器(Emby、Jellyfin、Plex)',
|
||||
description: '基礎設置、下載器(Qbittorrent、Transmission)、媒體服務器(Emby、Jellyfin、Plex、飛牛影視、綠聯影視)',
|
||||
},
|
||||
directory: {
|
||||
title: '存儲 & 目錄',
|
||||
@@ -1337,6 +1338,7 @@ export default {
|
||||
emby: 'Emby',
|
||||
jellyfin: 'Jellyfin',
|
||||
plex: 'Plex',
|
||||
ugreen: '綠聯影視',
|
||||
reloadSuccess: '系統配置已生效',
|
||||
reloadFailed: '重載系統失敗!',
|
||||
auxAuthEnable: '用戶輔助認證',
|
||||
@@ -2823,6 +2825,13 @@ export default {
|
||||
password: '密碼',
|
||||
syncLibraries: '同步媒體庫',
|
||||
syncLibrariesHint: '只有選中的媒體庫才會被同步',
|
||||
scanMode: '掃描模式',
|
||||
scanModeHint: '用於全庫刷新和按庫刷新:新添加和修改 / 補充缺失 / 覆蓋掃描',
|
||||
scanModeOptions: {
|
||||
newAndModified: '新添加和修改',
|
||||
supplementMissing: '補充缺失',
|
||||
fullOverride: '覆蓋掃描',
|
||||
},
|
||||
nameExists: '【{name}】已存在,請替換為其他名稱',
|
||||
},
|
||||
bangumi: {
|
||||
@@ -3124,7 +3133,7 @@ export default {
|
||||
title: '媒體伺服器',
|
||||
description: '設定媒體伺服器',
|
||||
info: '媒體伺服器設定說明',
|
||||
infoDesc: '設定媒體伺服器用於媒體庫管理,可選擇Emby、Jellyfin或Plex等',
|
||||
infoDesc: '設定媒體伺服器用於媒體庫管理,可選擇Emby、Jellyfin、Plex、飛牛影視或綠聯影視',
|
||||
type: '媒體伺服器類型',
|
||||
typeHint: '選擇要使用的媒體伺服器類型',
|
||||
name: '伺服器名稱',
|
||||
|
||||
@@ -11,6 +11,7 @@ import embyLogo from '@/assets/images/logos/emby.png'
|
||||
import jellyfinLogo from '@/assets/images/logos/jellyfin.png'
|
||||
import plexLogo from '@/assets/images/logos/plex.png'
|
||||
import trimemediaLogo from '@/assets/images/logos/trimemedia.png'
|
||||
import ugreenLogo from '@/assets/images/logos/ugreen.jpg'
|
||||
import wechatLogo from '@/assets/images/logos/wechat.png'
|
||||
import telegramLogo from '@/assets/images/logos/telegram.webp'
|
||||
import slackLogo from '@/assets/images/logos/slack.webp'
|
||||
@@ -40,6 +41,7 @@ const logoMap: Record<string, string> = {
|
||||
jellyfin: jellyfinLogo,
|
||||
plex: plexLogo,
|
||||
trimemedia: trimemediaLogo,
|
||||
ugreen: ugreenLogo,
|
||||
wechat: wechatLogo,
|
||||
telegram: telegramLogo,
|
||||
slack: slackLogo,
|
||||
|
||||
@@ -28,7 +28,7 @@ async function loadMediaStatistic() {
|
||||
},
|
||||
{
|
||||
title: t('dashboard.episodes'),
|
||||
stats: res.episode_count.toLocaleString(),
|
||||
stats: res.episode_count == null ? t('common.notFetched') : res.episode_count.toLocaleString(),
|
||||
icon: 'mdi-television-classic',
|
||||
color: 'warning',
|
||||
},
|
||||
|
||||
@@ -15,6 +15,20 @@ const librariesOptions = ref<{ title: string; value: string | undefined }[]>([
|
||||
},
|
||||
])
|
||||
|
||||
const ugreenScanModeOptions = computed(() => [
|
||||
{ title: t('mediaserver.scanModeOptions.newAndModified'), value: 'new_and_modified' },
|
||||
{ title: t('mediaserver.scanModeOptions.supplementMissing'), value: 'supplement_missing' },
|
||||
{ title: t('mediaserver.scanModeOptions.fullOverride'), value: 'full_override' },
|
||||
])
|
||||
|
||||
function ensureUgreenScanMode() {
|
||||
if (wizardData.value.mediaServer.type !== 'ugreen') return
|
||||
wizardData.value.mediaServer.config = wizardData.value.mediaServer.config || {}
|
||||
if (!wizardData.value.mediaServer.config.scan_mode) {
|
||||
wizardData.value.mediaServer.config.scan_mode = 'supplement_missing'
|
||||
}
|
||||
}
|
||||
|
||||
// 调用API查询媒体库
|
||||
async function loadLibrary(server: string) {
|
||||
try {
|
||||
@@ -42,6 +56,7 @@ async function loadLibrary(server: string) {
|
||||
// 选择媒体服务器并自动加载媒体库
|
||||
async function selectMediaServerWithLibrary(type: string) {
|
||||
selectMediaServer(type)
|
||||
ensureUgreenScanMode()
|
||||
// 如果选择了媒体服务器类型,自动加载媒体库
|
||||
if (type && wizardData.value.mediaServer.name) {
|
||||
await loadLibrary(wizardData.value.mediaServer.name)
|
||||
@@ -50,6 +65,7 @@ async function selectMediaServerWithLibrary(type: string) {
|
||||
|
||||
// 组件挂载时检查是否需要加载媒体库
|
||||
onMounted(async () => {
|
||||
ensureUgreenScanMode()
|
||||
// 如果已经有媒体服务器配置,自动加载媒体库
|
||||
if (wizardData.value.mediaServer.type && wizardData.value.mediaServer.name) {
|
||||
await loadLibrary(wizardData.value.mediaServer.name)
|
||||
@@ -60,6 +76,7 @@ onMounted(async () => {
|
||||
watch(
|
||||
() => [wizardData.value.mediaServer.type, wizardData.value.mediaServer.name],
|
||||
async ([type, name]) => {
|
||||
ensureUgreenScanMode()
|
||||
console.log('Media server changed:', { type, name })
|
||||
if (type && name) {
|
||||
await loadLibrary(name)
|
||||
@@ -141,6 +158,19 @@ watch(
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
<VCol cols="12" md="3">
|
||||
<VCard
|
||||
:color="wizardData.mediaServer.type === 'ugreen' ? 'primary' : 'default'"
|
||||
:variant="wizardData.mediaServer.type === 'ugreen' ? 'tonal' : 'outlined'"
|
||||
class="cursor-pointer"
|
||||
@click="selectMediaServerWithLibrary('ugreen')"
|
||||
>
|
||||
<VCardText class="text-center">
|
||||
<VImg :src="getLogoUrl('ugreen')" height="48" width="48" class="mx-auto mb-2" />
|
||||
<div class="text-h6">绿联影视</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</div>
|
||||
</VCol>
|
||||
@@ -380,6 +410,97 @@ watch(
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-else-if="wizardData.mediaServer.type === 'ugreen'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="wizardData.mediaServer.name"
|
||||
:label="t('common.name')"
|
||||
:placeholder="t('mediaserver.nameRequired')"
|
||||
:hint="t('mediaserver.serverAlias')"
|
||||
:error="validationErrors.mediaServer.name"
|
||||
:error-messages="validationErrors.mediaServer.name ? [t('mediaserver.nameRequired')] : []"
|
||||
persistent-hint
|
||||
active
|
||||
prepend-inner-icon="mdi-label"
|
||||
required
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="wizardData.mediaServer.config.host"
|
||||
:label="t('mediaserver.host')"
|
||||
:placeholder="t('mediaserver.hostPlaceholder')"
|
||||
:hint="t('mediaserver.hostHint')"
|
||||
:error="validationErrors.mediaServer.host"
|
||||
:error-messages="validationErrors.mediaServer.host ? [t('mediaserver.hostRequired')] : []"
|
||||
persistent-hint
|
||||
active
|
||||
prepend-inner-icon="mdi-server"
|
||||
required
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<VTextField
|
||||
v-model="wizardData.mediaServer.config.play_host"
|
||||
:label="t('mediaserver.playHost')"
|
||||
:placeholder="t('mediaserver.playHostPlaceholder')"
|
||||
:hint="t('mediaserver.playHostHint')"
|
||||
persistent-hint
|
||||
active
|
||||
prepend-inner-icon="mdi-play-network"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="wizardData.mediaServer.config.username"
|
||||
:label="t('mediaserver.username')"
|
||||
:error="validationErrors.mediaServer.username"
|
||||
:error-messages="validationErrors.mediaServer.username ? [t('mediaserver.usernameRequired')] : []"
|
||||
active
|
||||
prepend-inner-icon="mdi-account"
|
||||
required
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
type="password"
|
||||
v-model="wizardData.mediaServer.config.password"
|
||||
:label="t('mediaserver.password')"
|
||||
:error="validationErrors.mediaServer.password"
|
||||
:error-messages="validationErrors.mediaServer.password ? [t('mediaserver.passwordRequired')] : []"
|
||||
active
|
||||
prepend-inner-icon="mdi-lock"
|
||||
required
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<VAutocomplete
|
||||
v-model="wizardData.mediaServer.sync_libraries"
|
||||
:label="t('mediaserver.syncLibraries')"
|
||||
:items="librariesOptions"
|
||||
chips
|
||||
multiple
|
||||
clearable
|
||||
:hint="t('mediaserver.syncLibrariesHint')"
|
||||
persistent-hint
|
||||
active
|
||||
append-inner-icon="mdi-refresh"
|
||||
prepend-inner-icon="mdi-library"
|
||||
@click:append-inner="loadLibrary(wizardData.mediaServer.name)"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="wizardData.mediaServer.config.scan_mode"
|
||||
:label="t('mediaserver.scanMode')"
|
||||
:items="ugreenScanModeOptions"
|
||||
:hint="t('mediaserver.scanModeHint')"
|
||||
persistent-hint
|
||||
active
|
||||
prepend-inner-icon="mdi-radar"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-else-if="wizardData.mediaServer.type === 'plex'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
|
||||
Reference in New Issue
Block a user