From 2f46c198260ccd03206a0b1edc3dafd8aa428f25 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Tue, 9 Jun 2026 17:04:17 +0800 Subject: [PATCH] feat: add subtitle search actions --- src/locales/en-US.ts | 1 + src/locales/zh-CN.ts | 1 + src/locales/zh-TW.ts | 1 + src/pages/resource.vue | 33 ++++++++++++-- src/views/discover/MediaDetailView.vue | 63 ++++++++++++++++++++++++-- 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 80ded98e..312f36ec 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -953,6 +953,7 @@ export default { episodeCount: '{count} Episodes', actions: { searchResource: 'Search Resource', + searchSubtitle: 'Search Subtitle', subscribe: 'Subscribe', playOnline: 'Play Online', playInApp: 'Play in App', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index c22bb7d8..3d74d6df 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -949,6 +949,7 @@ export default { episodeCount: '{count}集', actions: { searchResource: '搜索资源', + searchSubtitle: '搜索字幕', subscribe: '订阅', playOnline: '在线播放', playInApp: 'APP播放', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 9c1e01f9..1a8257e3 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -949,6 +949,7 @@ export default { episodeCount: '{count}集', actions: { searchResource: '搜索資源', + searchSubtitle: '搜索字幕', subscribe: '訂閱', playOnline: '線上播放', playInApp: 'APP播放', diff --git a/src/pages/resource.vue b/src/pages/resource.vue index 128f052a..399b12f2 100644 --- a/src/pages/resource.vue +++ b/src/pages/resource.vue @@ -43,6 +43,7 @@ interface SearchParams { title: string year: string season: string + episode: string sites: string result_type: string } @@ -65,6 +66,7 @@ function createSearchParams(query: LocationQuery): SearchParams { title: query?.title?.toString() ?? '', year: query?.year?.toString() ?? '', season: query?.season?.toString() ?? '', + episode: query?.episode?.toString() ?? '', sites: query?.sites?.toString() ?? '', result_type: query?.result_type?.toString() === 'subtitle' ? 'subtitle' : 'torrent', } @@ -78,6 +80,7 @@ function normalizeSearchParams(params?: Partial | null): SearchPar title: params?.title?.toString() ?? '', year: params?.year?.toString() ?? '', season: params?.season?.toString() ?? '', + episode: params?.episode?.toString() ?? '', sites: params?.sites?.toString() ?? '', result_type: params?.result_type?.toString() === 'subtitle' ? 'subtitle' : 'torrent', } @@ -528,13 +531,22 @@ function buildSearchStreamUrl(params: SearchParams, requestToken?: string) { const isMediaSearch = /^[a-zA-Z]+:/.test(params.keyword) const url = getApiUrl( params.result_type === 'subtitle' - ? 'search/subtitle/title/stream' + ? isMediaSearch + ? `search/subtitle/media/${encodeURIComponent(params.keyword)}/stream` + : 'search/subtitle/title/stream' : isMediaSearch ? `search/media/${encodeURIComponent(params.keyword)}/stream` : 'search/title/stream', ) - if (params.result_type === 'subtitle') { + if (params.result_type === 'subtitle' && isMediaSearch) { + setSearchParam(url.searchParams, 'mtype', params.type) + setSearchParam(url.searchParams, 'title', params.title) + setSearchParam(url.searchParams, 'year', params.year) + setSearchParam(url.searchParams, 'season', params.season) + setSearchParam(url.searchParams, 'episode', params.episode) + setSearchParam(url.searchParams, 'sites', params.sites) + } else if (params.result_type === 'subtitle') { setSearchParam(url.searchParams, 'keyword', params.keyword) setSearchParam(url.searchParams, 'sites', params.sites) } else if (isMediaSearch) { @@ -732,8 +744,21 @@ async function searchByRequest(params: SearchParams, requestToken?: string) { // 静默刷新使用普通请求,保留当前结果直到新数据完整返回,避免返回页面时露出搜索进度态。 async function requestSearchResults(params: SearchParams, requestToken?: string) { let result: { [key: string]: any } + const isMediaSearch = /^[a-zA-Z]+:/.test(params.keyword) // 如果keyword的格式是 xxxx:xxxxx 且:前面的xxxx为字符,则按照媒体ID格式搜索 - if (params.result_type === 'subtitle') { + if (params.result_type === 'subtitle' && isMediaSearch) { + result = await api.get(`search/subtitle/media/${params.keyword}`, { + params: { + mtype: params.type, + title: params.title, + year: params.year, + season: params.season, + episode: params.episode, + sites: params.sites, + _ts: requestToken, + }, + }) + } else if (params.result_type === 'subtitle') { result = await api.get('search/subtitle/title', { params: { keyword: params.keyword, @@ -741,7 +766,7 @@ async function requestSearchResults(params: SearchParams, requestToken?: string) _ts: requestToken, }, }) - } else if (/^[a-zA-Z]+:/.test(params.keyword)) { + } else if (isMediaSearch) { result = await api.get(`search/media/${params.keyword}`, { params: { mtype: params.type, diff --git a/src/views/discover/MediaDetailView.vue b/src/views/discover/MediaDetailView.vue index b74e0143..762f3b35 100644 --- a/src/views/discover/MediaDetailView.vue +++ b/src/views/discover/MediaDetailView.vue @@ -40,6 +40,10 @@ const globalSettings = globalSettingsStore.globalSettings // 用户 Store const userStore = useUserStore() +const canSearch = computed(() => + hasPermission({ is_superuser: userStore.superUser, ...userStore.permissions }, 'search'), +) + // 提示框 const $toast = useToast() @@ -491,9 +495,16 @@ function joinArray(arr: string[]) { return arr.join('、') } +interface MediaSearchOptions { + season?: number | null + episode?: number | null +} + // 开始搜索 -function handleSearch() { +function handleSearch(resultType: 'torrent' | 'subtitle' = 'torrent', options: MediaSearchOptions = {}) { const keyword = getMediaId() + const season = options.season ?? mediaDetail.value.season + const episode = options.episode ?? null router.push({ path: '/resource', query: { @@ -502,8 +513,10 @@ function handleSearch() { area: searchType.value, title: mediaDetail.value.title, year: mediaDetail.value.year, - season: mediaDetail.value.season, + season, + episode, sites: selectedSites.value.join(','), + result_type: resultType, }, }) } @@ -572,6 +585,16 @@ function searchSites(sites: number[]) { handleSearch() } +// 搜索字幕 +function handleSubtitleSearch() { + handleSearch('subtitle') +} + +// 搜索单集字幕 +function handleEpisodeSubtitleSearch(season: number | null, episode: number | null) { + handleSearch('subtitle', { season, episode }) +} + onBeforeMount(() => { getMediaDetail() }) @@ -637,7 +660,7 @@ onBeforeMount(() => { { + + + {{ t('media.actions.searchSubtitle') }} + { class="ms-2" size="small" /> + + + {{ t('media.actions.searchSubtitle') }} +

{{ episode.overview }}