适配讲解详情多语言声线播放
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
lyf
2026-07-25 01:04:42 +08:00
parent cb6da6590d
commit 7001ec355a
8 changed files with 567 additions and 31 deletions

View File

@@ -78,6 +78,7 @@ export interface BackendHall {
stopCount?: number | null
linkedExhibitCount?: number | null
audioReadyStopCount?: number | null
audioOptionCount?: number | null
hasAudio?: boolean | null
audioStatus?: string | null
supportedLanguages?: string[] | null
@@ -158,6 +159,7 @@ export interface BackendCatalogStopItem {
hasAudio?: boolean | null
audioStatus?: string | null
supportedLanguages?: string[] | null
audioOptionCount?: number | null
hasTextRecord?: boolean | null
playTargetType?: string | null
playTargetId?: string | number | null
@@ -287,6 +289,7 @@ export const toCatalogHall = (
stopCount: normalizeNumber(source.stopCount),
linkedExhibitCount: normalizeNumber(source.linkedExhibitCount),
audioReadyStopCount: normalizeNumber(source.audioReadyStopCount),
audioOptionCount: normalizeNumber(source.audioOptionCount),
hasAudio: source.hasAudio === true,
audioStatus: normalizeCatalogAudioStatus(source.audioStatus),
supportedLanguages: normalizeSupportedLanguages(source.supportedLanguages),
@@ -359,6 +362,7 @@ export const toCatalogGuideStop = (
hasAudio: source.hasAudio === true,
audioStatus: normalizeCatalogAudioStatus(source.audioStatus),
supportedLanguages: normalizeSupportedLanguages(source.supportedLanguages),
audioOptionCount: normalizeNumber(source.audioOptionCount),
hasTextRecord: source.hasTextRecord === true,
poiId: stringifyId(source.poiId) || undefined,
mapX: normalizeNumber(source.mapX),

View File

@@ -6,6 +6,7 @@ import {
} from '@/utils/publicUrl'
export type GuideAudioLanguage = 'zh-CN' | 'yue-HK' | 'en-US'
export type GuideAudioVoiceGender = 'female' | 'male'
export interface BackendGuideStopLinkedExhibit {
id?: string | number | null
@@ -42,12 +43,26 @@ export interface BackendGuideStopInfo {
hasText?: boolean
supportedLanguages?: string[] | null
audioStatus?: string | null
audioOptions?: BackendGuideStopAudioOption[] | null
languageVariants?: BackendGuideStopLanguageVariant[] | null
reason?: string | null
linkedExhibitCount?: number | string | null
isSharedStop?: boolean | null
}
export interface BackendGuideStopAudioOption {
channelCode?: string | null
displayName?: string | null
languageCode?: string | null
languageName?: string | null
gender?: string | null
playUrl?: string | null
duration?: number | string | null
format?: string | null
isDefault?: boolean | null
sortOrder?: number | string | null
}
export interface BackendGuideStopLanguageVariant {
lang?: string | null
enabled?: boolean | null
@@ -129,6 +144,19 @@ export interface GuideStopLanguageVariant {
reason?: string
}
export interface GuideStopAudioOption {
channelCode: string
displayName: string
languageCode: GuideAudioLanguage
languageName?: string
gender: GuideAudioVoiceGender
playUrl: string
duration?: number
format?: string
isDefault: boolean
sortOrder?: number
}
export interface GuideStopInfo {
available: boolean
targetType: AudioPlayTargetType
@@ -151,6 +179,7 @@ export interface GuideStopInfo {
hasAudio: boolean
hasText: boolean
supportedLanguages: GuideAudioLanguage[]
audioOptions: GuideStopAudioOption[]
languageVariants: Record<GuideAudioLanguage, GuideStopLanguageVariant>
audioStatus: 'READY' | 'MISSING' | string
reason?: string
@@ -264,12 +293,57 @@ const normalizeLanguageVariants = (
return normalizedVariants
}
const normalizeVoiceGender = (value: string | null | undefined): GuideAudioVoiceGender | undefined => {
const normalized = value?.trim().toLowerCase()
return normalized === 'female' || normalized === 'male' ? normalized : undefined
}
const normalizeAudioOptions = (
options: BackendGuideStopAudioOption[] | null | undefined
): GuideStopAudioOption[] => (
(options || [])
.map<GuideStopAudioOption | null>((option) => {
if (!isSupportedGuideAudioLanguage(option.languageCode)) return null
const channelCode = option.channelCode?.trim()
const gender = normalizeVoiceGender(option.gender)
const playUrl = normalizeSameOriginPublicUrl(option.playUrl) || undefined
if (!channelCode || !gender || !playUrl) return null
const languageCode = normalizeGuideAudioLanguage(option.languageCode)
const displayName = option.displayName?.trim()
|| `${languageCode === 'en-US' ? '英文' : languageCode === 'yue-HK' ? '粤语' : '普通话'}${gender === 'female' ? '女声' : '男声'}`
return {
channelCode,
displayName,
languageCode,
languageName: option.languageName?.trim() || undefined,
gender,
playUrl,
duration: normalizeNumber(option.duration),
format: option.format?.trim() || undefined,
isDefault: option.isDefault === true,
sortOrder: normalizeNumber(option.sortOrder)
}
})
.filter(Boolean)
.sort((left, right) => (
(left!.sortOrder ?? Number.MAX_SAFE_INTEGER) - (right!.sortOrder ?? Number.MAX_SAFE_INTEGER)
|| left!.channelCode.localeCompare(right!.channelCode)
)) as GuideStopAudioOption[]
)
const supportedLanguagesFromVariants = (variants: Record<GuideAudioLanguage, GuideStopLanguageVariant>) => (
(Object.values(variants) as GuideStopLanguageVariant[])
.filter((variant) => variant.enabled && (variant.playable || variant.textAvailable))
.map((variant) => variant.lang)
)
const supportedLanguagesFromAudioOptions = (audioOptions: GuideStopAudioOption[]) => (
Array.from(new Set(audioOptions.map((option) => option.languageCode)))
)
const parseGalleryUrls = (value: BackendGuideStopInfo['galleryUrls'] | BackendGuideStopLinkedExhibit['galleryUrls']) => {
if (!value) return []
if (Array.isArray(value)) {
@@ -337,7 +411,10 @@ export const toGuideStopInfo = (
? normalizeSameOriginPublicUrl(source.coverImageUrl) || undefined
: undefined
const languageVariants = normalizeLanguageVariants(source.languageVariants)
const supportedLanguages = supportedLanguagesFromVariants(languageVariants)
const audioOptions = normalizeAudioOptions(source.audioOptions)
const supportedLanguages = audioOptions.length
? supportedLanguagesFromAudioOptions(audioOptions)
: supportedLanguagesFromVariants(languageVariants)
return {
available: source.available === true,
@@ -363,6 +440,7 @@ export const toGuideStopInfo = (
supportedLanguages: supportedLanguages.length
? supportedLanguages
: normalizeSupportedLanguages(source.supportedLanguages),
audioOptions,
languageVariants,
audioStatus: source.audioStatus || 'MISSING',
reason: source.reason || undefined,