mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-16 13:11:22 +08:00
feat: add subtitle search actions
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -949,6 +949,7 @@ export default {
|
||||
episodeCount: '{count}集',
|
||||
actions: {
|
||||
searchResource: '搜索资源',
|
||||
searchSubtitle: '搜索字幕',
|
||||
subscribe: '订阅',
|
||||
playOnline: '在线播放',
|
||||
playInApp: 'APP播放',
|
||||
|
||||
@@ -949,6 +949,7 @@ export default {
|
||||
episodeCount: '{count}集',
|
||||
actions: {
|
||||
searchResource: '搜索資源',
|
||||
searchSubtitle: '搜索字幕',
|
||||
subscribe: '訂閱',
|
||||
playOnline: '線上播放',
|
||||
playInApp: 'APP播放',
|
||||
|
||||
@@ -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<SearchParams> | 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,
|
||||
|
||||
@@ -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(() => {
|
||||
<VBtn
|
||||
v-if="
|
||||
(mediaDetail.tmdb_id || mediaDetail.douban_id || mediaDetail.bangumi_id) &&
|
||||
hasPermission({ is_superuser: userStore.superUser, ...userStore.permissions }, 'search')
|
||||
canSearch
|
||||
"
|
||||
variant="tonal"
|
||||
color="info"
|
||||
@@ -658,6 +681,21 @@ onBeforeMount(() => {
|
||||
</VList>
|
||||
</VMenu>
|
||||
</VBtn>
|
||||
<VBtn
|
||||
v-if="
|
||||
(mediaDetail.tmdb_id || mediaDetail.douban_id || mediaDetail.bangumi_id) &&
|
||||
canSearch
|
||||
"
|
||||
variant="tonal"
|
||||
color="info"
|
||||
class="ms-2 mb-2"
|
||||
@click="handleSubtitleSearch"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-subtitles-outline" />
|
||||
</template>
|
||||
{{ t('media.actions.searchSubtitle') }}
|
||||
</VBtn>
|
||||
<VBtn
|
||||
v-if="mediaDetail.type === '电影' || mediaDetail.douban_id || mediaDetail.bangumi_id"
|
||||
class="ms-2 mb-2"
|
||||
@@ -816,6 +854,25 @@ onBeforeMount(() => {
|
||||
class="ms-2"
|
||||
size="small"
|
||||
/>
|
||||
<VTooltip v-if="canSearch" location="top">
|
||||
<template #activator="{ props }">
|
||||
<IconBtn
|
||||
class="ms-1"
|
||||
color="info"
|
||||
variant="text"
|
||||
v-bind="props"
|
||||
@click.stop="
|
||||
handleEpisodeSubtitleSearch(
|
||||
season.season_number ?? null,
|
||||
episode.episode_number ?? null,
|
||||
)
|
||||
"
|
||||
>
|
||||
<VIcon icon="mdi-subtitles-outline" size="small" />
|
||||
</IconBtn>
|
||||
</template>
|
||||
<span>{{ t('media.actions.searchSubtitle') }}</span>
|
||||
</VTooltip>
|
||||
</div>
|
||||
<p>{{ episode.overview }}</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user