* feat(playback): add IsPGS helper and sup streaming extract path PGS (Blu-ray bitmap) subtitle tracks can be copied losslessly into a .sup elementary stream for client-side rendering, so they no longer have to be burned in. streamExtractOutput maps PGS to (copy, sup), and the seek/-t windowing now skips PGS like ASS: both formats are fetched once and consumed whole by their client-side renderers. This also fixes a pre-existing truncation bug: the -t duration cap was applied unconditionally, cutting embedded ASS extracts off at the default 600s window even though the ASS client fetches the full track. Extract the ffmpeg argument construction into streamExtractArgs for testability, following the buildFFmpegArgs pattern. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(api): expose PGS subtitle tracks as .sup stream URLs PGS tracks were filtered out of /playback/start subtitle_urls entirely, so the web player showed no subtitles for PGS-only files (#34). Include them with a .sup URL extension; DVD/DVB bitmap tracks stay hidden since they still have no non-burn-in delivery path. HandleSubtitle streams the full PGS track as application/octet-stream. The seek/duration window is forced to zero for sup: subtitleSeekPosition falls back to the session's last reported position even without a ?position= query, which would otherwise start the extract mid-file. The proxy-node subtitle handler gets the same sup branch, streaming ffmpeg output directly instead of buffering like its text paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(player): consolidate subtitle codec helpers into subtitleCodecs.ts Rename assSubtitles.ts to subtitleCodecs.ts — the module already labeled every codec, not just ASS — and add isPGSCodec/isBitmapCodec. Replace the duplicated BITMAP_CODECS set in SubtitleTranslateModal with the shared helper so codec lists live in one place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(player): native PGS subtitle rendering via libpgs Render PGS subtitle tracks client-side instead of leaving them unavailable (#34). usePGSSubtitles mirrors the JASSUB hook: when a PGS track is active it lazy-loads libpgs, which fetches the .sup stream in a worker, decodes display sets progressively as bytes arrive, and draws them onto a canvas positioned over the video. The renderer looks up the display set at currentTime + timeOffset, so the HLS stream origin adds and the user-facing delay subtracts — a positive delay shows subtitles later, matching VTT semantics. Offset changes apply through the timeOffset setter without recreating the renderer; track switches, PiP detach, and unmount dispose it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(player): prefer text over bitmap tracks in subtitle auto-select With PGS tracks now listed, an earlier PGS track would win auto-select over a later same-language SRT/ASS track. Deprioritize bitmap codecs within the same source tier — text is lighter to render and styleable — while a PGS track still wins when it is the only language match, and forced-PGS auto-select now works. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
42 lines
1.3 KiB
TypeScript
42 lines
1.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { isASSCodec, isBitmapCodec, isPGSCodec } from "./subtitleCodecs";
|
|
|
|
describe("isPGSCodec", () => {
|
|
it("matches PGS codec names case-insensitively", () => {
|
|
expect(isPGSCodec("pgs")).toBe(true);
|
|
expect(isPGSCodec("hdmv_pgs_subtitle")).toBe(true);
|
|
expect(isPGSCodec("HDMV_PGS_SUBTITLE")).toBe(true);
|
|
});
|
|
|
|
it("rejects non-PGS codecs", () => {
|
|
expect(isPGSCodec("dvd_subtitle")).toBe(false);
|
|
expect(isPGSCodec("ass")).toBe(false);
|
|
expect(isPGSCodec("subrip")).toBe(false);
|
|
expect(isPGSCodec(undefined)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("isBitmapCodec", () => {
|
|
it("covers all image-based codecs", () => {
|
|
expect(isBitmapCodec("pgs")).toBe(true);
|
|
expect(isBitmapCodec("hdmv_pgs_subtitle")).toBe(true);
|
|
expect(isBitmapCodec("dvd_subtitle")).toBe(true);
|
|
expect(isBitmapCodec("dvb_subtitle")).toBe(true);
|
|
});
|
|
|
|
it("rejects text codecs", () => {
|
|
expect(isBitmapCodec("srt")).toBe(false);
|
|
expect(isBitmapCodec("ass")).toBe(false);
|
|
expect(isBitmapCodec(undefined)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("isASSCodec", () => {
|
|
it("matches ASS/SSA only", () => {
|
|
expect(isASSCodec("ass")).toBe(true);
|
|
expect(isASSCodec("ssa")).toBe(true);
|
|
expect(isASSCodec("pgs")).toBe(false);
|
|
expect(isASSCodec(undefined)).toBe(false);
|
|
});
|
|
});
|