mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-25 08:01:20 -08:00
fix(multiple-subtitles): render overlapping cues on legacy hosts [PKG-MULTI-SUB-10]
This commit is contained in:
1 parent
31f9024bff
commit
8ae2762a23
24 files changed
+3465
-56
No files matched your search
@@ -266,3 +266,14 @@ syntax failures remain in the historical parser tests. Mobile cases use an Andro
|
||||
UA and synthetic DOM touches through the real core handlers, not trusted hardware
|
||||
input. Fullscreen mouse placement first waits for the progress control to be stable;
|
||||
the earlier hidden-preview screenshot timeout remains in the checkpoint evidence.
|
||||
|
||||
`multiple-subtitles-legacy.spec.js` uses real 5.1.2/5.1.7 cores and the candidate
|
||||
plugin to check three independently timed captions, VTT/SRT, semantic HTML/CSS,
|
||||
the original update method and native cue identities, scalar event payloads,
|
||||
repeated updates, selection/reset and empty intervals. It uses ASS for the third
|
||||
track on5.1.7; the5.1.2 published converter's malformed VTT remains a separate
|
||||
task11 failure and is not covered by substituting a VTT fixture. Use
|
||||
`ARTPLAYER_MULTIPLE_SUBTITLES_ARTIFACT` for actual main/legacy package bytes.
|
||||
The combination suite retains explicit published1.1/1.2 overlap-loss observations
|
||||
while requiring candidate captions to contain both languages. A passing historical
|
||||
observation is not a compatibility pass.
|
||||
@@ -172,18 +172,19 @@ for (const core of ['published-5.1.2', 'published-5.1.7']) {
|
||||
await window.art.plugins.add(window.multipleFactory({ subtitles: [{ name: 'a', url: '/test/combination.vtt' }, { name: 'b', url: '/test/combination.srt' }] }))
|
||||
})
|
||||
const indexMerge = implementation.version === '1.0.0'
|
||||
const displaysBoth = indexMerge || implementation.name === 'candidate'
|
||||
await expect.poll(() => page.evaluate(() => window.art.template.$track.track.cues?.length)).toBe(indexMerge ? 1 : 2)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1)
|
||||
await page.locator('#pause').click()
|
||||
await seek(page, 1.5)
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveText('First')
|
||||
// This is a deficiency probe, not successful simultaneous-caption acceptance.
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveCount(indexMerge ? 1 : 0)
|
||||
// Historical 1.1/1.2 failures remain observations; candidate must display both.
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveCount(displaysBoth ? 1 : 0)
|
||||
const state = await page.evaluate(() => ({ native: Array.from(window.art.template.$track.track.activeCues, cue => cue.getCueAsHTML().textContent), displayed: window.art.template.$subtitle.textContent }))
|
||||
expect(state.native).toEqual(indexMerge ? ['FirstTranslation'] : ['First', 'Translation'])
|
||||
expect(state.displayed).toBe(indexMerge ? 'FirstTranslation' : 'First')
|
||||
await testInfo.attach('single-active-cue-boundary', { contentType: 'application/json', body: JSON.stringify({ core, implementation: implementation.name, state, simultaneousCaptionsAccepted: indexMerge, unresolvedCandidateGap: !indexMerge, explanation: '1.0.0 combines by array index; 1.1.0 onward retains separately timed cues. Old core displays only its first active cue. Candidate gap remains open, even when this observation test passes.' }) })
|
||||
expect(state.displayed).toBe(displaysBoth ? 'FirstTranslation' : 'First')
|
||||
await testInfo.attach('single-active-cue-boundary', { contentType: 'application/json', body: JSON.stringify({ core, implementation: implementation.name, state, simultaneousCaptionsAccepted: displaysBoth, historicalCaptionLoss: !displaysBoth, explanation: '1.0.0 combines by array index; 1.1/1.2 retain separate cues but the old core displays only its first. Candidate adapts the old view while retaining separate native timings.' }) })
|
||||
await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks(['b']))
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveText('Translation')
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveCount(0)
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { hash } from '../../refactor/scripts/releases.mjs'
|
||||
import { multipleSubtitlesCandidate } from '../helpers/multiple-subtitles.js'
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
const candidate = await multipleSubtitlesCandidate()
|
||||
|
||||
for (const core of ['published-5.1.2', 'published-5.1.7']) {
|
||||
test(`Multiple subtitles legacy host ${core}: independently timed captions overlap and clear`, async ({ page, browser }, testInfo) => {
|
||||
await page.route('**/test/legacy-a.vtt', route => route.fulfill({ contentType: 'text/vtt', body: 'WEBVTT\n\n00:01.000 --> 00:03.000\n<b>A & safe</b>\n' }))
|
||||
await page.route('**/test/legacy-b.srt', route => route.fulfill({ contentType: 'text/plain', body: '1\n00:00:02,000 --> 00:00:04,000\nB\n' }))
|
||||
await page.route('**/test/legacy-c.ass', route => route.fulfill({ contentType: 'text/plain', body: 'Dialogue: 0,0:00:02.25,0:00:02.75,Default,,0,0,0,,C' }))
|
||||
await page.route('**/test/legacy-c.vtt', route => route.fulfill({ contentType: 'text/vtt', body: 'WEBVTT\n\n00:02.250 --> 00:02.750\nC\n' }))
|
||||
await page.goto(`/test/player.html?core=${core}`)
|
||||
await page.addScriptTag({ content: candidate.code })
|
||||
await page.addStyleTag({ content: '.art-subtitle-a { color: rgb(255, 0, 0); } .art-subtitle-b { color: rgb(0, 0, 255); }' })
|
||||
await page.evaluate(() => {
|
||||
window.createPlayer('/test/pattern.mp4')
|
||||
window.originalSubtitleUpdate = window.art.subtitle.update
|
||||
window.legacyPayloads = []
|
||||
window.art.on('subtitleUpdate', text => window.legacyPayloads.push(text))
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
||||
const thirdFormat = core === 'published-5.1.2' ? 'vtt' : 'ass'
|
||||
await page.evaluate(async (thirdFormat) => {
|
||||
await window.art.plugins.add(window.artplayerPluginMultipleSubtitles({ subtitles: [{ name: 'a', url: '/test/legacy-a.vtt' }, { name: 'b', url: '/test/legacy-b.srt' }, { name: 'c', url: `/test/legacy-c.${thirdFormat}` }] }))
|
||||
}, thirdFormat)
|
||||
await expect.poll(() => page.evaluate(() => window.art.template.$track.track.cues?.length)).toBe(3)
|
||||
await page.evaluate(() => {
|
||||
window.cueIdentities = Array.from(window.art.template.$track.track.cues)
|
||||
})
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.1)
|
||||
await page.locator('#pause').click()
|
||||
async function seek(time) {
|
||||
await page.evaluate(time => new Promise((resolve) => {
|
||||
window.art.once('video:seeked', resolve)
|
||||
window.art.seek = time
|
||||
}), time)
|
||||
}
|
||||
await seek(1.5)
|
||||
await expect(page.locator('.art-subtitle-a b')).toHaveText('A & safe')
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveCount(0)
|
||||
await seek(2.5)
|
||||
await expect(page.locator('.art-subtitle-a b')).toHaveText('A & safe')
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveText('B')
|
||||
await expect(page.locator('.art-subtitle-c')).toHaveText('C')
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveCSS('color', 'rgb(255, 0, 0)')
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveCSS('color', 'rgb(0, 0, 255)')
|
||||
const state = await page.evaluate(() => {
|
||||
const art = window.art
|
||||
art.subtitle.update()
|
||||
art.subtitle.update()
|
||||
const cues = Array.from(art.template.$track.track.cues)
|
||||
return { sameUpdate: art.subtitle.update === window.originalSubtitleUpdate, sameCues: cues.every((cue, index) => cue === window.cueIdentities[index]), times: cues.map(cue => [cue.startTime, cue.endTime]), legacyPayload: window.legacyPayloads.at(-1), firstText: art.template.$track.track.activeCues[0].text, counts: ['a', 'b', 'c'].map(name => art.template.$subtitle.querySelectorAll(`.art-subtitle-${name}`).length) }
|
||||
})
|
||||
expect(state.sameUpdate).toBe(true)
|
||||
expect(state.sameCues).toBe(true)
|
||||
expect(state.times).toEqual([[1, 3], [2, 4], [2.25, 2.75]])
|
||||
expect(state.legacyPayload).toBe(state.firstText)
|
||||
expect(state.counts).toEqual([1, 1, 1])
|
||||
await seek(3.5)
|
||||
await expect(page.locator('.art-subtitle-a')).toHaveCount(0)
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveText('B')
|
||||
await expect(page.locator('.art-subtitle-c')).toHaveCount(0)
|
||||
await page.evaluate(() => window.art.plugins.multipleSubtitles.tracks([]))
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveCount(0)
|
||||
await page.evaluate(() => window.art.plugins.multipleSubtitles.reset())
|
||||
await expect(page.locator('.art-subtitle-b')).toHaveText('B')
|
||||
await seek(2.5)
|
||||
await expect(page.locator('.art-subtitle-c')).toHaveText('C')
|
||||
await seek(4.5)
|
||||
await expect(page.locator('.art-subtitle-a, .art-subtitle-b, .art-subtitle-c')).toHaveCount(0)
|
||||
await testInfo.attach('legacy-captions', { contentType: 'application/json', body: JSON.stringify({ core, browser: browser.version(), pluginSha256: hash(candidate.code), thirdFormat, state, scope: 'Native VTT/SRT overlaps (ASS additionally on 5.1.7), semantic HTML/CSS, legacy scalar event payload, original host method and cue identities, repeated update/selection/reset/empty intervals', limitation: '5.1.2 published ASS converter loses required VTT line breaks; tracked separately in PKG-MULTI-SUB-11.' }) })
|
||||
await page.evaluate(() => window.art.destroy())
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user