Files
MoviePilot-Frontend/src/views/setting/AccountSettingDirectory.vue

348 lines
10 KiB
Vue

<!-- 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 { TransferDirectoryConf, StorageConf } from '@/api/types'
import DirectoryCard from '@/components/cards/DirectoryCard.vue'
import StorageCard from '@/components/cards/StorageCard.vue'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
// 所有下载目录
const directories = ref<TransferDirectoryConf[]>([])
// 所有存储
const storages = ref<StorageConf[]>([])
// 二级分类策略
const mediaCategories = ref<{ [key: string]: any }>({})
// 提示框
const $toast = useToast()
// 进度框
const progressDialog = ref(false)
// 数据源
const sourceItems = [
{ 'title': 'TheMovieDb', 'value': 'themoviedb' },
{ 'title': '豆瓣', 'value': 'douban' },
]
// 系统设置
const SystemSettings = ref<any>({
Basic: {
SCRAP_SOURCE: 'themoviedb',
MOVIE_RENAME_FORMAT: null,
TV_RENAME_FORMAT: null,
},
})
// 加载系统设置
async function loadSystemSettings() {
try {
const result: { [key: string]: any } = await api.get('system/env')
if (result.success) {
// 将API返回的值赋值给SystemSettings
for (const sectionKey of Object.keys(SystemSettings.value) as Array<keyof typeof SystemSettings.value>) {
Object.keys(SystemSettings.value[sectionKey]).forEach((key: string) => {
if (result.data.hasOwnProperty(key)) (SystemSettings.value[sectionKey] as any)[key] = result.data[key]
})
}
}
} catch (error) {
console.log(error)
}
}
// 重载系统生效配置
async function reloadSystem() {
progressDialog.value = true
try {
const result: { [key: string]: any } = await api.get('system/reload')
if (result.success) $toast.success(t('setting.system.reloadSuccess'))
else $toast.error(t('setting.system.reloadFailed'))
} catch (error) {
console.log(error)
}
progressDialog.value = false
}
// 移动结束
function orderDirectoryCards() {
// 更新所有目录的优先级
directories.value.forEach((item, index) => {
item.priority = index
})
}
// 查询存储
async function loadStorages() {
try {
const result: { [key: string]: any } = await api.get('system/setting/Storages')
storages.value = result.data?.value ?? []
} catch (error) {
console.log(error)
}
}
// 保存存储
async function saveStorages() {
try {
const result: { [key: string]: any } = await api.post('system/setting/Storages', storages.value)
if (result.success) $toast.success(t('setting.directory.storageSaveSuccess'))
else $toast.error(t('setting.directory.storageSaveFailed'))
} catch (error) {
console.log(error)
}
}
// 查询目录
async function loadDirectories() {
try {
const result: { [key: string]: any } = await api.get('system/setting/Directories')
directories.value = result.data?.value ?? []
} catch (error) {
console.log(error)
}
}
// 保存目录
async function saveDirectories() {
orderDirectoryCards()
try {
const names = directories.value.map(item => item.name)
if (new Set(names).size !== names.length) {
$toast.error(t('setting.directory.duplicateDirectoryName'))
return
}
const result: { [key: string]: any } = await api.post('system/setting/Directories', directories.value)
if (result.success) {
$toast.success(t('setting.directory.directorySaveSuccess'))
await reloadSystem()
} else $toast.error(t('setting.directory.directorySaveFailed'))
} catch (error) {
console.log(error)
}
}
// 添加媒体库目录
function addDirectory() {
let name = `${t('setting.directory.defaultDirName')}${directories.value.length + 1}`
while (directories.value.some(item => item.name === name)) {
name = `${t('setting.directory.defaultDirName')}${
parseInt(name.split(t('setting.directory.defaultDirName'))[1]) + 1
}`
}
directories.value.push({
name: name,
storage: 'local',
download_path: '',
priority: -1,
monitor_type: '',
media_type: '',
media_category: '',
transfer_type: '',
})
orderDirectoryCards()
}
// 移除媒体库目录
function removeDirectory(directory: TransferDirectoryConf) {
const index = directories.value.indexOf(directory)
if (index > -1) {
directories.value.splice(index, 1)
}
}
// 调用API查询自动分类配置
async function loadMediaCategories() {
try {
mediaCategories.value = await api.get('media/category')
} catch (error) {
console.log(error)
}
}
// 添加存储
function addStorage() {
storages.value.push({
name: `${t('storage.custom')} ${storages.value.length + 1}`,
type: 'custom',
config: {},
})
}
// 移除存储
function removeStorage(storage: StorageConf) {
const index = storages.value.indexOf(storage)
if (index > -1) {
storages.value.splice(index, 1)
}
}
// 更新存储
async function updatedStorage(storage: StorageConf) {
const index = storages.value.indexOf(storage)
if (index > -1) {
storages.value[index] = storage
}
}
// 保存设置
async function saveSystemSettings(value: any) {
try {
const result: { [key: string]: any } = await api.post('system/env', value)
if (result.success) {
$toast.success(t('setting.directory.organizeSaveSuccess'))
} else $toast.error(t('setting.directory.organizeSaveFailed'))
} catch (error) {
console.log(error)
}
}
// 加载数据
onMounted(() => {
loadDirectories()
loadStorages()
loadMediaCategories()
loadSystemSettings()
})
</script>
<template>
<VRow>
<VCol cols="12">
<VCard>
<VCardItem>
<VCardTitle>{{ t('setting.directory.storage') }}</VCardTitle>
<VCardSubtitle>{{ t('setting.directory.storageDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
v-model="storages"
handle=".cursor-move"
item-key="name"
tag="div"
:component-data="{ 'class': 'grid gap-3 grid-app-card' }"
>
<template #item="{ element }">
<StorageCard :storage="element" @close="removeStorage(element)" @done="updatedStorage" />
</template>
</draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" class="me-2" @click="saveStorages"> {{ t('common.save') }} </VBtn>
<VBtn color="success" variant="tonal" @click="addStorage">
<VIcon icon="mdi-plus" />
</VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
<VRow>
<VCol cols="12">
<VCard>
<VCardItem>
<VCardTitle>{{ t('setting.directory.directory') }}</VCardTitle>
<VCardSubtitle>{{ t('setting.directory.directoryDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<draggable
v-model="directories"
handle=".cursor-move"
item-key="pri"
tag="div"
@end="orderDirectoryCards"
:component-data="{ 'class': 'grid gap-3 grid-directory-card items-start' }"
>
<template #item="{ element }">
<DirectoryCard
:directory="element"
:categories="mediaCategories"
:storages="storages"
@update:modelValue="(value: any) => {element.download_path = value?.download; element.library_path = value?.library}"
@close="removeDirectory(element)"
/>
</template>
</draggable>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveDirectories"> {{ t('common.save') }} </VBtn>
<VBtn color="success" variant="tonal" @click="addDirectory">
<VIcon icon="mdi-plus" />
</VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
<VRow>
<VCol cols="12">
<VCard>
<VCardItem>
<VCardTitle>{{ t('setting.directory.organizeAndScrap') }}</VCardTitle>
<VCardSubtitle>{{ t('setting.directory.organizeAndScrapDesc') }}</VCardSubtitle>
</VCardItem>
<VCardText>
<VRow>
<VCol cols="12" md="6">
<VSelect
v-model="SystemSettings.Basic.SCRAP_SOURCE"
:items="sourceItems"
:label="t('setting.directory.scrapSource')"
:hint="t('setting.directory.scrapSourceHint')"
persistent-hint
prepend-inner-icon="mdi-database"
/>
</VCol>
<VCol cols="12">
<VTextarea
v-model="SystemSettings.Basic.MOVIE_RENAME_FORMAT"
:label="t('setting.directory.movieRenameFormat')"
:hint="t('setting.directory.movieRenameFormatHint')"
persistent-hint
clearable
active
prepend-inner-icon="mdi-movie-open"
/>
</VCol>
<VCol cols="12">
<VTextarea
v-model="SystemSettings.Basic.TV_RENAME_FORMAT"
:label="t('setting.directory.tvRenameFormat')"
:hint="t('setting.directory.tvRenameFormatHint')"
persistent-hint
clearable
active
prepend-inner-icon="mdi-television"
/>
</VCol>
</VRow>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveSystemSettings(SystemSettings.Basic)"> {{ t('common.save') }}</VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
<!-- 进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="t('setting.system.reloading')" />
</template>