mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-07 08:46:40 +08:00
fix: auto match manual transfer target path
This commit is contained in:
+17
-1
@@ -1292,7 +1292,7 @@ export interface TransferForm {
|
|||||||
// 目标存储
|
// 目标存储
|
||||||
target_storage: string
|
target_storage: string
|
||||||
// 目标路径
|
// 目标路径
|
||||||
target_path: string
|
target_path: string | null
|
||||||
// TMDB ID
|
// TMDB ID
|
||||||
tmdbid?: number
|
tmdbid?: number
|
||||||
// 豆瓣 ID
|
// 豆瓣 ID
|
||||||
@@ -1335,6 +1335,22 @@ export interface ManualTransferPayload extends Omit<TransferForm, 'fileitem'> {
|
|||||||
fileitems?: FileItem[]
|
fileitems?: FileItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动整理目的路径匹配结果
|
||||||
|
export interface ManualTransferTargetPathData {
|
||||||
|
// 目标存储
|
||||||
|
target_storage?: string | null
|
||||||
|
// 目标路径
|
||||||
|
target_path?: string | null
|
||||||
|
// 整理方式
|
||||||
|
transfer_type?: string | null
|
||||||
|
// 刮削
|
||||||
|
scrape?: boolean
|
||||||
|
// 媒体库类型子目录
|
||||||
|
library_type_folder?: boolean
|
||||||
|
// 媒体库类别子目录
|
||||||
|
library_category_folder?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
// 手动整理预览统计
|
// 手动整理预览统计
|
||||||
export interface ManualTransferPreviewSummary {
|
export interface ManualTransferPreviewSummary {
|
||||||
// 总数
|
// 总数
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
ManualTransferPayload,
|
ManualTransferPayload,
|
||||||
ManualTransferPreviewData,
|
ManualTransferPreviewData,
|
||||||
ManualTransferPreviewItem,
|
ManualTransferPreviewItem,
|
||||||
|
ManualTransferTargetPathData,
|
||||||
StorageConf,
|
StorageConf,
|
||||||
TransferDirectoryConf,
|
TransferDirectoryConf,
|
||||||
TransferForm,
|
TransferForm,
|
||||||
@@ -113,6 +114,14 @@ const episodeFormatRecommendState = reactive<{
|
|||||||
|
|
||||||
const episodeFormatRuleConfigured = ref<boolean | undefined>(undefined)
|
const episodeFormatRuleConfigured = ref<boolean | undefined>(undefined)
|
||||||
|
|
||||||
|
interface ManualTransferTargetPathRequest {
|
||||||
|
fileitem?: FileItem
|
||||||
|
fileitems?: FileItem[]
|
||||||
|
logid?: number
|
||||||
|
logids?: number[]
|
||||||
|
target_storage?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
// 生成文件项稳定键,用于去重和状态同步。
|
// 生成文件项稳定键,用于去重和状态同步。
|
||||||
function getFileItemKey(item?: FileItem) {
|
function getFileItemKey(item?: FileItem) {
|
||||||
return [item?.storage ?? '', item?.type ?? '', item?.path ?? ''].join('|')
|
return [item?.storage ?? '', item?.type ?? '', item?.path ?? ''].join('|')
|
||||||
@@ -265,7 +274,7 @@ const transferForm = reactive<TransferForm>({
|
|||||||
fileitem: {} as FileItem,
|
fileitem: {} as FileItem,
|
||||||
logid: 0,
|
logid: 0,
|
||||||
target_storage: props.target_storage ?? 'local',
|
target_storage: props.target_storage ?? 'local',
|
||||||
target_path: props.target_path ?? '',
|
target_path: normalizeTargetPath(props.target_path),
|
||||||
transfer_type: '',
|
transfer_type: '',
|
||||||
min_filesize: 0,
|
min_filesize: 0,
|
||||||
scrape: false,
|
scrape: false,
|
||||||
@@ -292,6 +301,79 @@ const targetDirectories = computed(() => {
|
|||||||
return [...new Set(libraryDirectories)]
|
return [...new Set(libraryDirectories)]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 构造目的路径自动匹配请求,只传用户真实上下文,避免用默认存储误导后端匹配。
|
||||||
|
function createTargetPathMatchRequest(): ManualTransferTargetPathRequest | undefined {
|
||||||
|
const payload: ManualTransferTargetPathRequest = {}
|
||||||
|
|
||||||
|
if (props.target_storage) {
|
||||||
|
payload.target_storage = props.target_storage
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedItems.value.length === 1) {
|
||||||
|
payload.fileitem = normalizedItems.value[0]
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedItems.value.length > 1) {
|
||||||
|
payload.fileitems = normalizedItems.value
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.logids?.length) {
|
||||||
|
if (props.logids.length > 1) {
|
||||||
|
payload.logids = props.logids
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.logid = props.logids[0]
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用后端匹配到的目的路径配置,未匹配时保持 null 等待用户手工选择。
|
||||||
|
function applyMatchedTargetPath(data?: ManualTransferTargetPathData) {
|
||||||
|
const matchedTargetPath = normalizeTargetPath(data?.target_path)
|
||||||
|
if (!matchedTargetPath) {
|
||||||
|
transferForm.target_path = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
transferForm.target_storage = data?.target_storage || transferForm.target_storage || 'local'
|
||||||
|
transferForm.transfer_type = data?.transfer_type || transferForm.transfer_type
|
||||||
|
transferForm.scrape = data?.scrape ?? false
|
||||||
|
transferForm.library_type_folder = data?.library_type_folder ?? false
|
||||||
|
transferForm.library_category_folder = data?.library_category_folder ?? false
|
||||||
|
transferForm.target_path = matchedTargetPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求后端按源目录匹配最合适的手动整理目的路径。
|
||||||
|
async function autoSelectTargetPath() {
|
||||||
|
if (normalizeTargetPath(props.target_path) || transferForm.target_path) return
|
||||||
|
|
||||||
|
const payload = createTargetPathMatchRequest()
|
||||||
|
if (!payload) {
|
||||||
|
transferForm.target_path = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await api.post<ApiResponse<ManualTransferTargetPathData>, ApiResponse<ManualTransferTargetPathData>>(
|
||||||
|
'transfer/manual/target-path',
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
transferForm.target_path = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMatchedTargetPath(result.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
transferForm.target_path = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 监听目的路径变化,配置默认值
|
// 监听目的路径变化,配置默认值
|
||||||
watch(
|
watch(
|
||||||
() => transferForm.target_path,
|
() => transferForm.target_path,
|
||||||
@@ -397,6 +479,12 @@ function getUniqueValues(values: (string | undefined)[]) {
|
|||||||
return [...new Set(values.map(item => item?.trim()).filter(Boolean) as string[])]
|
return [...new Set(values.map(item => item?.trim()).filter(Boolean) as string[])]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 归一化可选目的路径,保证未指定时向接口传递 null 而不是空字符串。
|
||||||
|
function normalizeTargetPath(path?: string | null) {
|
||||||
|
const normalizedPath = path?.trim()
|
||||||
|
return normalizedPath || null
|
||||||
|
}
|
||||||
|
|
||||||
// 统一解析接口返回的数字字段,兼容 string/number
|
// 统一解析接口返回的数字字段,兼容 string/number
|
||||||
function toPreviewNumber(value: unknown) {
|
function toPreviewNumber(value: unknown) {
|
||||||
if (value === undefined || value === null || value === '') return undefined
|
if (value === undefined || value === null || value === '') return undefined
|
||||||
@@ -636,6 +724,7 @@ function createTransferPayload(options: { item?: FileItem; items?: FileItem[]; l
|
|||||||
...transferForm,
|
...transferForm,
|
||||||
fileitem: sourceItem,
|
fileitem: sourceItem,
|
||||||
logid: options.logid ?? 0,
|
logid: options.logid ?? 0,
|
||||||
|
target_path: normalizeTargetPath(transferForm.target_path),
|
||||||
episode_group: transferForm.episode_group?.trim() || null,
|
episode_group: transferForm.episode_group?.trim() || null,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1115,8 +1204,9 @@ async function transfer(background: boolean = false) {
|
|||||||
emit('done')
|
emit('done')
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
loadDirectories()
|
await loadDirectories()
|
||||||
|
await autoSelectTargetPath()
|
||||||
loadStorages()
|
loadStorages()
|
||||||
loadEpisodeFormatRuleConfiguration()
|
loadEpisodeFormatRuleConfiguration()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user