mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-08-28 19:47:49 +08:00
feat(download): 未识别资源确认后继续下载 (#683)
Co-authored-by: liulang <liulang@25qp.cn>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useToast } from 'vue-toastification'
|
||||
import api from '@/api'
|
||||
import api, { isApiBusinessFailure, isApiResponse } from '@/api'
|
||||
import { doneNProgress, startNProgress } from '@/api/nprogress'
|
||||
import type {
|
||||
DownloaderConf,
|
||||
@@ -16,6 +16,11 @@ import { useI18n } from 'vue-i18n'
|
||||
import MediaIdSelector from '../misc/MediaIdSelector.vue'
|
||||
import { isMediaDataSource, isMusicMediaSource, isValidMediaSourceId } from '@/utils/mediaId'
|
||||
import { useGlobalSettingsStore } from '@/stores'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
|
||||
interface DownloadAddedData {
|
||||
requires_confirmation?: boolean
|
||||
}
|
||||
|
||||
// 多语言支持
|
||||
const { t } = useI18n()
|
||||
@@ -48,6 +53,7 @@ const emit = defineEmits(['done', 'error', 'close'])
|
||||
|
||||
// 提示框
|
||||
const $toast = useToast()
|
||||
const createConfirm = useConfirm()
|
||||
|
||||
// 选择的下载器
|
||||
const selectedDownloader = ref<string | null>(null)
|
||||
@@ -201,6 +207,7 @@ async function addDownload() {
|
||||
media_in?: MediaInfo
|
||||
media_source?: MediaDataSource
|
||||
music_type?: Exclude<MusicEntityType, 'artist'>
|
||||
allow_unrecognized?: boolean
|
||||
save_path: string | null
|
||||
torrent_in: TorrentInfo | undefined
|
||||
} = {
|
||||
@@ -222,7 +229,28 @@ async function addDownload() {
|
||||
|
||||
const endpoint = props.media ? 'download/' : 'download/add'
|
||||
|
||||
await api.post<null>(endpoint, payload, { feedback: 'silent' })
|
||||
try {
|
||||
await api.post<null>(endpoint, payload, { feedback: 'silent' })
|
||||
} catch (error) {
|
||||
if (
|
||||
!isApiBusinessFailure(error) ||
|
||||
!isApiResponse<DownloadAddedData>(error.payload) ||
|
||||
error.payload.data?.requires_confirmation !== true
|
||||
) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const confirmed = await createConfirm({
|
||||
type: 'warn',
|
||||
title: t('dialog.addDownload.unrecognizedTitle'),
|
||||
content: t('dialog.addDownload.unrecognizedContent'),
|
||||
confirmText: t('dialog.addDownload.continueDownload'),
|
||||
})
|
||||
if (!confirmed) return
|
||||
|
||||
payload.allow_unrecognized = true
|
||||
await api.post<null>(endpoint, payload, { feedback: 'silent' })
|
||||
}
|
||||
|
||||
// 添加下载成功
|
||||
$toast.success(
|
||||
@@ -241,9 +269,10 @@ async function addDownload() {
|
||||
}),
|
||||
)
|
||||
emit('error', message)
|
||||
} finally {
|
||||
loading.value = false
|
||||
doneNProgress()
|
||||
}
|
||||
loading.value = false
|
||||
doneNProgress()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
const API_BASE_URL = 'http://localhost/api/v1/'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
confirm: vi.fn(),
|
||||
doneNProgress: vi.fn(),
|
||||
startNProgress: vi.fn(),
|
||||
toastError: vi.fn(),
|
||||
@@ -27,6 +28,10 @@ vi.mock('vue-toastification', () => ({
|
||||
useToast: () => ({ error: mocks.toastError, success: mocks.toastSuccess }),
|
||||
}))
|
||||
|
||||
vi.mock('@/composables/useConfirm', () => ({
|
||||
useConfirm: () => mocks.confirm,
|
||||
}))
|
||||
|
||||
type SelectItem = string | { title: string; value: string }
|
||||
|
||||
const SelectStub = defineComponent({
|
||||
@@ -297,6 +302,7 @@ describe('AddDownloadDialog directories', () => {
|
||||
describe('AddDownloadDialog submissions', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.confirm.mockResolvedValue(false)
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
vi.spyOn(console, 'log').mockImplementation(() => {})
|
||||
})
|
||||
@@ -441,6 +447,72 @@ describe('AddDownloadDialog submissions', () => {
|
||||
expect(mocks.doneNProgress).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('retries an unrecognized download after confirmation', async () => {
|
||||
const submitted: Array<Record<string, unknown>> = []
|
||||
server.use(
|
||||
http.post(new URL('download/add', API_BASE_URL).href, async ({ request }) => {
|
||||
submitted.push((await request.json()) as Record<string, unknown>)
|
||||
if (submitted.length === 1) {
|
||||
return HttpResponse.json({
|
||||
data: { requires_confirmation: true },
|
||||
message: '无法识别媒体信息',
|
||||
success: false,
|
||||
})
|
||||
}
|
||||
return HttpResponse.json({ data: { download_id: 'collection-download' }, success: true })
|
||||
}),
|
||||
)
|
||||
mocks.confirm.mockResolvedValue(true)
|
||||
const user = userEvent.setup()
|
||||
const { events, torrent } = await renderDialog()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '开始下载' }))
|
||||
|
||||
await waitFor(() => expect(events.done).toHaveBeenCalledWith(torrent.enclosure))
|
||||
expect(mocks.confirm).toHaveBeenCalledWith({
|
||||
type: 'warn',
|
||||
title: '无法识别媒体信息',
|
||||
content: '无法识别此资源的媒体信息,是否仍要下载?',
|
||||
confirmText: '继续下载',
|
||||
})
|
||||
expect(submitted).toHaveLength(2)
|
||||
expect(submitted[0]).not.toHaveProperty('allow_unrecognized')
|
||||
expect(submitted[1]).toEqual({
|
||||
...submitted[0],
|
||||
allow_unrecognized: true,
|
||||
})
|
||||
expect(events.error).not.toHaveBeenCalled()
|
||||
expect(mocks.toastError).not.toHaveBeenCalled()
|
||||
expect(mocks.toastSuccess).toHaveBeenCalledWith('测试站 测试种子 下载成功!')
|
||||
expect(mocks.startNProgress).toHaveBeenCalledOnce()
|
||||
expect(mocks.doneNProgress).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('keeps the download dialog open when unrecognized download confirmation is cancelled', async () => {
|
||||
const submitted = vi.fn()
|
||||
server.use(
|
||||
downloadHandler(
|
||||
'download/add',
|
||||
{ data: { requires_confirmation: true }, message: '无法识别媒体信息', success: false },
|
||||
200,
|
||||
submitted,
|
||||
),
|
||||
)
|
||||
const user = userEvent.setup()
|
||||
const { events } = await renderDialog()
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '开始下载' }))
|
||||
|
||||
await waitFor(() => expect(mocks.confirm).toHaveBeenCalledOnce())
|
||||
expect(submitted).toHaveBeenCalledOnce()
|
||||
expect(events.done).not.toHaveBeenCalled()
|
||||
expect(events.error).not.toHaveBeenCalled()
|
||||
expect(mocks.toastSuccess).not.toHaveBeenCalled()
|
||||
expect(mocks.toastError).not.toHaveBeenCalled()
|
||||
expect(mocks.doneNProgress).toHaveBeenCalledOnce()
|
||||
expect(screen.getByRole('button', { name: '开始下载' })).not.toBeDisabled()
|
||||
})
|
||||
|
||||
it('treats success:false at HTTP 200 as a business failure', async () => {
|
||||
server.use(downloadHandler('download/add', { data: null, message: '下载器拒绝任务', success: false }))
|
||||
const user = userEvent.setup()
|
||||
@@ -450,6 +522,7 @@ describe('AddDownloadDialog submissions', () => {
|
||||
|
||||
await waitFor(() => expect(events.error).toHaveBeenCalledWith('下载器拒绝任务'))
|
||||
expect(events.done).not.toHaveBeenCalled()
|
||||
expect(mocks.confirm).not.toHaveBeenCalled()
|
||||
expect(mocks.toastError).toHaveBeenCalledWith('测试站 测试种子 下载失败:下载器拒绝任务!')
|
||||
expect(mocks.startNProgress).toHaveBeenCalledOnce()
|
||||
expect(mocks.doneNProgress).toHaveBeenCalledOnce()
|
||||
|
||||
@@ -3012,6 +3012,9 @@ export default {
|
||||
startDownload: 'Start Download',
|
||||
downloadSuccess: '{site} {title} downloaded successfully!',
|
||||
downloadFailed: '{site} {title} download failed: {message}!',
|
||||
unrecognizedTitle: 'Media Information Not Recognized',
|
||||
unrecognizedContent: 'Media information for this resource could not be recognized. Download it anyway?',
|
||||
continueDownload: 'Continue Download',
|
||||
showAdvancedOptions: 'Show Advanced Options',
|
||||
hideAdvancedOptions: 'Hide Advanced Options',
|
||||
},
|
||||
|
||||
@@ -2960,6 +2960,9 @@ export default {
|
||||
startDownload: '开始下载',
|
||||
downloadSuccess: '{site} {title} 下载成功!',
|
||||
downloadFailed: '{site} {title} 下载失败:{message}!',
|
||||
unrecognizedTitle: '无法识别媒体信息',
|
||||
unrecognizedContent: '无法识别此资源的媒体信息,是否仍要下载?',
|
||||
continueDownload: '继续下载',
|
||||
showAdvancedOptions: '显示高级选项',
|
||||
hideAdvancedOptions: '隐藏高级选项',
|
||||
},
|
||||
|
||||
@@ -2959,6 +2959,9 @@ export default {
|
||||
startDownload: '開始下載',
|
||||
downloadSuccess: '{site} {title} 下載成功!',
|
||||
downloadFailed: '{site} {title} 下載失敗:{message}!',
|
||||
unrecognizedTitle: '無法識別媒體資訊',
|
||||
unrecognizedContent: '無法識別此資源的媒體資訊,是否仍要下載?',
|
||||
continueDownload: '繼續下載',
|
||||
showAdvancedOptions: '顯示高級選項',
|
||||
hideAdvancedOptions: '隱藏高級選項',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user