feat:目录设置UI

This commit is contained in:
jxxghp
2024-05-22 18:01:53 +08:00
parent 6a3afa4240
commit 5ddc955805
11 changed files with 756 additions and 694 deletions

View File

@@ -831,3 +831,23 @@ export interface SystemNotification {
// 通知时间
date: string
}
// 下载目录/媒体库目录
export interface MediaDirectory {
// 类型 download/library
type?: string
// 别名
name?: string
// 路径
path?: string
// 媒体类型 电影/电视剧
media_type?: string
// 媒体类别 动画电影/国产剧
category?: string
// 刮削媒体信息
scrape?: boolean
// 自动二级分类,未指定类别时自动分类
auto_category?: boolean
// 优先级
priority?: number
}

View File

@@ -0,0 +1,92 @@
<script lang="ts" setup>
import type { MediaDirectory } from '@/api/types'
import { VTextField } from 'vuetify/lib/components/index.mjs'
// 输入参数
const props = defineProps({
type: String, // download/library
directory: {
type: Object as PropType<MediaDirectory>,
required: true, // 必填参数
},
categories: {
type: Object as PropType<{ [key: string]: any }>,
required: true,
},
width: String,
height: String,
})
// 类型下拉字典
const typeItems = [
{ title: '全部', value: '' },
{ title: '电影', value: '电影' },
{ title: '电视剧', value: '电视剧' },
{ title: '动漫', value: '动漫' },
]
// 定义触发的自定义事件
const emit = defineEmits(['close', 'changed'])
// 按钮点击
function onClose() {
emit('close')
}
// 根据选中的媒体类型,获取对应的媒体类别
const getCategories = computed(() => {
const default_value = [{ title: '全部', value: '' }]
if (!props.categories || !props.categories[props.directory?.media_type ?? '']) return default_value
return default_value.concat(props.categories[props.directory.media_type ?? ''])
})
</script>
<template>
<VCard variant="tonal" :width="props.width" :height="props.height">
<DialogCloseBtn @click="onClose" />
<VCardItem>
<VTextField
v-model="props.directory.name"
variant="underlined"
label="别名"
class="me-20 text-high-emphasis font-weight-bold"
/>
<span class="absolute top-3 right-12">
<IconBtn>
<VIcon class="cursor-move" icon="mdi-drag" />
</IconBtn>
</span>
</VCardItem>
<VCardText>
<VForm>
<VRow>
<VCol>
<VTextField v-model="props.directory.path" variant="underlined" label="路径" />
</VCol>
</VRow>
<VRow>
<VCol cols="4">
<VSelect
v-model="props.directory.media_type"
variant="underlined"
:items="typeItems"
label="媒体类型"
@update:modelValue="props.directory.category = ''"
/>
</VCol>
<VCol>
<VSelect v-model="props.directory.category" variant="underlined" :items="getCategories" label="媒体类别" />
</VCol>
</VRow>
<VRow>
<VCol v-if="!props.directory.category || props.directory.category === ''">
<VSwitch v-model="props.directory.auto_category" label="自动分类"></VSwitch>
</VCol>
<VCol v-if="type === 'library'">
<VSwitch v-model="props.directory.scrape" label="刮削元数据"></VSwitch>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</template>

View File

