mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-03 14:50:21 +08:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e6be057ca | ||
|
|
af69efa48b | ||
|
|
c551083fa4 | ||
|
|
9767feed29 | ||
|
|
4392818e92 | ||
|
|
8d22bafeb6 | ||
|
|
89ddd1fb78 | ||
|
|
24513fa22b | ||
|
|
cddde0c2a0 | ||
|
|
9c674e0018 | ||
|
|
0c6476d283 | ||
|
|
bf0c529a59 | ||
|
|
877bb4d4a2 | ||
|
|
dc4db0b2b3 | ||
|
|
a738d4a3b9 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "moviepilot",
|
"name": "moviepilot",
|
||||||
"version": "1.6.4",
|
"version": "1.6.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"bin": "dist/service.js",
|
"bin": "dist/service.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import PageRender from '@/components/render/PageRender.vue'
|
|||||||
import { isNullOrEmptyObject } from '@core/utils'
|
import { isNullOrEmptyObject } from '@core/utils'
|
||||||
import noImage from '@images/logos/plugin.png'
|
import noImage from '@images/logos/plugin.png'
|
||||||
import { getDominantColor } from '@/@core/utils/image'
|
import { getDominantColor } from '@/@core/utils/image'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -225,6 +226,13 @@ function visitAuthorPage() {
|
|||||||
window.open(props.plugin?.author_url, '_blank')
|
window.open(props.plugin?.author_url, '_blank')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查看日志URL
|
||||||
|
function openLoggerWindow() {
|
||||||
|
const token = store.state.auth.token
|
||||||
|
const url = `${import.meta.env.VITE_API_BASE_URL}system/logging?token=${token}&length=-1&logfile=plugins/${props.plugin?.id?.toLowerCase()}.log`
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
// 弹出菜单
|
// 弹出菜单
|
||||||
const dropdownItems = ref([
|
const dropdownItems = ref([
|
||||||
{
|
{
|
||||||
@@ -265,9 +273,20 @@ const dropdownItems = ref([
|
|||||||
click: uninstallPlugin,
|
click: uninstallPlugin,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '查看日志',
|
||||||
|
value: 5,
|
||||||
|
show: true,
|
||||||
|
props: {
|
||||||
|
prependIcon: 'mdi-file-document-outline',
|
||||||
|
click: () => {
|
||||||
|
openLoggerWindow()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '作者主页',
|
title: '作者主页',
|
||||||
value: 4,
|
value: 5,
|
||||||
show: true,
|
show: true,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-home-circle-outline',
|
prependIcon: 'mdi-home-circle-outline',
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts" setup>
|
<script lang='ts' setup>
|
||||||
import { useToast } from 'vue-toast-notification'
|
import { useToast } from 'vue-toast-notification'
|
||||||
import SubscribeEditForm from '../form/SubscribeEditForm.vue'
|
import SubscribeEditForm from '../form/SubscribeEditForm.vue'
|
||||||
import { calculateTimeDifference } from '@/@core/utils'
|
import { calculateTimeDifference } from '@/@core/utils'
|
||||||
import { formatSeason } from '@/@core/utils/formatters'
|
import { formatSeason } from '@/@core/utils/formatters'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import type { Subscribe } from '@/api/types'
|
import type { Subscribe } from '@/api/types'
|
||||||
|
import router from '@/router'
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -55,7 +56,7 @@ function getPercentage() {
|
|||||||
return Math.round(
|
return Math.round(
|
||||||
(((props.media?.total_episode ?? 0) - (props.media?.lack_episode ?? 0))
|
(((props.media?.total_episode ?? 0) - (props.media?.lack_episode ?? 0))
|
||||||
/ (props.media?.total_episode ?? 1))
|
/ (props.media?.total_episode ?? 1))
|
||||||
* 100,
|
* 100,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,8 +127,28 @@ const dropdownItems = ref([
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '取消订阅',
|
title: '查看详情',
|
||||||
value: 3,
|
value: 3,
|
||||||
|
props: {
|
||||||
|
prependIcon: 'mdi-open-in-new',
|
||||||
|
click: () => {
|
||||||
|
router.push({
|
||||||
|
path: '/media',
|
||||||
|
query: {
|
||||||
|
mediaid: `${
|
||||||
|
props.media?.tmdbid
|
||||||
|
? `tmdb:${props.media?.tmdbid}`
|
||||||
|
: `douban:${props.media?.doubanid}`
|
||||||
|
}`,
|
||||||
|
type: props.media?.type,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '取消订阅',
|
||||||
|
value: 4,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-trash-can-outline',
|
prependIcon: 'mdi-trash-can-outline',
|
||||||
color: 'error',
|
color: 'error',
|
||||||
@@ -162,7 +183,7 @@ const dropdownItems = ref([
|
|||||||
</template>
|
</template>
|
||||||
<VCardTitle :class="getTextClass()">
|
<VCardTitle :class="getTextClass()">
|
||||||
{{ props.media?.name }}
|
{{ props.media?.name }}
|
||||||
{{ formatSeason(props.media?.season ? props.media?.season.toString() : "") }}
|
{{ formatSeason(props.media?.season ? props.media?.season.toString() : '') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
<template #append>
|
<template #append>
|
||||||
<div class="me-n3">
|
<div class="me-n3">
|
||||||
@@ -252,7 +273,8 @@ const dropdownItems = ref([
|
|||||||
<VIcon
|
<VIcon
|
||||||
icon="mdi-download"
|
icon="mdi-download"
|
||||||
class="me-1"
|
class="me-1"
|
||||||
/> {{ lastUpdateText }}
|
/>
|
||||||
|
{{ lastUpdateText }}
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<VProgressLinear
|
<VProgressLinear
|
||||||
v-if="getPercentage() > 0"
|
v-if="getPercentage() > 0"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import AccountSettingAbout from '@/views/setting/AccountSettingAbout.vue'
|
|||||||
import AccountSettingSearch from '@/views/setting/AccountSettingSearch.vue'
|
import AccountSettingSearch from '@/views/setting/AccountSettingSearch.vue'
|
||||||
import AccountSettingSubscribe from '@/views/setting/AccountSettingSubscribe.vue'
|
import AccountSettingSubscribe from '@/views/setting/AccountSettingSubscribe.vue'
|
||||||
import AccountSettingService from '@/views/setting/AccountSettingService.vue'
|
import AccountSettingService from '@/views/setting/AccountSettingService.vue'
|
||||||
|
import AccountSettingSystem from '@/views/setting/AccountSettingSystem.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -20,6 +21,11 @@ const tabs = [
|
|||||||
icon: 'mdi-account',
|
icon: 'mdi-account',
|
||||||
tab: 'account',
|
tab: 'account',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '系统',
|
||||||
|
icon: 'mdi-cog',
|
||||||
|
tab: 'system',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '站点',
|
title: '站点',
|
||||||
icon: 'mdi-web',
|
icon: 'mdi-web',
|
||||||
@@ -83,6 +89,13 @@ const tabs = [
|
|||||||
</transition>
|
</transition>
|
||||||
</VWindowItem>
|
</VWindowItem>
|
||||||
|
|
||||||
|
<!-- 系统 -->
|
||||||
|
<VWindowItem value="system">
|
||||||
|
<transition name="fade-slide" appear>
|
||||||
|
<AccountSettingSystem />
|
||||||
|
</transition>
|
||||||
|
</VWindowItem>
|
||||||
|
|
||||||
<!-- 站点 -->
|
<!-- 站点 -->
|
||||||
<VWindowItem value="site">
|
<VWindowItem value="site">
|
||||||
<transition name="fade-slide" appear>
|
<transition name="fade-slide" appear>
|
||||||
|
|||||||
@@ -5,25 +5,21 @@ import NoDataFound from '@/components/NoDataFound.vue'
|
|||||||
import PluginAppCard from '@/components/cards/PluginAppCard.vue'
|
import PluginAppCard from '@/components/cards/PluginAppCard.vue'
|
||||||
import PluginCard from '@/components/cards/PluginCard.vue'
|
import PluginCard from '@/components/cards/PluginCard.vue'
|
||||||
|
|
||||||
// 数据列表
|
// 已安装插件列表
|
||||||
const dataList = ref<Plugin[]>([])
|
const dataList = ref<Plugin[]>([])
|
||||||
|
|
||||||
|
// 未安装插件列表
|
||||||
|
const uninstalledList = ref<Plugin[]>([])
|
||||||
|
|
||||||
// 是否刷新过
|
// 是否刷新过
|
||||||
const isRefreshed = ref(false)
|
const isRefreshed = ref(false)
|
||||||
|
|
||||||
|
// APP市场是否加载完成
|
||||||
|
const isAppMarketLoaded = ref(false)
|
||||||
|
|
||||||
// APP市场窗口
|
// APP市场窗口
|
||||||
const PluginAppDialog = ref(false)
|
const PluginAppDialog = ref(false)
|
||||||
|
|
||||||
// 获取已安装的插件列表
|
|
||||||
const getInstalledPluginList = computed(() => {
|
|
||||||
return dataList.value.filter(item => item.installed)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取未安装或者有更新的插件列表
|
|
||||||
const getUninstalledPluginList = computed(() => {
|
|
||||||
return dataList.value.filter(item => !item.installed || item.has_update)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 关闭插件市场窗口
|
// 关闭插件市场窗口
|
||||||
function pluginDialogClose() {
|
function pluginDialogClose() {
|
||||||
PluginAppDialog.value = false
|
PluginAppDialog.value = false
|
||||||
@@ -31,14 +27,19 @@ function pluginDialogClose() {
|
|||||||
|
|
||||||
// 新安装了插件
|
// 新安装了插件
|
||||||
function pluginInstalled() {
|
function pluginInstalled() {
|
||||||
fetchData()
|
fetchInstalledPlugins()
|
||||||
pluginDialogClose()
|
pluginDialogClose()
|
||||||
|
fetchUninstalledPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取插件列表数据
|
// 获取插件列表数据
|
||||||
async function fetchData() {
|
async function fetchInstalledPlugins() {
|
||||||
try {
|
try {
|
||||||
dataList.value = await api.get('plugin/')
|
dataList.value = await api.get('plugin/', {
|
||||||
|
params: {
|
||||||
|
state: 'installed',
|
||||||
|
},
|
||||||
|
})
|
||||||
isRefreshed.value = true
|
isRefreshed.value = true
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
@@ -46,8 +47,26 @@ async function fetchData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取未安装插件列表数据
|
||||||
|
async function fetchUninstalledPlugins() {
|
||||||
|
try {
|
||||||
|
uninstalledList.value = await api.get('plugin/', {
|
||||||
|
params: {
|
||||||
|
state: 'market',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
isAppMarketLoaded.value = true
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 加载时获取数据
|
// 加载时获取数据
|
||||||
onBeforeMount(fetchData)
|
onBeforeMount(() => {
|
||||||
|
fetchInstalledPlugins()
|
||||||
|
fetchUninstalledPlugins()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -63,19 +82,19 @@ onBeforeMount(fetchData)
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="getInstalledPluginList.length > 0"
|
v-if="dataList.length > 0"
|
||||||
class="grid gap-4 grid-plugin-card"
|
class="grid gap-4 grid-plugin-card"
|
||||||
>
|
>
|
||||||
<PluginCard
|
<PluginCard
|
||||||
v-for="data in getInstalledPluginList"
|
v-for="data in dataList"
|
||||||
:key="data.id"
|
:key="data.id"
|
||||||
:plugin="data"
|
:plugin="data"
|
||||||
@remove="fetchData"
|
@remove="fetchInstalledPlugins"
|
||||||
@save="fetchData"
|
@save="fetchInstalledPlugins"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<NoDataFound
|
<NoDataFound
|
||||||
v-if="getInstalledPluginList.length === 0 && isRefreshed"
|
v-if="dataList.length === 0 && isRefreshed"
|
||||||
error-code="404"
|
error-code="404"
|
||||||
error-title="没有安装插件"
|
error-title="没有安装插件"
|
||||||
error-description="点击右下角按钮,前往插件市场安装插件。"
|
error-description="点击右下角按钮,前往插件市场安装插件。"
|
||||||
@@ -121,16 +140,28 @@ onBeforeMount(fetchData)
|
|||||||
</VToolbar>
|
</VToolbar>
|
||||||
</div>
|
</div>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<div class="grid gap-4 grid-plugin-card">
|
<div
|
||||||
|
v-if="!isAppMarketLoaded"
|
||||||
|
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
|
||||||
|
>
|
||||||
|
正在加载插件市场,请稍候...
|
||||||
|
<VProgressCircular
|
||||||
|
v-if="!isAppMarketLoaded"
|
||||||
|
size="48"
|
||||||
|
indeterminate
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="isAppMarketLoaded" class="grid gap-4 grid-plugin-card">
|
||||||
<PluginAppCard
|
<PluginAppCard
|
||||||
v-for="data in getUninstalledPluginList"
|
v-for="data in uninstalledList"
|
||||||
:key="data.id"
|
:key="data.id"
|
||||||
:plugin="data"
|
:plugin="data"
|
||||||
@install="pluginInstalled"
|
@install="pluginInstalled"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<NoDataFound
|
<NoDataFound
|
||||||
v-if="getUninstalledPluginList.length === 0 && isRefreshed"
|
v-if="uninstalledList.length === 0 && isAppMarketLoaded"
|
||||||
error-code="404"
|
error-code="404"
|
||||||
error-title="没有未安装插件"
|
error-title="没有未安装插件"
|
||||||
error-description="所有可用插件均已安装。"
|
error-description="所有可用插件均已安装。"
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ const dropdownItems = ref([
|
|||||||
<VIcon :icon="getIcon(item.value.type || '')" />
|
<VIcon :icon="getIcon(item.value.type || '')" />
|
||||||
</VAvatar>
|
</VAvatar>
|
||||||
<div class="d-flex flex-column ms-1">
|
<div class="d-flex flex-column ms-1">
|
||||||
<span class="d-block whitespace-nowrap text-high-emphasis">
|
<span class="d-block text-high-emphasis">
|
||||||
{{ item.value.title }} {{ item.value.seasons }}{{ item.value.episodes }}
|
{{ item.value.title }} {{ item.value.seasons }}{{ item.value.episodes }}
|
||||||
</span>
|
</span>
|
||||||
<small>{{ item.value.category }}</small>
|
<small>{{ item.value.category }}</small>
|
||||||
|
|||||||
@@ -5,6 +5,52 @@ import type { NotificationSwitch } from '@/api/types'
|
|||||||
|
|
||||||
const messagemTypes = ref<NotificationSwitch[]>([])
|
const messagemTypes = ref<NotificationSwitch[]>([])
|
||||||
|
|
||||||
|
// 选中的消息渠道
|
||||||
|
const selectedChannels = ref([])
|
||||||
|
|
||||||
|
// 消息渠道标签页
|
||||||
|
const messagerTab = ref('wechat')
|
||||||
|
|
||||||
|
// 消息设置
|
||||||
|
const notificationSettings = ref({
|
||||||
|
WECHAT_CORPID: '',
|
||||||
|
WECHAT_APP_SECRET: '',
|
||||||
|
WECHAT_APP_ID: '',
|
||||||
|
WECHAT_PROXY: '',
|
||||||
|
WECHAT_TOKEN: '',
|
||||||
|
WECHAT_ENCODING_AESKEY: '',
|
||||||
|
WECHAT_ADMINS: '',
|
||||||
|
TELEGRAM_TOKEN: '',
|
||||||
|
TELEGRAM_CHAT_ID: '',
|
||||||
|
TELEGRAM_USERS: '',
|
||||||
|
TELEGRAM_ADMINS: '',
|
||||||
|
SLACK_OAUTH_TOKEN: '',
|
||||||
|
SLACK_APP_TOKEN: '',
|
||||||
|
SLACK_CHANNEL: '',
|
||||||
|
SYNOLOGYCHAT_WEBHOOK: '',
|
||||||
|
SYNOLOGYCHAT_TOKEN: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 消息渠道
|
||||||
|
const NotificationChannels = [
|
||||||
|
{
|
||||||
|
title: '微信',
|
||||||
|
value: 'wechat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Telegram',
|
||||||
|
value: 'telegram',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Slack',
|
||||||
|
value: 'slack',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'SynologyChat',
|
||||||
|
value: 'synologychat',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
// 提示框
|
// 提示框
|
||||||
const $toast = useToast()
|
const $toast = useToast()
|
||||||
|
|
||||||
@@ -32,87 +78,365 @@ async function saveNotificationSwitchs() {
|
|||||||
$toast.success('保存通知消息设置成功')
|
$toast.success('保存通知消息设置成功')
|
||||||
else
|
else
|
||||||
$toast.error('保存通知消息设置失败!')
|
$toast.error('保存通知消息设置失败!')
|
||||||
|
|
||||||
// messagemTypes.value = messagemTypes.value
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 调用API查询消息渠道设置
|
||||||
|
async function loadNotificationSettings() {
|
||||||
|
try {
|
||||||
|
const result1: { [key: string]: any } = await api.get('system/setting/MESSAGER')
|
||||||
|
if (result1.success)
|
||||||
|
selectedChannels.value = result1.data?.value?.split(',')
|
||||||
|
|
||||||
|
const result2: { [key: string]: any } = await api.get('system/env')
|
||||||
|
if (result2.success) {
|
||||||
|
const {
|
||||||
|
WECHAT_CORPID,
|
||||||
|
WECHAT_APP_SECRET,
|
||||||
|
WECHAT_APP_ID,
|
||||||
|
WECHAT_PROXY,
|
||||||
|
WECHAT_TOKEN,
|
||||||
|
WECHAT_ENCODING_AESKEY,
|
||||||
|
WECHAT_ADMINS,
|
||||||
|
TELEGRAM_TOKEN,
|
||||||
|
TELEGRAM_CHAT_ID,
|
||||||
|
TELEGRAM_USERS,
|
||||||
|
TELEGRAM_ADMINS,
|
||||||
|
SLACK_OAUTH_TOKEN,
|
||||||
|
SLACK_APP_TOKEN,
|
||||||
|
SLACK_CHANNEL,
|
||||||
|
SYNOLOGYCHAT_WEBHOOK,
|
||||||
|
SYNOLOGYCHAT_TOKEN,
|
||||||
|
} = result2.data
|
||||||
|
notificationSettings.value = {
|
||||||
|
WECHAT_CORPID,
|
||||||
|
WECHAT_APP_SECRET,
|
||||||
|
WECHAT_APP_ID,
|
||||||
|
WECHAT_PROXY,
|
||||||
|
WECHAT_TOKEN,
|
||||||
|
WECHAT_ENCODING_AESKEY,
|
||||||
|
WECHAT_ADMINS,
|
||||||
|
TELEGRAM_TOKEN,
|
||||||
|
TELEGRAM_CHAT_ID,
|
||||||
|
TELEGRAM_USERS,
|
||||||
|
TELEGRAM_ADMINS,
|
||||||
|
SLACK_OAUTH_TOKEN,
|
||||||
|
SLACK_APP_TOKEN,
|
||||||
|
SLACK_CHANNEL,
|
||||||
|
SYNOLOGYCHAT_WEBHOOK,
|
||||||
|
SYNOLOGYCHAT_TOKEN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API保存消息渠道设置
|
||||||
|
async function saveNotificationSettings() {
|
||||||
|
try {
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result1.success && result2.success) {
|
||||||
|
$toast.success('保存通知渠道设置成功')
|
||||||
|
reloadModule()
|
||||||
|
}
|
||||||
|
else { $toast.error('保存通知渠道设置失败!') }
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API接口重新加载模块
|
||||||
|
async function reloadModule() {
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.get('system/reload')
|
||||||
|
if (result.success)
|
||||||
|
$toast.success('重新加载模块成功')
|
||||||
|
else
|
||||||
|
$toast.error('重新加载模块失败!')
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadNotificationSwitchs()
|
loadNotificationSwitchs()
|
||||||
|
loadNotificationSettings()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VCard title="消息通知">
|
<VRow>
|
||||||
<VCardText> 对应消息类型只会发送给选中的消息渠道。 </VCardText>
|
<VCol cols="12">
|
||||||
|
<VCard title="通知渠道">
|
||||||
|
<VCardSubtitle>只有选中的渠道才会发送消息。</VCardSubtitle>
|
||||||
|
<VCardText>
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="selectedChannels"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
:items="NotificationChannels"
|
||||||
|
label="当前使用通知渠道"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</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>
|
||||||
|
</VTabs>
|
||||||
|
<VWindow
|
||||||
|
v-model="messagerTab"
|
||||||
|
class="mt-5 disable-tab-transition"
|
||||||
|
:touch="false"
|
||||||
|
>
|
||||||
|
<VWindowItem value="wechat">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.WECHAT_CORPID"
|
||||||
|
label="企业ID"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.WECHAT_APP_SECRET"
|
||||||
|
label="应用密钥"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.WECHAT_APP_ID"
|
||||||
|
label="应用ID"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.WECHAT_PROXY"
|
||||||
|
label="代理地址"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.WECHAT_TOKEN"
|
||||||
|
label="Token"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.WECHAT_ENCODING_AESKEY"
|
||||||
|
label="EncodingAESKey"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.WECHAT_ADMINS"
|
||||||
|
label="管理员白名单"
|
||||||
|
placeholder="多个用,分隔"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
<VWindowItem value="telegram">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.TELEGRAM_TOKEN"
|
||||||
|
label="Bot Token"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.TELEGRAM_CHAT_ID"
|
||||||
|
label="Chat ID"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.TELEGRAM_USERS"
|
||||||
|
label="用户白名单"
|
||||||
|
placeholder="多个用,分隔"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.TELEGRAM_ADMINS"
|
||||||
|
label="管理员白名单"
|
||||||
|
placeholder="多个用,分隔"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
<VWindowItem value="slack">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="5">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.SLACK_OAUTH_TOKEN"
|
||||||
|
label="Slack Bot User OAuth Token"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="5">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.SLACK_APP_TOKEN"
|
||||||
|
label="Slack App-Level Token"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="2">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.SLACK_CHANNEL"
|
||||||
|
label="频道名称"
|
||||||
|
placeholder="全体"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
<VWindowItem value="synologychat">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.SYNOLOGYCHAT_WEBHOOK"
|
||||||
|
label="Webhook"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.SYNOLOGYCHAT_TOKEN"
|
||||||
|
label="Token"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
</VWindow>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
<VCardText>
|
||||||
|
<VForm @submit.prevent="() => {}">
|
||||||
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
|
<VBtn
|
||||||
|
mtype="submit"
|
||||||
|
@click="saveNotificationSettings"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</VBtn>
|
||||||
|
</div>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12">
|
||||||
|
<VCard title="消息类型">
|
||||||
|
<VCardSubtitle> 对应消息类型只会发送给选中的消息渠道。 </VCardSubtitle>
|
||||||
|
<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>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="message in messagemTypes"
|
||||||
|
:key="message.mtype"
|
||||||
|
>
|
||||||
|
<td>
|
||||||
|
{{ message.mtype }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<VCheckbox v-model="message.wechat" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<VCheckbox v-model="message.telegram" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<VCheckbox v-model="message.slack" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<VCheckbox v-model="message.synologychat" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="messagemTypes.length === 0">
|
||||||
|
<td
|
||||||
|
colspan="5"
|
||||||
|
class="text-center"
|
||||||
|
>
|
||||||
|
没有设置任何通知渠道
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</VTable>
|
||||||
|
<VDivider />
|
||||||
|
|
||||||
<VTable class="text-no-wrap">
|
<VCardText>
|
||||||
<thead>
|
<VForm @submit.prevent="() => {}">
|
||||||
<tr>
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
<th scope="col">
|
<VBtn
|
||||||
消息类型
|
mtype="submit"
|
||||||
</th>
|
@click="saveNotificationSwitchs"
|
||||||
<th scope="col">
|
>
|
||||||
微信
|
保存
|
||||||
</th>
|
</VBtn>
|
||||||
<th scope="col">
|
</div>
|
||||||
Telegram
|
</VForm>
|
||||||
</th>
|
</VCardText>
|
||||||
<th scope="col">
|
</VCard>
|
||||||
Slack
|
</VCol>
|
||||||
</th>
|
</VRow>
|
||||||
<th scope="col">
|
|
||||||
SynologyChat
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="message in messagemTypes"
|
|
||||||
:key="message.mtype"
|
|
||||||
>
|
|
||||||
<td>
|
|
||||||
{{ message.mtype }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<VCheckbox v-model="message.wechat" />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<VCheckbox v-model="message.telegram" />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<VCheckbox v-model="message.slack" />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<VCheckbox v-model="message.synologychat" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="messagemTypes.length === 0">
|
|
||||||
<td
|
|
||||||
colspan="4"
|
|
||||||
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>
|
|
||||||
</div>
|
|
||||||
</VForm>
|
|
||||||
</VCardText>
|
|
||||||
</VCard>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VCard title="定时作业">
|
<VCard title="定时作业">
|
||||||
<VCardText> 手动执行不会影响作业正常的时间表。 </VCardText>
|
<VCardSubtitle> 手动执行不会影响作业正常的时间表。 </VCardSubtitle>
|
||||||
|
|
||||||
<VTable class="text-no-wrap">
|
<VTable class="text-no-wrap">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -17,12 +17,32 @@ const resetSitesDisabled = ref(false)
|
|||||||
// 种子优先规则
|
// 种子优先规则
|
||||||
const selectedTorrentPriority = ref<string>('seeder')
|
const selectedTorrentPriority = ref<string>('seeder')
|
||||||
|
|
||||||
|
// CookieCloud设置项
|
||||||
|
const cookieCloudSetting = ref({
|
||||||
|
COOKIECLOUD_HOST: '',
|
||||||
|
COOKIECLOUD_KEY: '',
|
||||||
|
COOKIECLOUD_PASSWORD: '',
|
||||||
|
COOKIECLOUD_INTERVAL: 0,
|
||||||
|
USER_AGENT: '',
|
||||||
|
})
|
||||||
|
|
||||||
// 种子优先规则下拉框
|
// 种子优先规则下拉框
|
||||||
const TorrentPriorityItems = [
|
const TorrentPriorityItems = [
|
||||||
{ title: '站点优先', value: 'site' },
|
{ title: '站点优先', value: 'site' },
|
||||||
{ title: '做种数优先', value: 'seeder' },
|
{ title: '做种数优先', value: 'seeder' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 同步间隔下拉框
|
||||||
|
const CookieCloudIntervalItems = [
|
||||||
|
{ title: '每小时', value: 60 },
|
||||||
|
{ title: '每6小时', value: 360 },
|
||||||
|
{ title: '每12小时', value: 720 },
|
||||||
|
{ title: '每天', value: 1440 },
|
||||||
|
{ title: '每周', value: 10080 },
|
||||||
|
{ title: '每月', value: 43200 },
|
||||||
|
{ title: '永不', value: 0 },
|
||||||
|
]
|
||||||
|
|
||||||
// 重置站点
|
// 重置站点
|
||||||
async function resetSites() {
|
async function resetSites() {
|
||||||
try {
|
try {
|
||||||
@@ -77,13 +97,111 @@ async function saveTorrentPriority() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载CookieCloud设置
|
||||||
|
async function loadCookieCloudSettings() {
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.get('system/env')
|
||||||
|
if (result.success) {
|
||||||
|
const {
|
||||||
|
COOKIECLOUD_HOST,
|
||||||
|
COOKIECLOUD_KEY,
|
||||||
|
COOKIECLOUD_PASSWORD,
|
||||||
|
COOKIECLOUD_INTERVAL,
|
||||||
|
USER_AGENT,
|
||||||
|
} = result.data
|
||||||
|
cookieCloudSetting.value = {
|
||||||
|
COOKIECLOUD_HOST,
|
||||||
|
COOKIECLOUD_KEY,
|
||||||
|
COOKIECLOUD_PASSWORD,
|
||||||
|
COOKIECLOUD_INTERVAL,
|
||||||
|
USER_AGENT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API保存CookieCloud设置
|
||||||
|
async function saveCookieCloudetting() {
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.post(
|
||||||
|
'system/env',
|
||||||
|
cookieCloudSetting.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result.success)
|
||||||
|
$toast.success('保存站点同步设置成功')
|
||||||
|
else
|
||||||
|
$toast.error('保存站点同步设置失败!')
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
queryTorrentPriority()
|
queryTorrentPriority()
|
||||||
|
loadCookieCloudSettings()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VRow>
|
<VRow>
|
||||||
|
<VCol cols="12">
|
||||||
|
<VCard title="站点同步">
|
||||||
|
<VCardSubtitle> 从CookieCloud快速同步站点数据。 </VCardSubtitle>
|
||||||
|
<VCardText>
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="cookieCloudSetting.COOKIECLOUD_HOST"
|
||||||
|
label="CookieCloud服务器地址"
|
||||||
|
placeholder="https://movie-pilot.org/cookiecloud"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="cookieCloudSetting.COOKIECLOUD_KEY"
|
||||||
|
label="用户KEY"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="cookieCloudSetting.COOKIECLOUD_PASSWORD"
|
||||||
|
type="password"
|
||||||
|
label="端对端加密密码"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="cookieCloudSetting.COOKIECLOUD_INTERVAL"
|
||||||
|
label="自动同步间隔"
|
||||||
|
:items="CookieCloudIntervalItems"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12">
|
||||||
|
<VTextField
|
||||||
|
v-model="cookieCloudSetting.USER_AGENT"
|
||||||
|
label="浏览器User-Agent"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
<VCardItem>
|
||||||
|
<VBtn
|
||||||
|
type="submit"
|
||||||
|
@click="saveCookieCloudetting"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</VBtn>
|
||||||
|
</VCardItem>
|
||||||
|
</VCard>
|
||||||
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VCard title="下载优先规则">
|
<VCard title="下载优先规则">
|
||||||
<VCardSubtitle> 按站点或做种数量优先下载。 </VCardSubtitle>
|
<VCardSubtitle> 按站点或做种数量优先下载。 </VCardSubtitle>
|
||||||
@@ -94,8 +212,7 @@ onMounted(() => {
|
|||||||
<VSelect
|
<VSelect
|
||||||
v-model="selectedTorrentPriority"
|
v-model="selectedTorrentPriority"
|
||||||
:items="TorrentPriorityItems"
|
:items="TorrentPriorityItems"
|
||||||
label="优先规则"
|
label="当前使用下载优先规则"
|
||||||
outlined
|
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang='ts' setup>
|
||||||
import { useToast } from 'vue-toast-notification'
|
import { useToast } from 'vue-toast-notification'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import FilterRuleCard from '@/components/cards/FilterRuleCard.vue'
|
import FilterRuleCard from '@/components/cards/FilterRuleCard.vue'
|
||||||
@@ -32,6 +32,9 @@ const selectedRssSites = ref<number[]>([])
|
|||||||
// 当前规则类型
|
// 当前规则类型
|
||||||
const currentRuleType = ref('SubscribeFilterRules')
|
const currentRuleType = ref('SubscribeFilterRules')
|
||||||
|
|
||||||
|
// 是否开启订阅定时搜索
|
||||||
|
const enableIntervalSearch = ref(false)
|
||||||
|
|
||||||
// 包含与排除规则
|
// 包含与排除规则
|
||||||
const defaultFilterRules = ref({
|
const defaultFilterRules = ref({
|
||||||
include: '',
|
include: '',
|
||||||
@@ -41,6 +44,29 @@ const defaultFilterRules = ref({
|
|||||||
show_edit_dialog: false,
|
show_edit_dialog: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 订阅模式选择项
|
||||||
|
const subscribeModeItems = [
|
||||||
|
{ title: '自动', value: 'spider' },
|
||||||
|
{ title: '站点RSS', value: 'rss' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 选择的订阅模式
|
||||||
|
const selectedSubscribeMode = ref('spider')
|
||||||
|
|
||||||
|
// RSS运行周期选择项
|
||||||
|
const rssIntervalItems = [
|
||||||
|
{ title: '5分钟', value: 5 },
|
||||||
|
{ title: '10分钟', value: 10 },
|
||||||
|
{ title: '20分钟', value: 20 },
|
||||||
|
{ title: '半小时', value: 30 },
|
||||||
|
{ title: '1小时', value: 60 },
|
||||||
|
{ title: '12小时', value: 720 },
|
||||||
|
{ title: '1天', value: 1440 },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 选择的RSS运行周期
|
||||||
|
const selectedRssInterval = ref<number>(5)
|
||||||
|
|
||||||
// 导入代码弹窗
|
// 导入代码弹窗
|
||||||
const importCodeDialog = ref(false)
|
const importCodeDialog = ref(false)
|
||||||
|
|
||||||
@@ -62,9 +88,26 @@ async function querySelectedRssSites() {
|
|||||||
// 保存用户选中的订阅站点
|
// 保存用户选中的订阅站点
|
||||||
async function saveSelectedRssSites() {
|
async function saveSelectedRssSites() {
|
||||||
try {
|
try {
|
||||||
const result: { [key: string]: any } = await api.post('system/setting/RssSites', selectedRssSites.value)
|
const result1: { [key: string]: any } = await api.post(
|
||||||
|
'system/setting/RssSites',
|
||||||
|
selectedRssSites.value)
|
||||||
|
|
||||||
if (result.success)
|
const result2: { [key: string]: any } = await api.post(
|
||||||
|
'system/setting/SUBSCRIBE_SEARCH',
|
||||||
|
enableIntervalSearch.value ? 'True' : 'False',
|
||||||
|
)
|
||||||
|
|
||||||
|
const result3: { [key: string]: any } = await api.post(
|
||||||
|
'system/setting/SUBSCRIBE_MODE',
|
||||||
|
selectedSubscribeMode.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
const result4: { [key: string]: any } = await api.post(
|
||||||
|
'system/setting/SUBSCRIBE_RSS_INTERVAL',
|
||||||
|
selectedRssInterval.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result1.success && result2.success && result3.success && result4.success)
|
||||||
$toast.success('订阅站点保存成功')
|
$toast.success('订阅站点保存成功')
|
||||||
else
|
else
|
||||||
$toast.error('订阅站点保存失败!')
|
$toast.error('订阅站点保存失败!')
|
||||||
@@ -82,6 +125,19 @@ async function querySites() {
|
|||||||
// 过滤站点,只有启用的站点才显示
|
// 过滤站点,只有启用的站点才显示
|
||||||
allSites.value = data.filter(item => item.is_active)
|
allSites.value = data.filter(item => item.is_active)
|
||||||
querySelectedRssSites()
|
querySelectedRssSites()
|
||||||
|
|
||||||
|
// 查询订阅搜索开关
|
||||||
|
const result: { [key: string]: any } = await api.get('system/setting/SUBSCRIBE_SEARCH')
|
||||||
|
if (result.success)
|
||||||
|
enableIntervalSearch.value = result.data?.value
|
||||||
|
// 查询订阅模式
|
||||||
|
const result2: { [key: string]: any } = await api.get('system/setting/SUBSCRIBE_MODE')
|
||||||
|
if (result2.success)
|
||||||
|
selectedSubscribeMode.value = result2.data?.value
|
||||||
|
// 查询站点RSS周期
|
||||||
|
const result3: { [key: string]: any } = await api.get('system/setting/SUBSCRIBE_RSS_INTERVAL')
|
||||||
|
if (result3.success)
|
||||||
|
selectedRssInterval.value = result3.data?.value
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
@@ -346,7 +402,34 @@ onMounted(() => {
|
|||||||
</VChip>
|
</VChip>
|
||||||
</VChipGroup>
|
</VChipGroup>
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
|
<VCardText>
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="selectedSubscribeMode"
|
||||||
|
:items="subscribeModeItems"
|
||||||
|
label="订阅模式"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="selectedRssInterval"
|
||||||
|
:items="rssIntervalItems"
|
||||||
|
label="站点RSS周期"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSwitch
|
||||||
|
v-model="enableIntervalSearch"
|
||||||
|
label="开启订阅定时搜索"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
<VBtn type="submit" @click="saveSelectedRssSites">
|
<VBtn type="submit" @click="saveSelectedRssSites">
|
||||||
保存
|
保存
|
||||||
@@ -386,7 +469,7 @@ onMounted(() => {
|
|||||||
</VMenu>
|
</VMenu>
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
</template>
|
</template>
|
||||||
<VCardSubtitle> 设置在正常订阅时默认使用的优先级,未在优先级中的资源将不会自动下载。 </VCardSubtitle>
|
<VCardSubtitle> 设置在正常订阅时默认使用的优先级,未在优先级中的资源将不会自动下载。</VCardSubtitle>
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
<div class="grid gap-3 grid-filterrule-card">
|
<div class="grid gap-3 grid-filterrule-card">
|
||||||
<FilterRuleCard
|
<FilterRuleCard
|
||||||
@@ -452,7 +535,7 @@ onMounted(() => {
|
|||||||
</VMenu>
|
</VMenu>
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
</template>
|
</template>
|
||||||
<VCardSubtitle> 设置在订阅洗版时使用的优先级,匹配优先级1时洗版完成。 </VCardSubtitle>
|
<VCardSubtitle> 设置在订阅洗版时使用的优先级,匹配优先级1时洗版完成。</VCardSubtitle>
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
<div class="grid gap-3 grid-filterrule-card">
|
<div class="grid gap-3 grid-filterrule-card">
|
||||||
<FilterRuleCard
|
<FilterRuleCard
|
||||||
@@ -488,7 +571,7 @@ onMounted(() => {
|
|||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VCard title="默认过滤规则">
|
<VCard title="默认过滤规则">
|
||||||
<VCardSubtitle> 设置在订阅时默认使用的过滤规则。 </VCardSubtitle>
|
<VCardSubtitle> 设置在订阅时默认使用的过滤规则。</VCardSubtitle>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm>
|
<VForm>
|
||||||
<VRow>
|
<VRow>
|
||||||
@@ -555,7 +638,7 @@ onMounted(() => {
|
|||||||
</VDialog>
|
</VDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang='scss'>
|
||||||
.grid-filterrule-card {
|
.grid-filterrule-card {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
||||||
padding-block-end: 1rem;
|
padding-block-end: 1rem;
|
||||||
|
|||||||
732
src/views/setting/AccountSettingSystem.vue
Normal file
732
src/views/setting/AccountSettingSystem.vue
Normal file
@@ -0,0 +1,732 @@
|
|||||||
|
<!-- eslint-disable sonarjs/no-duplicate-string -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useToast } from 'vue-toast-notification'
|
||||||
|
import { VRow } from 'vuetify/lib/components/index.mjs'
|
||||||
|
import api from '@/api'
|
||||||
|
|
||||||
|
// 选中的媒体服务器
|
||||||
|
const selectedMediaServers = ref([])
|
||||||
|
|
||||||
|
// 下载器选中标签页
|
||||||
|
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: '',
|
||||||
|
DOWNLOADER_MONITOR: true,
|
||||||
|
TORRENT_TAG: '',
|
||||||
|
QB_HOST: '',
|
||||||
|
QB_USER: '',
|
||||||
|
QB_PASSWORD: '',
|
||||||
|
QB_CATEGORY: false,
|
||||||
|
QB_SEQUENTIAL: false,
|
||||||
|
QB_FORCE_RESUME: false,
|
||||||
|
TR_HOST: '',
|
||||||
|
TR_USER: '',
|
||||||
|
TR_PASSWORD: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 媒体服务器设置项
|
||||||
|
const mediaServerSettings = ref({
|
||||||
|
MEDIASERVER_SYNC_INTERVAL: 6,
|
||||||
|
MEDIASERVER_SYNC_BLACKLIST: '',
|
||||||
|
EMBY_HOST: '',
|
||||||
|
EMBY_PLAY_HOST: '',
|
||||||
|
EMBY_API_KEY: '',
|
||||||
|
JELLYFIN_HOST: '',
|
||||||
|
JELLYFIN_PLAY_HOST: '',
|
||||||
|
JELLYFIN_API_KEY: '',
|
||||||
|
PLEX_HOST: '',
|
||||||
|
PLEX_PLAY_HOST: '',
|
||||||
|
PLEX_TOKEN: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 下载器字典项
|
||||||
|
const Downloaders = [
|
||||||
|
{
|
||||||
|
title: 'Qbittorrent',
|
||||||
|
value: 'qbittorrent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Transmission',
|
||||||
|
value: 'transmission',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 媒体服务器字典项
|
||||||
|
const MediaServers = [
|
||||||
|
{
|
||||||
|
title: 'Emby',
|
||||||
|
value: 'emby',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Jellyfin',
|
||||||
|
value: 'jellyfin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Plex',
|
||||||
|
value: 'plex',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 转移方式字典
|
||||||
|
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 },
|
||||||
|
{ title: '每小时', value: 1 },
|
||||||
|
{ title: '每6小时', value: 6 },
|
||||||
|
{ title: '每12小时', value: 12 },
|
||||||
|
{ title: '每天', value: 24 },
|
||||||
|
{ title: '每周', value: 168 },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 提示框
|
||||||
|
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 loadDownladerSetting() {
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.get('system/env')
|
||||||
|
if (result.success) {
|
||||||
|
const {
|
||||||
|
DOWNLOADER,
|
||||||
|
DOWNLOADER_MONITOR,
|
||||||
|
TORRENT_TAG,
|
||||||
|
QB_HOST,
|
||||||
|
QB_USER,
|
||||||
|
QB_PASSWORD,
|
||||||
|
QB_CATEGORY,
|
||||||
|
QB_SEQUENTIAL,
|
||||||
|
QB_FORCE_RESUME,
|
||||||
|
TR_HOST,
|
||||||
|
TR_USER,
|
||||||
|
TR_PASSWORD,
|
||||||
|
} = result.data
|
||||||
|
downloaderSettings.value = {
|
||||||
|
DOWNLOADER,
|
||||||
|
DOWNLOADER_MONITOR,
|
||||||
|
TORRENT_TAG,
|
||||||
|
QB_HOST,
|
||||||
|
QB_USER,
|
||||||
|
QB_PASSWORD,
|
||||||
|
QB_CATEGORY,
|
||||||
|
QB_SEQUENTIAL,
|
||||||
|
QB_FORCE_RESUME,
|
||||||
|
TR_HOST,
|
||||||
|
TR_USER,
|
||||||
|
TR_PASSWORD,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API保存下载器设置
|
||||||
|
async function saveDownloaderSetting() {
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.post(
|
||||||
|
'system/env',
|
||||||
|
downloaderSettings.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
$toast.success('保存下载器设置成功')
|
||||||
|
reloadModule()
|
||||||
|
}
|
||||||
|
else { $toast.error('保存下载器设置失败!') }
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API查询媒体服务器设置
|
||||||
|
async function loadMediaServerSetting() {
|
||||||
|
try {
|
||||||
|
const result1: { [key: string]: any } = await api.get('system/setting/MEDIASERVER')
|
||||||
|
if (result1.success)
|
||||||
|
selectedMediaServers.value = result1.data?.value?.split(',')
|
||||||
|
|
||||||
|
const result2: { [key: string]: any } = await api.get('system/env')
|
||||||
|
if (result2.success) {
|
||||||
|
const {
|
||||||
|
MEDIASERVER_SYNC_INTERVAL,
|
||||||
|
MEDIASERVER_SYNC_BLACKLIST,
|
||||||
|
EMBY_HOST,
|
||||||
|
EMBY_PLAY_HOST,
|
||||||
|
EMBY_API_KEY,
|
||||||
|
JELLYFIN_HOST,
|
||||||
|
JELLYFIN_PLAY_HOST,
|
||||||
|
JELLYFIN_API_KEY,
|
||||||
|
PLEX_HOST,
|
||||||
|
PLEX_PLAY_HOST,
|
||||||
|
PLEX_TOKEN,
|
||||||
|
} = result2.data
|
||||||
|
mediaServerSettings.value = {
|
||||||
|
MEDIASERVER_SYNC_INTERVAL,
|
||||||
|
MEDIASERVER_SYNC_BLACKLIST,
|
||||||
|
EMBY_HOST,
|
||||||
|
EMBY_PLAY_HOST,
|
||||||
|
EMBY_API_KEY,
|
||||||
|
JELLYFIN_HOST,
|
||||||
|
JELLYFIN_PLAY_HOST,
|
||||||
|
JELLYFIN_API_KEY,
|
||||||
|
PLEX_HOST,
|
||||||
|
PLEX_PLAY_HOST,
|
||||||
|
PLEX_TOKEN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API保存媒体服务器设置
|
||||||
|
async function saveMediaServerSetting() {
|
||||||
|
try {
|
||||||
|
const result1: { [key: string]: any } = await api.post(
|
||||||
|
'system/setting/MEDIASERVER',
|
||||||
|
selectedMediaServers.value.join(','),
|
||||||
|
)
|
||||||
|
|
||||||
|
const result2: { [key: string]: any } = await api.post(
|
||||||
|
'system/env',
|
||||||
|
mediaServerSettings.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result1.success && result2.success) {
|
||||||
|
$toast.success('保存媒体服务器设置成功')
|
||||||
|
reloadModule()
|
||||||
|
}
|
||||||
|
else { $toast.error('保存媒体服务器设置失败!') }
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API接口重新加载模块
|
||||||
|
async function reloadModule() {
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.get('system/reload')
|
||||||
|
if (result.success)
|
||||||
|
$toast.success('重新加载模块成功')
|
||||||
|
else
|
||||||
|
$toast.error('重新加载模块失败!')
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
onMounted(() => {
|
||||||
|
loadDownladerSetting()
|
||||||
|
loadMediaServerSetting()
|
||||||
|
loadMediaSettings()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12">
|
||||||
|
<VCard title="下载器">
|
||||||
|
<VCardSubtitle>只有选中的下载器才会被默认使用。</VCardSubtitle>
|
||||||
|
<VCardText>
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="downloaderSettings.DOWNLOADER"
|
||||||
|
:items="Downloaders"
|
||||||
|
label="当前使用下载器"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="downloaderSettings.TORRENT_TAG"
|
||||||
|
label="下载器种子标签"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSwitch
|
||||||
|
v-model="downloaderSettings.DOWNLOADER_MONITOR"
|
||||||
|
label="监控下载器"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<VCol>
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<VWindowItem value="qbittorrent">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="downloaderSettings.QB_HOST"
|
||||||
|
label="地址"
|
||||||
|
placeholder="IP:PORT"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="downloaderSettings.QB_USER"
|
||||||
|
label="用户名"
|
||||||
|
placeholder="admin"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="downloaderSettings.QB_PASSWORD"
|
||||||
|
type="password"
|
||||||
|
label="密码"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VSwitch
|
||||||
|
v-model="downloaderSettings.QB_CATEGORY"
|
||||||
|
label="自动分类管理"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VSwitch
|
||||||
|
v-model="downloaderSettings.QB_SEQUENTIAL"
|
||||||
|
label="顺序下载"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VSwitch
|
||||||
|
v-model="downloaderSettings.QB_FORCE_RESUME"
|
||||||
|
label="强制继续"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
<VWindowItem value="transmission">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="downloaderSettings.TR_HOST"
|
||||||
|
label="地址"
|
||||||
|
placeholder="IP:PORT"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="downloaderSettings.TR_USER"
|
||||||
|
label="用户名"
|
||||||
|
placeholder="admin"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="downloaderSettings.TR_PASSWORD"
|
||||||
|
type="password"
|
||||||
|
label="密码"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
</VWindow>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
<VCardText>
|
||||||
|
<VForm @submit.prevent="() => {}">
|
||||||
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
|
<VBtn
|
||||||
|
mtype="submit"
|
||||||
|
@click="saveDownloaderSetting"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</VBtn>
|
||||||
|
</div>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12">
|
||||||
|
<VCard title="媒体服务器">
|
||||||
|
<VCardSubtitle>只有选中的媒体服务器才会被默认使用。</VCardSubtitle>
|
||||||
|
<VCardText>
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VSelect
|
||||||
|
v-model="selectedMediaServers"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
:items="MediaServers"
|
||||||
|
label="当前使用媒体服务器"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VSelect
|
||||||
|
v-model="mediaServerSettings.MEDIASERVER_SYNC_INTERVAL"
|
||||||
|
:items="syncIntervalItems"
|
||||||
|
label="同步周期"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.MEDIASERVER_SYNC_BLACKLIST"
|
||||||
|
label="媒体库同步黑名单"
|
||||||
|
placeholder="使用,分隔"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<VCol>
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<VWindowItem value="emby">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.EMBY_HOST"
|
||||||
|
label="地址"
|
||||||
|
placeholder="IP:PORT"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.EMBY_PLAY_HOST"
|
||||||
|
label="外网播放地址"
|
||||||
|
placeholder="http(s)://domain:port"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.EMBY_API_KEY"
|
||||||
|
label="API密钥"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
<VWindowItem value="jellyfin">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.JELLYFIN_HOST"
|
||||||
|
label="地址"
|
||||||
|
placeholder="IP:PORT"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.JELLYFIN_PLAY_HOST"
|
||||||
|
label="外网播放地址"
|
||||||
|
placeholder="http(s)://domain:port"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.JELLYFIN_API_KEY"
|
||||||
|
label="API密钥"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
<VWindowItem value="plex">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.PLEX_HOST"
|
||||||
|
label="地址"
|
||||||
|
placeholder="IP:PORT"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.PLEX_PLAY_HOST"
|
||||||
|
label="外网播放地址"
|
||||||
|
placeholder="http(s)://domain:port"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaServerSettings.PLEX_TOKEN"
|
||||||
|
label="API密钥"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
|
</VWindow>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
<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="下载目录"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaSettings.DOWNLOAD_MOVIE_PATH"
|
||||||
|
label="电影下载目录"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaSettings.DOWNLOAD_TV_PATH"
|
||||||
|
label="电视剧下载目录"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaSettings.DOWNLOAD_ANIME_PATH"
|
||||||
|
label="动漫下载目录"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSwitch
|
||||||
|
v-model="mediaSettings.DOWNLOAD_CATEGORY"
|
||||||
|
label="下载目录自动分类"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="mediaSettings.TRANSFER_TYPE"
|
||||||
|
:items="transferTypeItems"
|
||||||
|
label="整理方式"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="mediaSettings.OVERWRITE_MODE"
|
||||||
|
:items="overwriteModeItems"
|
||||||
|
label="覆盖模式"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSwitch
|
||||||
|
v-model="mediaSettings.SCRAP_METADATA"
|
||||||
|
label="自动刮削媒体信息"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaSettings.LIBRARY_PATH"
|
||||||
|
label="媒体库目录"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaSettings.LIBRARY_MOVIE_NAME"
|
||||||
|
label="电影目录名称"
|
||||||
|
placeholder="电影"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaSettings.LIBRARY_TV_NAME"
|
||||||
|
label="电视剧目录名称"
|
||||||
|
placeholder="电视剧"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="mediaSettings.LIBRARY_ANIME_NAME"
|
||||||
|
label="动漫目录名称"
|
||||||
|
placeholder="动漫"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSwitch
|
||||||
|
v-model="mediaSettings.LIBRARY_CATEGORY"
|
||||||
|
label="媒体库目录自动分类"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
<VCardText>
|
||||||
|
<VForm @submit.prevent="() => {}">
|
||||||
|
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||||
|
<VBtn
|
||||||
|
mtype="submit"
|
||||||
|
@click="saveMediaSetting"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</VBtn>
|
||||||
|
</div>
|
||||||
|
</VForm>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user