@@ -5,6 +5,7 @@ import type {
|
||||
ExplainGuideStopPage,
|
||||
ExplainTrack,
|
||||
MediaAsset,
|
||||
MuseumAudioOption,
|
||||
MuseumExhibit,
|
||||
MuseumHall,
|
||||
SearchIndexItem
|
||||
@@ -155,14 +156,36 @@ export class ExplainUseCase {
|
||||
return '中文'
|
||||
}
|
||||
|
||||
private audioOptionForLanguage(
|
||||
exhibit: MuseumExhibit,
|
||||
language: AudioLanguage
|
||||
): MuseumAudioOption | undefined {
|
||||
const options = exhibit.audioOptions?.filter((option) => (
|
||||
option.languageCode === language && Boolean(option.audioUrl?.trim())
|
||||
)) || []
|
||||
|
||||
return options.find((option) => option.isDefault) || options[0]
|
||||
}
|
||||
|
||||
private applyLanguageVariant(exhibit: MuseumExhibit, language: AudioLanguage): MuseumExhibit {
|
||||
const variant = exhibit.audioVariants?.[language]
|
||||
const audioOption = this.audioOptionForLanguage(exhibit, language)
|
||||
const hasAudioOptions = Boolean(exhibit.audioOptions?.length)
|
||||
const mandarinText = exhibit.audioVariants?.['zh-CN']?.text
|
||||
|| exhibit.guideText
|
||||
|| exhibit.description
|
||||
const supportedLanguages = exhibit.supportedLanguages?.length
|
||||
? exhibit.supportedLanguages
|
||||
: exhibit.audioVariants ? Object.keys(exhibit.audioVariants) : [language]
|
||||
: exhibit.audioOptions?.length
|
||||
? Array.from(new Set(exhibit.audioOptions.map((option) => option.languageCode)))
|
||||
: exhibit.audioVariants ? Object.keys(exhibit.audioVariants) : [language]
|
||||
const legacyPlayable = (variant?.playable ?? variant?.available) === true && Boolean(variant?.audioUrl)
|
||||
const playable = hasAudioOptions ? Boolean(audioOption?.audioUrl) : legacyPlayable
|
||||
const audioUrl = hasAudioOptions ? audioOption?.audioUrl : variant?.audioUrl
|
||||
const audioDuration = hasAudioOptions ? audioOption?.duration : variant?.audioDuration
|
||||
const audioStatus = hasAudioOptions
|
||||
? playable ? 'READY' : 'MISSING'
|
||||
: variant?.audioStatus || (playable ? 'READY' : 'MISSING')
|
||||
|
||||
if (!variant) {
|
||||
const label = this.languageLabel(language)
|
||||
@@ -172,18 +195,20 @@ export class ExplainUseCase {
|
||||
return {
|
||||
...exhibit,
|
||||
guideText,
|
||||
audioUrl: undefined,
|
||||
audioDuration: undefined,
|
||||
audioUrl,
|
||||
audioDuration,
|
||||
audioLanguage: language,
|
||||
audioHasText: language === 'yue-HK' && Boolean(mandarinText),
|
||||
audioAvailable: false,
|
||||
audioStatus: 'MISSING',
|
||||
audioUnavailableReason: `当前讲解暂无${label}音频`,
|
||||
audioAvailable: playable,
|
||||
audioStatus,
|
||||
audioUnavailableReason: playable ? undefined : `当前讲解暂无${label}音频`,
|
||||
audioChannelCode: audioOption?.channelCode,
|
||||
audioVoiceGender: audioOption?.gender,
|
||||
audioVoiceDisplayName: audioOption?.displayName,
|
||||
supportedLanguages
|
||||
}
|
||||
}
|
||||
|
||||
const playable = (variant.playable ?? variant.available) === true && Boolean(variant.audioUrl)
|
||||
const textAvailable = variant.textAvailable ?? (variant.hasText === true || Boolean(variant.text))
|
||||
const guideText = variant.text
|
||||
|| (language === 'yue-HK' ? mandarinText : undefined)
|
||||
@@ -193,8 +218,8 @@ export class ExplainUseCase {
|
||||
...exhibit,
|
||||
guideTitle: variant.title || exhibit.guideTitle,
|
||||
guideText,
|
||||
audioUrl: playable ? variant.audioUrl : undefined,
|
||||
audioDuration: variant.audioDuration,
|
||||
audioUrl: playable ? audioUrl : undefined,
|
||||
audioDuration,
|
||||
audioLanguage: language,
|
||||
audioHasText: textAvailable,
|
||||
audioText: variant.text || undefined,
|
||||
@@ -202,10 +227,13 @@ export class ExplainUseCase {
|
||||
audioTextHash: variant.textHash,
|
||||
audioNarrationTier: variant.narrationTier,
|
||||
audioAvailable: playable,
|
||||
audioStatus: variant.audioStatus || (playable ? 'READY' : 'MISSING'),
|
||||
audioStatus,
|
||||
audioUnavailableReason: playable
|
||||
? undefined
|
||||
: audioReasonToText(variant.reason || 'NO_PUBLISHED_AUDIO'),
|
||||
audioChannelCode: audioOption?.channelCode,
|
||||
audioVoiceGender: audioOption?.gender,
|
||||
audioVoiceDisplayName: audioOption?.displayName,
|
||||
supportedLanguages
|
||||
}
|
||||
}
|
||||
@@ -240,10 +268,20 @@ export class ExplainUseCase {
|
||||
fallback: variant.fallback,
|
||||
reason: variant.reason
|
||||
}]))
|
||||
const currentVariant = audioVariants[stopInfo.lang]
|
||||
const audioAvailable = currentVariant?.playable === true && Boolean(currentVariant.audioUrl)
|
||||
const audioOptions: MuseumAudioOption[] = (stopInfo.audioOptions || []).map((option) => ({
|
||||
channelCode: option.channelCode,
|
||||
displayName: option.displayName,
|
||||
languageCode: option.languageCode,
|
||||
languageName: option.languageName,
|
||||
gender: option.gender,
|
||||
audioUrl: option.playUrl,
|
||||
duration: option.duration,
|
||||
format: option.format,
|
||||
isDefault: option.isDefault,
|
||||
sortOrder: option.sortOrder
|
||||
}))
|
||||
|
||||
return {
|
||||
const exhibit: MuseumExhibit = {
|
||||
...(fallback || {}),
|
||||
id: fallback?.id || linkedPrimary?.id || stopInfo.targetId,
|
||||
name: stopInfo.title || fallback?.name || linkedPrimary?.name || '讲解内容',
|
||||
@@ -263,22 +301,16 @@ export class ExplainUseCase {
|
||||
size: fallback?.size,
|
||||
tags: fallback?.tags,
|
||||
guideTitle: stopInfo.title || fallback?.guideTitle,
|
||||
guideText: currentVariant?.text || stopInfo.description || fallback?.guideText || fallback?.description,
|
||||
audioUrl: audioAvailable ? currentVariant?.audioUrl : undefined,
|
||||
audioDuration: currentVariant?.audioDuration,
|
||||
guideText: stopInfo.description || fallback?.guideText || fallback?.description,
|
||||
audioUrl: undefined,
|
||||
audioDuration: undefined,
|
||||
audioLanguage: stopInfo.lang,
|
||||
audioHasText: currentVariant?.textAvailable || stopInfo.hasText,
|
||||
audioText: currentVariant?.text,
|
||||
audioTextLength: currentVariant?.textLength,
|
||||
audioTextHash: currentVariant?.textHash,
|
||||
audioNarrationTier: currentVariant?.narrationTier,
|
||||
audioUnavailableReason: audioAvailable
|
||||
? undefined
|
||||
: audioReasonToText(currentVariant?.reason || stopInfo.reason || (stopInfo.audioStatus === 'MISSING' ? 'NO_PUBLISHED_AUDIO' : undefined)),
|
||||
audioAvailable,
|
||||
audioStatus: currentVariant?.audioStatus || stopInfo.audioStatus,
|
||||
audioHasText: stopInfo.hasText,
|
||||
audioAvailable: false,
|
||||
audioStatus: stopInfo.audioStatus,
|
||||
supportedLanguages: stopInfo.supportedLanguages,
|
||||
audioVariants,
|
||||
audioOptions,
|
||||
imageStatus: stopInfo.imageStatus,
|
||||
imageSource: stopInfo.imageSource,
|
||||
galleryUrls: stopInfo.imageStatus === 'READY' ? stopInfo.galleryUrls : [],
|
||||
@@ -291,6 +323,8 @@ export class ExplainUseCase {
|
||||
playTargetType: audioTarget.targetType,
|
||||
playTargetId: audioTarget.targetId
|
||||
}
|
||||
|
||||
return this.applyLanguageVariant(exhibit, stopInfo.lang)
|
||||
}
|
||||
|
||||
private applyPlayInfo(
|
||||
@@ -467,6 +501,29 @@ export class ExplainUseCase {
|
||||
return this.applyLanguageVariant(exhibit, language)
|
||||
}
|
||||
|
||||
selectExplainDetailAudioOption(exhibit: MuseumExhibit, channelCode: string): MuseumExhibit {
|
||||
const language = exhibit.audioLanguage as AudioLanguage | undefined
|
||||
const audioOption = exhibit.audioOptions?.find((option) => (
|
||||
option.channelCode === channelCode
|
||||
&& option.languageCode === language
|
||||
&& Boolean(option.audioUrl?.trim())
|
||||
))
|
||||
if (!audioOption) return exhibit
|
||||
|
||||
return {
|
||||
...exhibit,
|
||||
audioUrl: audioOption.audioUrl,
|
||||
audioDuration: audioOption.duration,
|
||||
audioLanguage: audioOption.languageCode,
|
||||
audioAvailable: true,
|
||||
audioStatus: 'READY',
|
||||
audioUnavailableReason: undefined,
|
||||
audioChannelCode: audioOption.channelCode,
|
||||
audioVoiceGender: audioOption.gender,
|
||||
audioVoiceDisplayName: audioOption.displayName
|
||||
}
|
||||
}
|
||||
|
||||
async loadExplainDetailText(exhibit: MuseumExhibit): Promise<ExplainTextSelection> {
|
||||
const targetType = exhibit.playTargetType || 'ITEM'
|
||||
const targetId = exhibit.playTargetId || exhibit.id
|
||||
@@ -671,12 +728,12 @@ export class ExplainUseCase {
|
||||
const targetType = exhibit.playTargetType || 'ITEM'
|
||||
const targetId = exhibit.playTargetId || exhibit.id
|
||||
|
||||
if (!options.refreshPlayInfo && exhibit.audioUrl?.trim()) {
|
||||
if ((!options.refreshPlayInfo || exhibit.audioChannelCode) && exhibit.audioUrl?.trim()) {
|
||||
return {
|
||||
exhibit,
|
||||
track: null,
|
||||
media: {
|
||||
id: `static-${targetType}-${targetId}-${exhibit.audioLanguage || dataSourceConfig.audioLanguage}`,
|
||||
id: `static-${exhibit.audioChannelCode || `${targetType}-${targetId}`}-${exhibit.audioLanguage || dataSourceConfig.audioLanguage}`,
|
||||
type: 'audio',
|
||||
url: exhibit.audioUrl,
|
||||
duration: exhibit.audioDuration,
|
||||
|
||||
Reference in New Issue
Block a user