Files
xuetang/TUICallKit-Vue3/native-adapter/tang-detective/utils/cosMedia.js
T
2026-09-09 14:47:29 +08:00

126 lines
4.4 KiB
JavaScript

// Transport-only lookup. Review eligibility continues to belong to the story
// and release manifests; an HTTPS URL never confers audio approval.
const manifest = require('./cosMediaManifest')
const paths = Object.create(null)
const urls = Object.create(null)
const SHARE_ASSET_ID = 'image.tang.share-preview'
for (const entry of manifest.entries) {
paths[entry.sourcePath] = entry
urls[entry.url] = entry
}
function entryFor(value) {
if (typeof value !== 'string') return null
if (urls[value]) return urls[value]
let relative = value.replace(/^\//, '')
if (relative.startsWith(`${manifest.namespace}/`)) {
relative = relative.slice(manifest.namespace.length + 1)
}
return paths[relative] || null
}
function resolve(value) {
const entry = entryFor(value)
return entry ? entry.url : ''
}
function verifiedUrl(value, sha256, kind) {
const entry = urls[value]
return entry && entry.sha256 === sha256 && entry.kind === kind
? entry.url : ''
}
function mapMedia(value) {
if (typeof value === 'string') {
if (/^\/(?:[a-z0-9-]+\/)?(?:assets|package-[a-z0-9-]+)\/.*\.(?:jpe?g|png|webp|gif|svg|avif|mp3|wav|aac|m4a|ogg|mp4|webm|mov)$/i.test(value)) {
// Unregistered declarations remain text-only; never guess an object URL.
return resolve(value)
}
return value
}
if (Array.isArray(value)) return value.map(mapMedia)
if (!value || typeof value !== 'object') return value
const mapped = {}
Object.keys(value).forEach(key => { mapped[key] = mapMedia(value[key]) })
return mapped
}
function beginComicImage(page, pageData) {
const previous = page._cosComicImage
if (!previous || previous.pageId !== pageData.currentPageId) {
page._cosComicImage = {
pageId: pageData.currentPageId,
generation: (previous ? previous.generation : 0) + 1,
failed: Object.create(null),
lastPatch: null,
}
}
const state = page._cosComicImage
pageData.comicImageGeneration = state.generation
// Ordinary reader interactions re-render this page. They must not revive a
// URL that already failed or replace terminal text fallback with an image.
if (state.lastPatch) Object.assign(pageData, state.lastPatch)
}
function isCurrentComicImageEvent(page, event) {
const state = page._cosComicImage
const dataset = event && event.currentTarget && event.currentTarget.dataset
return Boolean(state && dataset && !page.__tangDead && !page.__tangOriginalUnloaded
&& page.__tangVisible !== false && page.data.comicImageSrc
&& state.pageId === page.data.currentPageId
&& dataset.cosPageId === state.pageId
&& Number(dataset.cosImageGeneration) === state.generation
&& dataset.cosImageSrc === page.data.comicImageSrc)
}
function recordComicImageError(page, event) {
if (!isCurrentComicImageEvent(page, event)) return false
const state = page._cosComicImage
const src = page.data.comicImageSrc
if (state.failed[src]) return false
state.failed[src] = true
return true
}
function canUseComicFallback(page, src) {
const state = page._cosComicImage
const entry = entryFor(src)
return Boolean(state && entry && entry.kind === 'image' && !state.failed[src])
}
function applyComicImageFallback(page, patch) {
if (page._cosComicImage) page._cosComicImage.lastPatch = { ...patch }
page.setData(patch)
}
function prepareSharePreview(page, assetManager) {
if (page._sharePreviewPromise) return page._sharePreviewPromise
const generation = page.__tangShowGeneration
const alive = () => !page.__tangDead && !page.__tangOriginalUnloaded
&& page.__tangVisible !== false && page.__tangShowGeneration === generation
// Resolve again for each share; the manager rehashes cached bytes before use.
const promise = Promise.resolve().then(() => assetManager.resolve(SHARE_ASSET_ID))
.then(result => {
if (!alive()) return ''
const localPath = result && result.available && result.uri || ''
page.setData({ sharePreviewLocalPath: localPath })
return localPath
})
.catch(() => {
if (alive()) page.setData({ sharePreviewLocalPath: '' })
return ''
})
.finally(() => {
if (page._sharePreviewPromise === promise) page._sharePreviewPromise = null
})
page._sharePreviewPromise = promise
return promise
}
module.exports = {
SHARE_ASSET_ID, entryFor, resolve, verifiedUrl, mapMedia, prepareSharePreview,
beginComicImage, isCurrentComicImageEvent, recordComicImageError,
canUseComicFallback, applyComicImageFallback,
}