@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user