@@ -9,6 +9,7 @@ import AccountSettingSearch from '@/views/setting/AccountSettingSearch.vue'
import AccountSettingSubscribe from '@/views/setting/AccountSettingSubscribe.vue'
import AccountSettingService from '@/views/setting/AccountSettingService.vue'
import AccountSettingSystem from '@/views/setting/AccountSettingSystem.vue'
import AccountSettingDirectory from '@/views/setting/AccountSettingDirectory.vue'
const route = useRoute()
@@ -22,10 +23,15 @@ const tabs = [
tab: 'account',
},
{
title: '系统',
icon: 'mdi-cog',
title: '连接',
icon: 'mdi-server-network',
tab: 'system',
},
{
title: '目录',
icon: 'mdi-folder',
tab: 'directory',
},
{
title: '站点',
icon: 'mdi-web',
@@ -81,13 +87,20 @@ const tabs = [
</transition>
</VWindowItem>
<!-- 系统 -->
<!-- 连接 -->
<VWindowItem value="system">
<transition name="fade-slide" appear>
<AccountSettingSystem />
</transition>
</VWindowItem>
<!-- 目录 -->
<VWindowItem value="directory">
<transition name="fade-slide" appear>
<AccountSettingDirectory />
</transition>
</VWindowItem>
<!-- 站点 -->
<VWindowItem value="site">
<transition name="fade-slide" appear>
@@ -122,12 +135,14 @@ const tabs = [
<AccountSettingNotification />
</transition>
</VWindowItem>
<!-- 词表 -->
<VWindowItem value="words">
<transition name="fade-slide" appear>
<AccountSettingWords />
</transition>
</VWindowItem>
<!-- 关于 -->
<VWindowItem value="about">
<transition name="fade-slide" appear>

View File

@@ -0,0 +1,318 @@
<!-- eslint-disable sonarjs/no-duplicate-string -->
<script lang="ts" setup>
import { useToast } from 'vue-toast-notification'
import draggable from 'vuedraggable'
import { VRow } from 'vuetify/lib/components/index.mjs'
import api from '@/api'
import { MediaDirectory } from '@/api/types'
import MediaDirectoryCard from '@/components/cards/MediaDirectoryCard.vue'
// 媒体库设置项
const transferSettings = ref({
TRANSFER_TYPE: 'copy',
OVERWRITE_MODE: 'size',
})
// 转移方式字典
const transferTypeItems = [
{ title: '硬链接', value: 'link' },
{ title: '复制', value: 'copy' },
{ title: '移动', value: 'move' },
{ title: '软链接', value: 'softlink' },
{ title: 'rclone复制', value: 'rclone_copy' },
{ title: 'rclone移动', value: 'rclone_move' },
]
// 覆盖模式字典
const overwriteModeItems = [
{ title: '从不覆盖', value: 'never' },
{ title: '按大小覆盖', value: 'size' },
{ title: '总是覆盖', value: 'always' },
{ title: '仅保留最新版本', value: 'latest' },
]
// 所有下载目录
const downloadDirectories = ref<MediaDirectory[]>([])
// 所有媒体库目录
const libraryDirectories = ref<MediaDirectory[]>([])
// 二级分类策略
const mediaCategories = ref<{ [key: string]: any }>({})
// 提示框
const $toast = useToast()
// 加载媒体库设置
async function loadTransferSettings() {
try {
const result: { [key: string]: any } = await api.get('system/env')
if (result.success) {
const { TRANSFER_TYPE, OVERWRITE_MODE } = result.data
transferSettings.value = {
TRANSFER_TYPE,
OVERWRITE_MODE,
}
}
} catch (error) {
console.log(error)
}
}
// 调用API保存媒体设置
async function saveTransferSetting() {
try {
const result: { [key: string]: any } = await api.post('system/env', transferSettings.value)
if (result.success) $toast.success('保存媒体库设置成功')
else $toast.error('保存媒体库设置失败!')
} catch (error) {
console.log(error)
}
}
// 移动结束
function orderDownloadCards() {
// 更新所有目录的优先级
downloadDirectories.value.forEach((item, index) => {
item.priority = index
})
}
// 移动结束
function orderLibraryCards() {
// 更新所有目录的优先级
libraryDirectories.value.forEach((item, index) => {
item.priority = index
})
}
// 关闭目录卡片
function libraryCardClose(name: string) {
libraryDirectories.value = libraryDirectories.value.filter(item => item.name !== name)
}
// 关闭下载卡片
function downloadCardClose(name: string) {
downloadDirectories.value = downloadDirectories.value.filter(item => item.name !== name)
}
// 查询下载目录
async function loadDownloadDirectories() {
try {
const result: { [key: string]: any } = await api.get('system/setting/DownloadDirectories')
if (result.success && result.data?.value) {
downloadDirectories.value = result.data.value
}
} catch (error) {
console.log(error)
}
}
// 保存下载目录
async function saveDownloadDirectories() {
orderDownloadCards()
try {
const value = downloadDirectories.value.map(item => {
return {
name: item.name,
path: item.path,
media_type: item.media_type,
category: item.category,
auto_category: item.auto_category,
priority: item.priority,
}
})
const result: { [key: string]: any } = await api.post('system/setting/DownloadDirectories', value)
if (result.success) $toast.success('下载目录设置保存成功!')
} catch (e) {
console.error('保存下载目录设置失败')
}
}
// 添加下载目录
function addDownloadDirectory() {
downloadDirectories.value.push({
name: `下载目录${downloadDirectories.value.length + 1}`,
path: '',
media_type: '全部',
category: '',
})
}
// 查询媒体库目录
async function loadLibraryDirectories() {
try {
const result: { [key: string]: any } = await api.get('system/setting/LibraryDirectories')
if (result.success && result.data?.value) {
libraryDirectories.value = result.data.value
}
} catch (error) {
console.log(error)
}
}
// 保存媒体库目录
async function saveLibraryDirectories() {
orderLibraryCards()
try {
const value = libraryDirectories.value.map(item => {
return {
name: item.name,
path: item.path,
media_type: item.media_type,
category: item.category,
auto_category: item.auto_category,
scrape: item.scrape,
priority: item.priority,
}
})
const result: { [key: string]: any } = await api.post('system/setting/LibraryDirectories', value)
if (result.success) $toast.success('媒体库目录设置保存成功!')
} catch (e) {
console.error('保存媒体库目录设置失败')
}
}
// 添加媒体库目录
function addLibraryDirectory() {
libraryDirectories.value.push({
name: `媒体库目录${libraryDirectories.value.length + 1}`,
path: '',
media_type: '全部',
category: '',
scrape: true,
})
}
// 调用API查询自动分类配置
async function loadMediaCategories() {
try {
mediaCategories.value = await api.get('media/category')
} catch (error) {
console.log(error)
}
}
// 加载数据
onMounted(() => {
loadTransferSettings()
loadMediaCategories()
loadDownloadDirectories()
loadLibraryDirectories()
})
</script>
<template>
<VRow>
<VCol cols="12">
<VCard>
<VCardItem>
<VCardTitle>下载目录</VCardTitle>
<VCardSubtitle>设置下载目录路径和分类按顺序依次匹配使用</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
v-model="downloadDirectories"
handle=".cursor-move"
item-key="pri"
tag="div"
@end="orderDownloadCards"
:component-data="{ 'class': 'grid gap-3 grid-directory-card' }"
>
<template #item="{ element }">
<MediaDirectoryCard
type="download"
:directory="element"
:categories="mediaCategories"
@close="downloadCardClose(element.name)"
/>
</template>
</draggable>
</VCardText>
<VCardText>
<VBtn type="submit" class="me-2" @click="saveDownloadDirectories"> 保存 </VBtn>
<VBtn color="success" variant="tonal" @click="addDownloadDirectory">
<VIcon icon="mdi-plus" />
</VBtn>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard>
<VCardItem>
<VCardTitle>媒体库目录</VCardTitle>
<VCardSubtitle>设置媒体文件整理后存储目录和分类按顺序依次匹配使用</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
v-model="libraryDirectories"
handle=".cursor-move"
item-key="pri"
tag="div"
@end="orderLibraryCards"
:component-data="{ 'class': 'grid gap-3 grid-directory-card' }"
>
<template #item="{ element }">
<MediaDirectoryCard
type="library"
:directory="element"
:categories="mediaCategories"
@close="libraryCardClose(element.name)"
/>
</template>
</draggable>
</VCardText>
<VCardText>
<VBtn type="submit" class="me-2" @click="saveLibraryDirectories"> 保存 </VBtn>
<VBtn color="success" variant="tonal" @click="addLibraryDirectory">
<VIcon icon="mdi-plus" />
</VBtn>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard>
<VCardItem>
<VCardTitle>整理模式</VCardTitle>
<VCardSubtitle>设置文件整理方式和偏好</VCardSubtitle>
</VCardItem>
<VCardText>
<VForm>
<VRow>
<VCol cols="12" md="6">
<VSelect
v-model="transferSettings.TRANSFER_TYPE"
:items="transferTypeItems"
label="整理方式"
hint="硬链接需要确保下载目录和媒体库目录不跨盘、不跨共享目录、不分别映射rclone需要手动在容器中完成配置且配置名为`MP`"
/>
</VCol>
<VCol cols="12" md="6">
<VSelect
v-model="transferSettings.OVERWRITE_MODE"
:items="overwriteModeItems"
label="覆盖模式"
hint="从不覆盖:不覆盖已存在的文件;按大小覆盖:大文件将覆盖小文件;总是覆盖:总是覆盖已存在的文件;仅保留最新版本:保留最新版本的文件,删除其它版本的文件"
/>
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn mtype="submit" @click="saveTransferSetting"> 保存 </VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
.grid-directory-card {
grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr));
padding-block-end: 1rem;
}
</style>

View File

@@ -67,8 +67,7 @@ async function loadNotificationSwitchs() {
const result: NotificationSwitch[] = await api.get('message/switchs')
messagemTypes.value = result
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -76,17 +75,11 @@ async function loadNotificationSwitchs() {
// 调用API保存消息开关
async function saveNotificationSwitchs() {
try {
const result: { [key: string]: any } = await api.post(
'message/switchs',
messagemTypes.value,
)
const result: { [key: string]: any } = await api.post('message/switchs', messagemTypes.value)
if (result.success)
$toast.success('保存通知消息设置成功')
else
$toast.error('保存通知消息设置失败!')
}
catch (error) {
if (result.success) $toast.success('保存通知消息设置成功')
else $toast.error('保存通知消息设置失败!')
} catch (error) {
console.log(error)
}
}
@@ -143,8 +136,7 @@ async function loadNotificationSettings() {
VOCECHAT_CHANNEL_ID,
}
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -152,23 +144,17 @@ async function loadNotificationSettings() {
// 调用API保存消息渠道设置
async function saveNotificationSettings() {
try {
const result1: { [key: string]: any } = await api.post(
'system/setting/MESSAGER',
selectedChannels.value.join(','),
)
const result1: { [key: string]: any } = await api.post('system/setting/MESSAGER', selectedChannels.value.join(','))
const result2: { [key: string]: any } = await api.post(
'system/env',
notificationSettings.value,
)
const result2: { [key: string]: any } = await api.post('system/env', notificationSettings.value)
if (result1.success && result2.success) {
$toast.success('保存通知渠道设置成功')
reloadModule()
} else {
$toast.error('保存通知渠道设置失败!')
}
else { $toast.error('保存通知渠道设置失败!') }
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -177,12 +163,9 @@ async function saveNotificationSettings() {
async function reloadModule() {
try {
const result: { [key: string]: any } = await api.get('system/reload')
if (result.success)
$toast.success('重新加载模块成功')
else
$toast.error('重新加载模块失败!')
}
catch (error) {
if (result.success) $toast.success('重新加载模块成功')
else $toast.error('重新加载模块失败!')
} catch (error) {
console.log(error)
}
}
@@ -197,8 +180,11 @@ onMounted(() => {
<template>
<VRow>
<VCol cols="12">
<VCard title="通知渠道">
<VCardSubtitle>只有选中的渠道才会发送消息</VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>通知渠道</VCardTitle>
<VCardSubtitle>只有选中的渠道才会发送消息</VCardSubtitle>
</VCardItem>
<VCardText>
<VForm>
<VRow>
@@ -215,31 +201,14 @@ onMounted(() => {
</VRow>
<VRow>
<VCol>
<VTabs
v-model="messagerTab"
stacked
>
<VTab value="wechat">
微信
</VTab>
<VTab value="telegram">
Telegram
</VTab>
<VTab value="slack">
Slack
</VTab>
<VTab value="synologychat">
SynologyChat
</VTab>
<VTab value="vocechat">
VoceChat
</VTab>
<VTabs v-model="messagerTab" stacked>
<VTab value="wechat"> 微信 </VTab>
<VTab value="telegram"> Telegram </VTab>
<VTab value="slack"> Slack </VTab>
<VTab value="synologychat"> SynologyChat </VTab>
<VTab value="vocechat"> VoceChat </VTab>
</VTabs>
<VWindow
v-model="messagerTab"
class="mt-5 disable-tab-transition"
:touch="false"
>
<VWindow v-model="messagerTab" class="mt-5 disable-tab-transition" :touch="false">
<VWindowItem value="wechat">
<VForm>
<VRow>
@@ -386,10 +355,7 @@ onMounted(() => {
<VForm>
<VRow>
<VCol cols="12" md="4">
<VTextField
v-model="notificationSettings.VOCECHAT_HOST"
label="地址"
/>
<VTextField v-model="notificationSettings.VOCECHAT_HOST" label="地址" />
</VCol>
<VCol cols="12" md="4">
<VTextField
@@ -417,12 +383,7 @@ onMounted(() => {
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn
mtype="submit"
@click="saveNotificationSettings"
>
保存
</VBtn>
<VBtn mtype="submit" @click="saveNotificationSettings"> 保存 </VBtn>
</div>
</VForm>
</VCardText>
@@ -431,36 +392,24 @@ onMounted(() => {
</VRow>
<VRow>
<VCol cols="12">
<VCard title="消息类型">
<VCardSubtitle> 对应消息类型只会发送给选中的消息渠道 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>消息类型</VCardTitle>
<VCardSubtitle>对应消息类型只会发送给选中的消息渠道</VCardSubtitle>
</VCardItem>
<VTable class="text-no-wrap">
<thead>
<tr>
<th scope="col">
消息类型
</th>
<th scope="col">
微信
</th>
<th scope="col">
Telegram
</th>
<th scope="col">
Slack
</th>
<th scope="col">
SynologyChat
</th>
<th scope="col">
VoceChat
</th>
<th scope="col">消息类型</th>
<th scope="col">微信</th>
<th scope="col">Telegram</th>
<th scope="col">Slack</th>
<th scope="col">SynologyChat</th>
<th scope="col">VoceChat</th>
</tr>
</thead>
<tbody>
<tr
v-for="message in messagemTypes"
:key="message.mtype"
>
<tr v-for="message in messagemTypes" :key="message.mtype">
<td>
{{ message.mtype }}
</td>
@@ -481,26 +430,15 @@ onMounted(() => {
</td>
</tr>
<tr v-if="messagemTypes.length === 0">
<td
colspan="6"
class="text-center"
>
没有设置任何通知渠道
</td>
<td colspan="6" class="text-center">没有设置任何通知渠道</td>
</tr>
</tbody>
</VTable>
<VDivider />
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn
mtype="submit"
@click="saveNotificationSwitchs"
>
保存
</VBtn>
<VBtn mtype="submit" @click="saveNotificationSwitchs"> 保存 </VBtn>
</div>
</VForm>
</VCardText>

View File

@@ -280,8 +280,11 @@ onMounted(() => {
<template>
<VRow>
<VCol cols="12">
<VCard title="媒体数据源">
<VCardSubtitle> 设定搜索时展示哪些源的媒体信息</VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>媒体数据源</VCardTitle>
<VCardSubtitle>设定搜索时展示哪些源的媒体信息</VCardSubtitle>
</VCardItem>
<VCardText>
<VRow>
<VCol cols="12" md="6">
@@ -296,17 +299,18 @@ onMounted(() => {
</VCol>
</VRow>
</VCardText>
<VCardItem>
<VCardText>
<VBtn type="submit" @click="saveMediaSourceSetting"> 保存 </VBtn>
</VCardItem>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="搜索站点">
<VCardSubtitle> 只有选中的站点才会在搜索中使用</VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>搜索站点</VCardTitle>
<VCardSubtitle> 只有选中的站点才会在搜索中使用</VCardSubtitle>
</VCardItem>
<VCardText>
<VChipGroup v-model="selectedSites" column multiple>
<VChip
v-for="site in allSites"
@@ -319,38 +323,40 @@ onMounted(() => {
{{ site.name }}
</VChip>
</VChipGroup>
</VCardItem>
<VCardItem>
</VCardText>
<VCardText>
<VBtn type="submit" @click="saveSelectedSites"> 保存 </VBtn>
</VCardItem>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="搜索优先级">
<template #append>
<IconBtn>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem variant="plain" @click="shareRules">
<template #prepend>
<VIcon icon="mdi-share" />
</template>
<VListItemTitle>分享</VListItemTitle>
</VListItem>
<VListItem variant="plain" @click="importCodeDialog = true">
<template #prepend>
<VIcon icon="mdi-import" />
</template>
<VListItemTitle>导入</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<VCardSubtitle> 设置在搜索时默认使用的优先级排序未在优先级中的资源将不在搜索结果中显示 </VCardSubtitle>
<VCard>
<VCardItem>
<template #append>
<IconBtn>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem variant="plain" @click="shareRules">
<template #prepend>
<VIcon icon="mdi-share" />
</template>
<VListItemTitle>分享</VListItemTitle>
</VListItem>
<VListItem variant="plain" @click="importCodeDialog = true">
<template #prepend>
<VIcon icon="mdi-import" />
</template>
<VListItemTitle>导入</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<VCardTitle>搜索优先级</VCardTitle>
<VCardSubtitle>设置在搜索时默认使用的优先级排序未在优先级中的资源将不在搜索结果中显示</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
v-model="filterCards"
handle=".cursor-move"
@@ -369,18 +375,21 @@ onMounted(() => {
/>
</template>
</draggable>
</VCardItem>
<VCardItem>
</VCardText>
<VCardText>
<VBtn type="submit" class="me-2" @click="saveCustomFilters()"> 保存 </VBtn>
<VBtn color="success" variant="tonal" @click="addFilterCard()">
<VIcon icon="mdi-plus" />
</VBtn>
</VCardItem>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="默认过滤规则">
<VCardSubtitle> 设置在搜索时默认使用的过滤规则 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>默认过滤规则</VCardTitle>
<VCardSubtitle>设置在搜索时默认使用的过滤规则</VCardSubtitle>
</VCardItem>
<VCardText>
<VForm>
<VRow>
@@ -421,9 +430,9 @@ onMounted(() => {
</VRow>
</VForm>
</VCardText>
<VCardItem>
<VCardText>
<VBtn type="submit" @click="saveDefaultFilter"> 保存 </VBtn>
</VCardItem>
</VCardText>
</VCard>
</VCol>
</VRow>

View File

@@ -18,8 +18,7 @@ async function loadSchedulerList() {
const res: ScheduleInfo[] = await api.get('dashboard/schedule')
schedulerList.value = res
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -52,8 +51,7 @@ function runCommand(id: string) {
setTimeout(() => {
loadSchedulerList()
}, 1000)
}
catch (e) {
} catch (e) {
console.log(e)
}
}
@@ -77,32 +75,23 @@ onUnmounted(() => {
</script>
<template>
<VCard title="定时作业">
<VCardSubtitle> 包含系统内置服务以及插件提供的服务手动执行不会影响作业正常的时间表 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>定时作业</VCardTitle>
<VCardSubtitle>包含系统内置服务以及插件提供的服务手动执行不会影响作业正常的时间表</VCardSubtitle>
</VCardItem>
<VTable class="text-no-wrap">
<thead>
<tr>
<th scope="col">
提供者
</th>
<th scope="col">
任务名称
</th>
<th scope="col">
任务状态
</th>
<th scope="col">
下一次执行时间
</th>
<th scope="col">提供者</th>
<th scope="col">任务名称</th>
<th scope="col">任务状态</th>
<th scope="col">下一次执行时间</th>
<th scope="col" />
</tr>
</thead>
<tbody>
<tr
v-for="scheduler in schedulerList"
:key="scheduler.id"
>
<tr v-for="scheduler in schedulerList" :key="scheduler.id">
<td>
{{ scheduler.provider }}
</td>
@@ -118,11 +107,7 @@ onUnmounted(() => {
{{ scheduler.next_run }}
</td>
<td>
<VBtn
size="small"
:disabled="scheduler.status === '正在运行'"
@click="runCommand(scheduler.id)"
>
<VBtn size="small" :disabled="scheduler.status === '正在运行'" @click="runCommand(scheduler.id)">
<template #prepend>
<VIcon>mdi-play</VIcon>
</template>
@@ -131,12 +116,7 @@ onUnmounted(() => {
</td>
</tr>
<tr v-if="schedulerList.length === 0">
<td
colspan="4"
class="text-center"
>
没有后台服务
</td>
<td colspan="4" class="text-center">没有后台服务</td>
</tr>
</tbody>
</VTable>

View File

@@ -51,16 +51,12 @@ async function resetSites() {
resetSitesText.value = '正在重置...'
const result: { [key: string]: any } = await api.get('site/reset')
if (result.success)
$toast.success('站点重置成功请等待CookieCloud同步完成')
else
$toast.error('站点重置失败!')
if (result.success) $toast.success('站点重置成功请等待CookieCloud同步完成')
else $toast.error('站点重置失败')
resetSitesDisabled.value = false
resetSitesText.value = '重置站点数据'
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -68,13 +64,10 @@ async function resetSites() {
// 查询种子优先规则
async function queryTorrentPriority() {
try {
const result: { [key: string]: any } = await api.get(
'system/setting/TorrentsPriority',
)
const result: { [key: string]: any } = await api.get('system/setting/TorrentsPriority')
selectedTorrentPriority.value = result.data?.value
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -88,12 +81,9 @@ async function saveTorrentPriority() {
selectedTorrentPriority.value,
)
if (result.success)
$toast.success('优先规则保存成功')
else
$toast.error('优先规则保存失败!')
}
catch (error) {
if (result.success) $toast.success('优先规则保存成功')
else $toast.error('优先规则保存失败!')
} catch (error) {
console.log(error)
}
}
@@ -120,8 +110,7 @@ async function loadCookieCloudSettings() {
COOKIECLOUD_ENABLE_LOCAL,
}
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -129,17 +118,11 @@ async function loadCookieCloudSettings() {
// 调用API保存CookieCloud设置
async function saveCookieCloudetting() {
try {
const result: { [key: string]: any } = await api.post(
'system/env',
cookieCloudSetting.value,
)
const result: { [key: string]: any } = await api.post('system/env', cookieCloudSetting.value)
if (result.success)
$toast.success('保存站点同步设置成功')
else
$toast.error('保存站点同步设置失败!')
}
catch (error) {
if (result.success) $toast.success('保存站点同步设置成功')
else $toast.error('保存站点同步设置失败!')
} catch (error) {
console.log(error)
}
}
@@ -154,8 +137,11 @@ onMounted(() => {
<template>
<VRow>
<VCol cols="12">
<VCard title="站点同步">
<VCardSubtitle> 从CookieCloud快速同步站点数据 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>站点同步</VCardTitle>
<VCardSubtitle>从CookieCloud快速同步站点数据</VCardSubtitle>
</VCardItem>
<VCardText>
<VForm>
<VRow>
@@ -210,19 +196,17 @@ onMounted(() => {
</VRow>
</VForm>
</VCardText>
<VCardItem>
<VBtn
type="submit"
@click="saveCookieCloudetting"
>
保存
</VBtn>
</VCardItem>
<VCardText>
<VBtn type="submit" @click="saveCookieCloudetting"> 保存 </VBtn>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="下载优先规则">
<VCardSubtitle> 按站点或做种数量优先下载 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>下载优先规则</VCardTitle>
<VCardSubtitle>按站点或做种数量优先下载</VCardSubtitle>
</VCardItem>
<VCardText>
<VForm>
<VRow>
@@ -237,14 +221,9 @@ onMounted(() => {
</VRow>
</VForm>
</VCardText>
<VCardItem>
<VBtn
type="submit"
@click="saveTorrentPriority"
>
保存
</VBtn>
</VCardItem>
<VCardText>
<VBtn type="submit" @click="saveTorrentPriority"> 保存 </VBtn>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">

View File

@@ -311,10 +311,12 @@ onMounted(() => {
<template>
<VRow>
<VCol cols="12">
<VCard title="订阅站点">
<VCardSubtitle> 只有选中的站点才会在订阅中使用</VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>订阅站点</VCardTitle>
<VCardSubtitle>只有选中的站点才会在订阅中使用</VCardSubtitle>
</VCardItem>
<VCardText>
<VChipGroup v-model="selectedRssSites" column multiple>
<VChip
v-for="site in allSites"
@@ -327,7 +329,7 @@ onMounted(() => {
{{ site.name }}
</VChip>
</VChipGroup>
</VCardItem>
</VCardText>
<VCardText>
<VForm>
<VRow>
@@ -359,36 +361,39 @@ onMounted(() => {
</VRow>
</VForm>
</VCardText>
<VCardItem>
<VCardText>
<VBtn type="submit" @click="saveSelectedRssSites"> 保存 </VBtn>
</VCardItem>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="订阅优先级">
<template #append>
<IconBtn>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem variant="plain" @click="shareRules('SubscribeFilterRules')">
<template #prepend>
<VIcon icon="mdi-share" />
</template>
<VListItemTitle>分享</VListItemTitle>
</VListItem>
<VListItem variant="plain" @click="importRules('SubscribeFilterRules')">
<template #prepend>
<VIcon icon="mdi-import" />
</template>
<VListItemTitle>导入</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<VCardSubtitle> 设置在正常订阅时默认使用的优先级未在优先级中的资源将不会自动下载</VCardSubtitle>
<VCard>
<VCardItem>
<template #append>
<IconBtn>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem variant="plain" @click="shareRules('SubscribeFilterRules')">
<template #prepend>
<VIcon icon="mdi-share" />
</template>
<VListItemTitle>分享</VListItemTitle>
</VListItem>
<VListItem variant="plain" @click="importRules('SubscribeFilterRules')">
<template #prepend>
<VIcon icon="mdi-import" />
</template>
<VListItemTitle>导入</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<VCardTitle>订阅优先级</VCardTitle>
<VCardSubtitle> 设置在正常订阅时默认使用的优先级未在优先级中的资源将不会自动下载</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
v-model="subscribeFilterCards"
handle=".cursor-move"
@@ -407,40 +412,43 @@ onMounted(() => {
/>
</template>
</draggable>
</VCardItem>
<VCardItem>
</VCardText>
<VCardText>
<VBtn type="submit" class="me-2" @click="saveCustomFilters('SubscribeFilterRules')"> 保存 </VBtn>
<VBtn color="success" variant="tonal" @click="addFilterCard('SubscribeFilterRules')">
<VIcon icon="mdi-plus" />
</VBtn>
</VCardItem>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="洗版优先级">
<template #append>
<IconBtn>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem variant="plain" @click="shareRules('BestVersionFilterRules')">
<template #prepend>
<VIcon icon="mdi-share" />
</template>
<VListItemTitle>分享</VListItemTitle>
</VListItem>
<VListItem variant="plain" @click="importRules('BestVersionFilterRules')">
<template #prepend>
<VIcon icon="mdi-import" />
</template>
<VListItemTitle>导入</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<VCardSubtitle> 设置在订阅洗版时使用的优先级匹配优先级1时洗版完成</VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>洗版优先级</VCardTitle>
<template #append>
<IconBtn>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem variant="plain" @click="shareRules('BestVersionFilterRules')">
<template #prepend>
<VIcon icon="mdi-share" />
</template>
<VListItemTitle>分享</VListItemTitle>
</VListItem>
<VListItem variant="plain" @click="importRules('BestVersionFilterRules')">
<template #prepend>
<VIcon icon="mdi-import" />
</template>
<VListItemTitle>导入</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<VCardSubtitle> 设置在订阅洗版时使用的优先级匹配优先级1时洗版完成</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
v-model="bestVersionFilterCards"
handle=".cursor-move"
@@ -459,18 +467,21 @@ onMounted(() => {
/>
</template>
</draggable>
</VCardItem>
<VCardItem>
</VCardText>
<VCardText>
<VBtn type="submit" class="me-2" @click="saveCustomFilters('BestVersionFilterRules')"> 保存 </VBtn>
<VBtn color="success" variant="tonal" @click="addFilterCard('BestVersionFilterRules')">
<VIcon icon="mdi-plus" />
</VBtn>
</VCardItem>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="默认过滤规则">
<VCardSubtitle> 设置在订阅时默认使用的过滤规则</VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>默认过滤规则</VCardTitle>
<VCardSubtitle> 设置在订阅时默认使用的过滤规则</VCardSubtitle>
</VCardItem>
<VCardText>
<VForm>
<VRow>
@@ -529,9 +540,9 @@ onMounted(() => {
</VRow>
</VForm>
</VCardText>
<VCardItem>
<VCardText>
<VBtn type="submit" @click="saveDefaultFilter"> 保存 </VBtn>
</VCardItem>
</VCardText>
</VCard>
</VCol>
</VRow>

View File

@@ -3,7 +3,6 @@
import { useToast } from 'vue-toast-notification'
import { VRow } from 'vuetify/lib/components/index.mjs'
import api from '@/api'
import { requiredValidator } from '@/@validators'
// 选中的媒体服务器
const selectedMediaServers = ref([])
@@ -17,23 +16,6 @@ const downloaderTab = ref('qbittorrent')
// 媒体服务器选中标签页
const mediaserverTab = ref('emby')
// 媒体库设置项
const mediaSettings = ref({
SCRAP_METADATA: true,
DOWNLOAD_PATH: '',
DOWNLOAD_MOVIE_PATH: '',
DOWNLOAD_TV_PATH: '',
DOWNLOAD_ANIME_PATH: '',
DOWNLOAD_CATEGORY: false,
TRANSFER_TYPE: 'copy',
OVERWRITE_MODE: 'size',
LIBRARY_PATH: '',
LIBRARY_MOVIE_NAME: '',
LIBRARY_TV_NAME: '',
LIBRARY_ANIME_NAME: '',
LIBRARY_CATEGORY: false,
})
// 下载器设置项
const downloaderSettings = ref({
DOWNLOADER_MONITOR: true,
@@ -92,24 +74,6 @@ const MediaServers = [
},
]
// 转移方式字典
const transferTypeItems = [
{ title: '硬链接', value: 'link' },
{ title: '复制', value: 'copy' },
{ title: '移动', value: 'move' },
{ title: '软链接', value: 'softlink' },
{ title: 'rclone复制', value: 'rclone_copy' },
{ title: 'rclone移动', value: 'rclone_move' },
]
// 覆盖模式字典
const overwriteModeItems = [
{ title: '从不覆盖', value: 'never' },
{ title: '按大小覆盖', value: 'size' },
{ title: '总是覆盖', value: 'always' },
{ title: '仅保留最新版本', value: 'latest' },
]
// 媒体库同步周期字典
const syncIntervalItems = [
{ title: '从不', value: 0 },
@@ -123,72 +87,11 @@ const syncIntervalItems = [
// 提示框
const $toast = useToast()
// 加载媒体库设置
async function loadMediaSettings() {
try {
const result: { [key: string]: any } = await api.get('system/env')
if (result.success) {
const {
SCRAP_METADATA,
DOWNLOAD_PATH,
DOWNLOAD_MOVIE_PATH,
DOWNLOAD_TV_PATH,
DOWNLOAD_ANIME_PATH,
DOWNLOAD_CATEGORY,
TRANSFER_TYPE,
OVERWRITE_MODE,
LIBRARY_PATH,
LIBRARY_MOVIE_NAME,
LIBRARY_TV_NAME,
LIBRARY_ANIME_NAME,
LIBRARY_CATEGORY,
} = result.data
mediaSettings.value = {
SCRAP_METADATA,
DOWNLOAD_PATH,
DOWNLOAD_MOVIE_PATH,
DOWNLOAD_TV_PATH,
DOWNLOAD_ANIME_PATH,
DOWNLOAD_CATEGORY,
TRANSFER_TYPE,
OVERWRITE_MODE,
LIBRARY_PATH,
LIBRARY_MOVIE_NAME,
LIBRARY_TV_NAME,
LIBRARY_ANIME_NAME,
LIBRARY_CATEGORY,
}
}
}
catch (error) {
console.log(error)
}
}
// 调用API保存媒体设置
async function saveMediaSetting() {
try {
const result: { [key: string]: any } = await api.post(
'system/env',
mediaSettings.value,
)
if (result.success)
$toast.success('保存媒体库设置成功')
else
$toast.error('保存媒体库设置失败!')
}
catch (error) {
console.log(error)
}
}
// 调用API查询下载器设置
async function loadDownloaderSetting() {
try {
const result1: { [key: string]: any } = await api.get('system/setting/DOWNLOADER')
if (result1.success)
selectedDownloaders.value = result1.data?.value?.split(',')
if (result1.success) selectedDownloaders.value = result1.data?.value?.split(',')
const result2: { [key: string]: any } = await api.get('system/env')
if (result2.success) {
@@ -219,8 +122,7 @@ async function loadDownloaderSetting() {
TR_PASSWORD,
}
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -232,18 +134,15 @@ async function saveDownloaderSetting() {
'system/setting/DOWNLOADER',
selectedDownloaders.value.join(','),
)
const result2: { [key: string]: any } = await api.post(
'system/env',
downloaderSettings.value,
)
const result2: { [key: string]: any } = await api.post('system/env', downloaderSettings.value)
if (result1.success && result2.success) {
$toast.success('保存下载器设置成功')
reloadModule()
} else {
$toast.error('保存下载器设置失败!')
}
else { $toast.error('保存下载器设置失败!') }
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -252,8 +151,7 @@ async function saveDownloaderSetting() {
async function loadMediaServerSetting() {
try {
const result1: { [key: string]: any } = await api.get('system/setting/MEDIASERVER')
if (result1.success)
selectedMediaServers.value = result1.data?.value?.split(',')
if (result1.success) selectedMediaServers.value = result1.data?.value?.split(',')
const result2: { [key: string]: any } = await api.get('system/env')
if (result2.success) {
@@ -284,8 +182,7 @@ async function loadMediaServerSetting() {
PLEX_TOKEN,
}
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -298,18 +195,15 @@ async function saveMediaServerSetting() {
selectedMediaServers.value.join(','),
)
const result2: { [key: string]: any } = await api.post(
'system/env',
mediaServerSettings.value,
)
const result2: { [key: string]: any } = await api.post('system/env', mediaServerSettings.value)
if (result1.success && result2.success) {
$toast.success('保存媒体服务器设置成功')
reloadModule()
} else {
$toast.error('保存媒体服务器设置失败!')
}
else { $toast.error('保存媒体服务器设置失败!') }
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -318,12 +212,9 @@ async function saveMediaServerSetting() {
async function reloadModule() {
try {
const result: { [key: string]: any } = await api.get('system/reload')
if (result.success)
$toast.success('重新加载模块成功')
else
$toast.error('重新加载模块失败!')
}
catch (error) {
if (result.success) $toast.success('重新加载模块成功')
else $toast.error('重新加载模块失败!')
} catch (error) {
console.log(error)
}
}
@@ -332,15 +223,17 @@ async function reloadModule() {
onMounted(() => {
loadDownloaderSetting()
loadMediaServerSetting()
loadMediaSettings()
})
</script>
<template>
<VRow>
<VCol cols="12">
<VCard title="下载器">
<VCardSubtitle>只有选中的第1个下载器才会被默认使用</VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>下载器</VCardTitle>
<VCardSubtitle>只有选中的第1个下载器才会被默认使用</VCardSubtitle>
</VCardItem>
<VCardText>
<VForm>
<VRow>
@@ -373,22 +266,11 @@ onMounted(() => {
</VRow>
<VRow>
<VCol>
<VTabs
v-model="downloaderTab"
stacked
>
<VTab value="qbittorrent">
Qbittorrent
</VTab>
<VTab value="transmission">
Transmission
</VTab>
<VTabs v-model="downloaderTab" stacked>
<VTab value="qbittorrent"> Qbittorrent </VTab>
<VTab value="transmission"> Transmission </VTab>
</VTabs>
<VWindow
v-model="downloaderTab"
class="mt-5 disable-tab-transition"
:touch="false"
>
<VWindow v-model="downloaderTab" class="mt-5 disable-tab-transition" :touch="false">
<VWindowItem value="qbittorrent">
<VForm>
<VRow>
@@ -478,12 +360,7 @@ onMounted(() => {
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn
mtype="submit"
@click="saveDownloaderSetting"
>
保存
</VBtn>
<VBtn mtype="submit" @click="saveDownloaderSetting"> 保存 </VBtn>
</div>
</VForm>
</VCardText>
@@ -492,8 +369,11 @@ onMounted(() => {
</VRow>
<VRow>
<VCol cols="12">
<VCard title="媒体服务器">
<VCardSubtitle>只有选中的媒体服务器才会被默认使用</VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>媒体服务器</VCardTitle>
<VCardSubtitle>只有选中的媒体服务器才会被默认使用</VCardSubtitle>
</VCardItem>
<VCardText>
<VForm>
<VRow>
@@ -526,25 +406,12 @@ onMounted(() => {
</VRow>
<VRow>
<VCol>
<VTabs
v-model="mediaserverTab"
stacked
>
<VTab value="emby">
Emby
</VTab>
<VTab value="jellyfin">
Jellyfin
</VTab>
<VTab value="plex">
Plex
</vtab>
<VTabs v-model="mediaserverTab" stacked>
<VTab value="emby"> Emby </VTab>
<VTab value="jellyfin"> Jellyfin </VTab>
<VTab value="plex"> Plex </VTab>
</VTabs>
<VWindow
v-model="mediaserverTab"
class="mt-5 disable-tab-transition"
:touch="false"
>
<VWindow v-model="mediaserverTab" class="mt-5 disable-tab-transition" :touch="false">
<VWindowItem value="emby">
<VForm>
<VRow>
@@ -640,140 +507,7 @@ onMounted(() => {
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn
mtype="submit"
@click="saveMediaServerSetting"
>
保存
</VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
<VRow>
<VCol cols="12">
<VCard title="媒体库">
<VCardSubtitle>设置下载目录媒体库目录以及整理方式</VCardSubtitle>
<VCardText>
<VForm>
<VRow>
<VCol cols="12" md="6">
<VTextField
v-model="mediaSettings.DOWNLOAD_PATH"
label="下载目录"
:rules="[requiredValidator]"
hint="MoviePilot添加的下载任务的默认保存目录必须设置"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaSettings.DOWNLOAD_MOVIE_PATH"
label="电影下载目录"
hint="为电影设置单独的下载保存目录,不设置则使用下载目录"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaSettings.DOWNLOAD_TV_PATH"
label="电视剧下载目录"
hint="为电视剧设置单独的下载保存目录,不设置则使用下载目录"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaSettings.DOWNLOAD_ANIME_PATH"
label="动漫下载目录"
hint="为动漫设置单独的下载保存目录,不设置则使用下载目录"
/>
</VCol>
<VCol cols="12" md="6">
<VSwitch
v-model="mediaSettings.DOWNLOAD_CATEGORY"
label="下载目录自动分类"
hint="开启后,下载任务保存目录将根据二级分类策略自动分类存放到下载目录的二级子目录中,二级分类策略需要编辑配置文件目录下的`category.yml`文件,插件市场有提供文件编辑插件"
/>
</VCol>
</VRow>
<VRow>
<VCol cols="12" md="6">
<VSelect
v-model="mediaSettings.TRANSFER_TYPE"
:items="transferTypeItems"
label="整理方式"
hint="硬链接需要确保下载目录和媒体库目录不跨盘、不跨共享目录、不分别映射rclone需要手动在容器中完成配置且配置名为`MP`"
/>
</VCol>
<VCol cols="12" md="6">
<VSelect
v-model="mediaSettings.OVERWRITE_MODE"
:items="overwriteModeItems"
label="覆盖模式"
hint="从不覆盖:不覆盖已存在的文件;按大小覆盖:大文件将覆盖小文件;总是覆盖:总是覆盖已存在的文件;仅保留最新版本:保留最新版本的文件,删除其它版本的文件"
/>
</VCol>
<VCol cols="12" md="6">
<VSwitch
v-model="mediaSettings.SCRAP_METADATA"
label="自动刮削媒体信息"
hint="开启后,整理完成后将自动刮削媒体信息,如海报、简介等"
/>
</VCol>
</VRow>
<VRow>
<VCol cols="12" md="6">
<VTextField
v-model="mediaSettings.LIBRARY_PATH"
label="媒体库目录"
placeholder="多个目录使用,分隔"
:rules="[requiredValidator]"
hint="整理完成后的媒体文件存放的根目录,所有整理场景下未设定目的目录时都将整理到该目录下,必须设置"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaSettings.LIBRARY_MOVIE_NAME"
label="电影目录名称"
placeholder="电影"
hint="设置电影的存放一级目录名称,不设置则使用使用`电影`做为目录名称"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaSettings.LIBRARY_TV_NAME"
label="电视剧目录名称"
placeholder="电视剧"
hint="设置电视剧的存放一级目录名称,不设置则使用使用`电视剧`做为目录名称"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="mediaSettings.LIBRARY_ANIME_NAME"
label="动漫目录名称"
placeholder="动漫"
hint="设置动漫的存放一级目录名称,不设置则使用使用`动漫`做为目录名称"
/>
</VCol>
<VCol cols="12" md="6">
<VSwitch
v-model="mediaSettings.LIBRARY_CATEGORY"
label="媒体库目录自动分类"
hint="开启后,整理完成后的媒体文件将根据二级分类策略自动分类存放到媒体库一级目录的二级子目录中,二级分类策略需要编辑配置文件目录下的`category.yml`文件,插件市场有提供文件编辑插件"
/>
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn
mtype="submit"
@click="saveMediaSetting"
>
保存
</VBtn>
<VBtn mtype="submit" @click="saveMediaServerSetting"> 保存 </VBtn>
</div>
</VForm>
</VCardText>

View File

@@ -20,13 +20,10 @@ const transferExcludeWords = ref('')
// 查询已设置的识别词
async function queryCustomIdentifiers() {
try {
const result: { [key: string]: any } = await api.get(
'system/setting/CustomIdentifiers',
)
const result: { [key: string]: any } = await api.get('system/setting/CustomIdentifiers')
customIdentifiers.value = result.data?.value.join('\n')
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -34,13 +31,10 @@ async function queryCustomIdentifiers() {
// 查询已设置的制作组
async function queryCustomReleaseGroups() {
try {
const result: { [key: string]: any } = await api.get(
'system/setting/CustomReleaseGroups',
)
const result: { [key: string]: any } = await api.get('system/setting/CustomReleaseGroups')
customReleaseGroups.value = result.data?.value.join('\n')
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -48,13 +42,10 @@ async function queryCustomReleaseGroups() {
// 查询已设置的自定义占位符
async function queryCustomization() {
try {
const result: { [key: string]: any } = await api.get(
'system/setting/Customization',
)
const result: { [key: string]: any } = await api.get('system/setting/Customization')
customization.value = result.data?.value.join('\n')
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -62,13 +53,10 @@ async function queryCustomization() {
// 查询已设置的屏蔽词
async function queryTransferExcludeWords() {
try {
const result: { [key: string]: any } = await api.get(
'system/setting/TransferExcludeWords',
)
const result: { [key: string]: any } = await api.get('system/setting/TransferExcludeWords')
transferExcludeWords.value = result.data?.value.join('\n')
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -82,12 +70,9 @@ async function saveCustomIdentifiers() {
customIdentifiers.value.split('\n'),
)
if (result.success)
$toast.success('自定义识别词保存成功')
else
$toast.error('自定义识别词保存失败!')
}
catch (error) {
if (result.success) $toast.success('自定义识别词保存成功')
else $toast.error('自定义识别词保存失败!')
} catch (error) {
console.log(error)
}
}
@@ -101,12 +86,9 @@ async function saveCustomReleaseGroups() {
customReleaseGroups.value.split('\n'),
)
if (result.success)
$toast.success('自定义制作组/字幕组保存成功')
else
$toast.error('自定义制作组/字幕组保存失败!')
}
catch (error) {
if (result.success) $toast.success('自定义制作组/字幕组保存成功')
else $toast.error('自定义制作组/字幕组保存失败!')
} catch (error) {
console.log(error)
}
}
@@ -120,12 +102,9 @@ async function saveCustomization() {
customization.value.split('\n'),
)
if (result.success)
$toast.success('自定义占位符保存成功')
else
$toast.error('自定义占位符保存失败!')
}
catch (error) {
if (result.success) $toast.success('自定义占位符保存成功')
else $toast.error('自定义占位符保存失败!')
} catch (error) {
console.log(error)
}
}
@@ -139,12 +118,9 @@ async function saveTransferExcludeWords() {
transferExcludeWords.value.split('\n'),
)
if (result.success)
$toast.success('文件整理屏蔽词保存成功')
else
$toast.error('文件整理屏蔽词保存失败!')
}
catch (error) {
if (result.success) $toast.success('文件整理屏蔽词保存成功')
else $toast.error('文件整理屏蔽词保存失败!')
} catch (error) {
console.log(error)
}
}
@@ -160,104 +136,94 @@ onMounted(() => {
<template>
<VRow>
<VCol cols="12">
<VCard title="自定义识别词">
<VCardSubtitle> 添加规则对种子名或者文件名进行预处理以校正识别 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>自定义识别词</VCardTitle>
<VCardSubtitle> 添加规则对种子名或者文件名进行预处理以校正识别 </VCardSubtitle>
</VCardItem>
<VCardText>
<VTextarea
v-model="customIdentifiers"
auto-grow
placeholder="支持正则表达式,特殊字符需要\转义,一行为一组"
hint="支持正则表达式,特殊字符需要\转义,一行为一组"
/>
</VCardItem>
<VCardItem>
<VAlert
type="info"
variant="tonal"
title="支持的配置格式(注意空格):"
>
</VCardText>
<VCardText>
<VAlert type="info" variant="tonal" title="支持的配置格式(注意空格):">
<span
v-html="`
v-html="
`
屏蔽词<br>
被替换词 => 替换词<br>
前定位词 <> 后定位词 >> 集偏移量EP<br>
被替换词 => 替换词 && 前定位词 <> 后定位词 >> 集偏移量EP<br>
其中替换词支持格式:{[tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID识别其中s、e为季数和集数可选<br>
`"
`
"
/>
</VAlert>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
@click="saveCustomIdentifiers"
>
保存
</VBtn>
</VCardItem>
</VCardText>
<VCardText>
<VBtn type="submit" @click="saveCustomIdentifiers"> 保存 </VBtn>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="自定义制作组/字幕组">
<VCardSubtitle> 添加无法识别的制作组/字幕组。 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>自定义制作组/字幕组</VCardTitle>
<VCardSubtitle> 添加无法识别的制作组/字幕组。 </VCardSubtitle>
</VCardItem>
<VCardText>
<VTextarea
v-model="customReleaseGroups"
auto-grow
placeholder="支持正则表达式,特殊字符需要\转义,一行代表一个制作组/字幕组"
hint="支持正则表达式,特殊字符需要\转义,一行代表一个制作组/字幕组"
/>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
@click="saveCustomReleaseGroups"
>
保存
</VBtn>
</VCardItem>
</VCardText>
<VCardText>
<VBtn type="submit" @click="saveCustomReleaseGroups"> 保存 </VBtn>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="自定义占位符">
<VCardSubtitle> 添加自定义占位符识别正则,重命名格式中添加{customization}使用。 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>自定义占位符</VCardTitle>
<VCardSubtitle> 添加自定义占位符识别正则,重命名格式中添加{customization}使用。 </VCardSubtitle>
</VCardItem>
<VCardText>
<VTextarea
v-model="customization"
auto-grow
placeholder="多个匹配对象请换行分隔,支持正则表达式,特殊字符注意转义"
hint="多个匹配对象请换行分隔,支持正则表达式,特殊字符注意转义"
/>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
@click="saveCustomization"
>
保存
</VBtn>
</VCardItem>
</VCardText>
<VCardText>
<VBtn type="submit" @click="saveCustomization"> 保存 </VBtn>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="文件整理屏蔽词">
<VCardSubtitle> 目录名或文件名中包含屏蔽词时不进行整理。 </VCardSubtitle>
<VCard>
<VCardItem>
<VCardTitle>文件整理屏蔽词</VCardTitle>
<VCardSubtitle> 目录名或文件名中包含屏蔽词时不进行整理。 </VCardSubtitle>
</VCardItem>
<VCardText>
<VTextarea
v-model="transferExcludeWords"
auto-grow
placeholder="支持正则表达式,特殊字符需要\转义,一行代表一个屏蔽词"
hint="支持正则表达式,特殊字符需要\转义,一行代表一个屏蔽词"
/>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
@click="saveTransferExcludeWords"
>
保存
</VBtn>
</VCardItem>
</VCardText>
<VCardText>
<VBtn type="submit" @click="saveTransferExcludeWords"> 保存 </VBtn>
</VCardText>
</VCard>
</VCol>
</VRow>