2 changed files with 192 additions and 0 deletions
@ -0,0 +1,136 @@ |
|||||||
|
import { describe, it } from "vitest"; |
||||||
|
import { |
||||||
|
isSupportedSubtitle, |
||||||
|
getMWCaptionTypeFromUrl, |
||||||
|
parseSubtitles, |
||||||
|
} from "@/backend/helpers/captions"; |
||||||
|
import { MWCaptionType } from "@/backend/helpers/streams"; |
||||||
|
import { ass, srt, visibleSubtitlesTestVtt, vtt } from "./testdata"; |
||||||
|
|
||||||
|
describe("subtitles", () => { |
||||||
|
it("should return true if given url ends with a known subtitle type", ({ |
||||||
|
expect, |
||||||
|
}) => { |
||||||
|
expect(isSupportedSubtitle("https://example.com/test.srt")).toBe(true); |
||||||
|
expect(isSupportedSubtitle("https://example.com/test.vtt")).toBe(true); |
||||||
|
expect(isSupportedSubtitle("https://example.com/test.txt")).toBe(false); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should return corresponding MWCaptionType", ({ expect }) => { |
||||||
|
expect(getMWCaptionTypeFromUrl("https://example.com/test.srt")).toBe( |
||||||
|
MWCaptionType.SRT |
||||||
|
); |
||||||
|
expect(getMWCaptionTypeFromUrl("https://example.com/test.vtt")).toBe( |
||||||
|
MWCaptionType.VTT |
||||||
|
); |
||||||
|
expect(getMWCaptionTypeFromUrl("https://example.com/test.txt")).toBe( |
||||||
|
MWCaptionType.UNKNOWN |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should throw when empty text is given", ({ expect }) => { |
||||||
|
expect(() => parseSubtitles("")).toThrow("Given text is empty"); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should parse srt", ({ expect }) => { |
||||||
|
const parsed = parseSubtitles(srt); |
||||||
|
const parsedSrt = [ |
||||||
|
{ |
||||||
|
type: "caption", |
||||||
|
index: 1, |
||||||
|
start: 0, |
||||||
|
end: 0, |
||||||
|
duration: 0, |
||||||
|
content: "Test", |
||||||
|
text: "Test", |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "caption", |
||||||
|
index: 2, |
||||||
|
start: 0, |
||||||
|
end: 0, |
||||||
|
duration: 0, |
||||||
|
content: "Test", |
||||||
|
text: "Test", |
||||||
|
}, |
||||||
|
]; |
||||||
|
expect(parsed).toHaveLength(2); |
||||||
|
expect(parsed).toEqual(parsedSrt); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should parse vtt", ({ expect }) => { |
||||||
|
const parsed = parseSubtitles(vtt); |
||||||
|
const parsedVtt = [ |
||||||
|
{ |
||||||
|
type: "caption", |
||||||
|
index: 1, |
||||||
|
start: 0, |
||||||
|
end: 4000, |
||||||
|
duration: 4000, |
||||||
|
content: "Where did he go?", |
||||||
|
text: "Where did he go?", |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "caption", |
||||||
|
index: 2, |
||||||
|
start: 3000, |
||||||
|
end: 6500, |
||||||
|
duration: 3500, |
||||||
|
content: "I think he went down this lane.", |
||||||
|
text: "I think he went down this lane.", |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "caption", |
||||||
|
index: 3, |
||||||
|
start: 4000, |
||||||
|
end: 6500, |
||||||
|
duration: 2500, |
||||||
|
content: "What are you waiting for?", |
||||||
|
text: "What are you waiting for?", |
||||||
|
}, |
||||||
|
]; |
||||||
|
expect(parsed).toHaveLength(3); |
||||||
|
expect(parsed).toEqual(parsedVtt); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should parse ass", ({ expect }) => { |
||||||
|
const parsed = parseSubtitles(ass); |
||||||
|
expect(parsed).toHaveLength(3); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should delay subtitles when given a delay", ({ expect }) => { |
||||||
|
const videoTime = 11; |
||||||
|
let delayedSeconds = 0; |
||||||
|
const parsed = parseSubtitles(visibleSubtitlesTestVtt); |
||||||
|
const isVisible = (start: number, end: number, delay: number): boolean => { |
||||||
|
const delayedStart = start / 1000 + delay; |
||||||
|
const delayedEnd = end / 1000 + delay; |
||||||
|
return ( |
||||||
|
Math.max(0, delayedStart) <= videoTime && |
||||||
|
Math.max(0, delayedEnd) >= videoTime |
||||||
|
); |
||||||
|
}; |
||||||
|
const visibleSubtitles = parsed.filter((c) => |
||||||
|
isVisible(c.start, c.end, delayedSeconds) |
||||||
|
); |
||||||
|
expect(visibleSubtitles).toHaveLength(1); |
||||||
|
|
||||||
|
delayedSeconds = 10; |
||||||
|
const delayedVisibleSubtitles = parsed.filter((c) => |
||||||
|
isVisible(c.start, c.end, delayedSeconds) |
||||||
|
); |
||||||
|
expect(delayedVisibleSubtitles).toHaveLength(1); |
||||||
|
|
||||||
|
delayedSeconds = -10; |
||||||
|
const delayedVisibleSubtitles2 = parsed.filter((c) => |
||||||
|
isVisible(c.start, c.end, delayedSeconds) |
||||||
|
); |
||||||
|
expect(delayedVisibleSubtitles2).toHaveLength(1); |
||||||
|
|
||||||
|
delayedSeconds = -20; |
||||||
|
const delayedVisibleSubtitles3 = parsed.filter((c) => |
||||||
|
isVisible(c.start, c.end, delayedSeconds) |
||||||
|
); |
||||||
|
expect(delayedVisibleSubtitles3).toHaveLength(1); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
const srt = ` |
||||||
|
1 |
||||||
|
00:00:00,000 --> 00:00:00,000 |
||||||
|
Test |
||||||
|
|
||||||
|
2 |
||||||
|
00:00:00,000 --> 00:00:00,000 |
||||||
|
Test |
||||||
|
`;
|
||||||
|
const vtt = ` |
||||||
|
WEBVTT |
||||||
|
|
||||||
|
00:00:00.000 --> 00:00:04.000 position:10%,line-left align:left size:35% |
||||||
|
Where did he go? |
||||||
|
|
||||||
|
00:00:03.000 --> 00:00:06.500 position:90% align:right size:35% |
||||||
|
I think he went down this lane. |
||||||
|
|
||||||
|
00:00:04.000 --> 00:00:06.500 position:45%,line-right align:center size:35% |
||||||
|
What are you waiting for? |
||||||
|
`;
|
||||||
|
const ass = `[Script Info]
|
||||||
|
; Generated by Ebby.co |
||||||
|
Title: |
||||||
|
Original Script: |
||||||
|
ScriptType: v4.00+ |
||||||
|
Collisions: Normal |
||||||
|
PlayResX: 384 |
||||||
|
PlayResY: 288 |
||||||
|
PlayDepth: 0 |
||||||
|
Timer: 100.0 |
||||||
|
WrapStyle: 0 |
||||||
|
|
||||||
|
[v4+ Styles] |
||||||
|
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding |
||||||
|
Style: Default, Arial, 16, &H00FFFFFF, &H00000000, &H00000000, &H00000000, 0, 0, 0, 0, 100, 100, 0, 0, 1, 1, 0, 2, 15, 15, 15, 0 |
||||||
|
|
||||||
|
[Events] |
||||||
|
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text |
||||||
|
Dialogue: 0,0:00:10.00,0:00:20.00,Default,,0000,0000,0000,,This is the first subtitle. |
||||||
|
Dialogue: 0,0:00:30.00,0:00:34.00,Default,,0000,0000,0000,,This is the second. |
||||||
|
Dialogue: 0,0:00:34.00,0:00:35.00,Default,,0000,0000,0000,,Third`;
|
||||||
|
|
||||||
|
const visibleSubtitlesTestVtt = `WEBVTT
|
||||||
|
|
||||||
|
00:00:00.000 --> 00:00:10.000 position:10%,line-left align:left size:35% |
||||||
|
Test 1 |
||||||
|
|
||||||
|
00:00:10.000 --> 00:00:20.000 position:90% align:right size:35% |
||||||
|
Test 2 |
||||||
|
|
||||||
|
00:00:20.000 --> 00:00:31.000 position:45%,line-right align:center size:35% |
||||||
|
Test 3 |
||||||
|
`;
|
||||||
|
|
||||||
|
export { vtt, srt, ass, visibleSubtitlesTestVtt }; |
||||||
Loading…
Reference in new issue