Recently Written · git

subread.koplugin

KOReader plugin: the book follows the narration of an audiobook, from an .srt made by subread.space

git clone https://github.com/equwal/subread.koplugin

Log | Files | Refs


spec/cues_spec.lua (5102 bytes)

1 local Cues = require("subread.cues")
2 local Srt = require("subread.srt")
3 
4 local function build(triples)
5     local list = {}
6     for _, t in ipairs(triples) do
7         list[#list + 1] = {
8             start = t[1], stop = t[2], text = t[3],
9             norm = t[3], no_place = false,
10         }
11     end
12     return Cues.new(list)
13 end
14 
15 local SIMPLE = {
16     { 0, 2, "alpha one" },
17     { 2, 4, "beta two" },
18     { 4, 6, "gamma three" },
19     { 8, 10, "delta four" }, -- a gap from 6 to 8
20 }
21 
22 describe("SubRead cues", function()
23     it("counts the cues", function()
24         assert.equals(4, build(SIMPLE):count())
25         assert.equals(0, Cues.new({}):count())
26     end)
27 
28     it("finds the last cue that started", function()
29         local index = build(SIMPLE)
30         assert.equals(0, index:lastStartedAt(-1))
31         assert.equals(1, index:lastStartedAt(0))
32         assert.equals(1, index:lastStartedAt(1.9))
33         assert.equals(3, index:lastStartedAt(7))
34         assert.equals(4, index:lastStartedAt(100))
35     end)
36 
37     it("finds the cue that is on", function()
38         local index = build(SIMPLE)
39         assert.equals(1, index:findByTime(0))
40         assert.equals(1, index:findByTime(1.99))
41         assert.equals(2, index:findByTime(2))
42         assert.equals(4, index:findByTime(9))
43     end)
44 
45     it("returns nil in a gap and outside the file", function()
46         local index = build(SIMPLE)
47         assert.is_nil(index:findByTime(7))
48         assert.is_nil(index:findByTime(-1))
49         assert.is_nil(index:findByTime(11))
50     end)
51 
52     it("agrees with a slow reference for every time", function()
53         local index = build(SIMPLE)
54         local function reference(t)
55             local best
56             for i, cue in ipairs(index.list) do
57                 if cue.start <= t and t < cue.stop then best = i end
58             end
59             return best
60         end
61         for step = -20, 240 do
62             local t = step / 20
63             assert.equals(reference(t), index:findByTime(t))
64         end
65     end)
66 
67     it("lets the latest overlapping cue win", function()
68         local index = build({
69             { 0, 100, "long background cue" },
70             { 10, 12, "short cue on top" },
71         })
72         assert.equals(1, index:findByTime(5))
73         assert.equals(2, index:findByTime(11))
74         -- After the short cue ends the long one is still on.
75         assert.equals(1, index:findByTime(20))
76     end)
77 
78     it("finds the next cue start", function()
79         local index = build(SIMPLE)
80         assert.equals(1, index:nextAfter(-5))
81         assert.equals(2, index:nextAfter(0))
82         assert.equals(4, index:nextAfter(6))
83         assert.is_nil(index:nextAfter(8))
84     end)
85 
86     it("steps over cues that share a start time", function()
87         local index = build({ { 0, 1, "a" }, { 5, 6, "b" }, { 5, 7, "c" }, { 9, 10, "d" } })
88         assert.equals(4, index:nextAfter(5))
89     end)
90 
91     it("finds the nearest cue for a start time", function()
92         local index = build(SIMPLE)
93         assert.equals(1, index:findNearest(-3))
94         assert.equals(3, index:findNearest(6.5)) -- gap, nearer the cue before
95         assert.equals(4, index:findNearest(7.9)) -- gap, nearer the cue after
96         assert.equals(4, index:findNearest(50))
97         assert.is_nil(Cues.new({}):findNearest(1))
98     end)
99 
100     it("finds the cue that holds a piece of text", function()
101         local index = build(SIMPLE)
102         assert.equals(2, index:findIndexContaining("beta"))
103         assert.is_nil(index:findIndexContaining("missing"))
104         assert.is_nil(index:findIndexContaining(""))
105     end)
106 
107     it("starts the text search at the given index", function()
108         local index = build({ { 0, 1, "same words" }, { 1, 2, "same words" } })
109         assert.equals(1, index:findIndexContaining("same"))
110         assert.equals(2, index:findIndexContaining("same", 2))
111     end)
112 
113     it("treats the needle as plain text, not a pattern", function()
114         local index = build({ { 0, 1, "cost is 5 % of it" } })
115         assert.equals(1, index:findIndexContaining("5 %"))
116     end)
117 
118     it("finds the cue for a page of book text", function()
119         local index = build(SIMPLE)
120         -- Book text with other white space than the cue text.
121         assert.equals(2, index:findIndexForText("beta\n   two and more words"))
122         assert.is_nil(index:findIndexForText("nothing here at all matches"))
123         assert.is_nil(index:findIndexForText(""))
124     end)
125 
126     it("skips over page text that no cue holds", function()
127         local index = build({ { 0, 1, "the real sentence of the book" } })
128         -- The page starts with a running head that is not in any cue.
129         local page = "CHAPTER ONE 12 the real sentence of the book"
130         assert.equals(1, index:findIndexForText(page))
131     end)
132 
133     it("builds from a parsed file", function()
134         local data = "1\n00:00:00,000 --> 00:00:01,000\nhello there friend\n"
135         local index = Cues.new(Srt.parse(data))
136         assert.equals(1, index:count())
137         assert.equals(1, index:findByTime(0.5))
138         assert.equals("hello there friend", index:get(1).norm)
139     end)
140 end)