Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions mpvacious/subtitles/observer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,7 @@ self.collect_from_all_dialogues = function(n_lines)
return Subtitle:new() -- return a default empty new Subtitle to let consumer handle
end
local text, end_sub = all_dialogs.get_n_text(current_sub, n_lines)
local secondary_text, _
if current_secondary_sub == nil then
secondary_text = ''
else
secondary_text, _ = all_secondary_dialogs.get_n_text(current_secondary_sub, n_lines) -- we'll use main sub's timing
end
local secondary_text = all_secondary_dialogs.get_overlapping_text(current_sub['start'], end_sub['end'])
return Subtitle:new {
['text'] = text,
['secondary'] = secondary_text,
Expand All @@ -261,11 +256,13 @@ self.collect_from_current = function()
if secondary_dialogs.is_empty() then
secondary_dialogs.insert(Subtitle:now('secondary'))
end
local start_time = self.get_timing('start')
local end_time = self.get_timing('end')
return Subtitle:new {
['text'] = dialogs.get_text(),
['secondary'] = secondary_dialogs.get_text(),
['start'] = self.get_timing('start'),
['end'] = self.get_timing('end'),
['secondary'] = secondary_dialogs.get_overlapping_text(start_time, end_time),
['start'] = start_time,
['end'] = end_time,
}
end

Expand Down
77 changes: 74 additions & 3 deletions mpvacious/subtitles/sub_list.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,74 @@ local MAX_SUB_GAP_SECONDS = 20 -- stop joining lines separated by a longer gap
local new_sub_list = function()
local subs_list = {}

local append_text = function(speech, previous_sub, sub)
local lines = {}
local text = sub['text']:gsub('\r\n', '\n'):gsub('\r', '\n')
for line in (text .. '\n'):gmatch('(.-)\n') do
table.insert(lines, line)
end

local overlap = previous_sub and sub['start'] <= previous_sub['end'] and math.min(#speech, #lines) or 0
while overlap > 0 do
local matches = true
for i = 1, overlap do
if speech[#speech - overlap + i] ~= lines[i] then
matches = false
break
end
end
if matches then
break
end
overlap = overlap - 1
end
for i = overlap + 1, #lines do
table.insert(speech, lines[i])
end
end

local get_time = function(position)
local i = position == 'start' and 1 or #subs_list
return subs_list[i][position]
end
local get_text = function()
local speech = {}
local previous_sub = nil
for _, sub in ipairs(subs_list) do
table.insert(speech, sub['text'])
append_text(speech, previous_sub, sub)
previous_sub = sub
end
return table.concat(speech, CONCAT_CHR)
end
local get_n_text = function(sub, n_lines)
local speech = {}
local end_sub = sub
local previous_sub = nil
local n_subs = 0
for _, v in ipairs(subs_list) do
if v['start'] - end_sub['end'] >= MAX_SUB_GAP_SECONDS then
break
end
if v >= sub and #speech < n_lines then
table.insert(speech, v['text'])
if v >= sub and n_subs < n_lines then
append_text(speech, previous_sub, v)
previous_sub = v
end_sub = v
n_subs = n_subs + 1
end
end
return table.concat(speech, CONCAT_CHR), end_sub
end
local get_overlapping_text = function(start_time, end_time)
local speech = {}
local previous_sub = nil
for _, sub in ipairs(subs_list) do
if sub['start'] < end_time and sub['end'] > start_time then
append_text(speech, previous_sub, sub)
previous_sub = sub
end
end
return table.concat(speech, CONCAT_CHR):gsub('%s+', ' '):match('^%s*(.-)%s*$')
end
local insert = function(sub)
if sub == nil or h.is_empty(sub.text) then
return false
Expand Down Expand Up @@ -71,6 +114,7 @@ local new_sub_list = function()
get_time = get_time,
get_text = get_text,
get_n_text = get_n_text,
get_overlapping_text = get_overlapping_text,
insert = insert,
is_empty = function()
return h.is_empty(subs_list)
Expand Down Expand Up @@ -208,6 +252,31 @@ local function test_get_subs_list_returns_array_copy()
h.assert_equals(subs.get_subs_list()[1]['text'], "First line")
end

local function test_text_collection_removes_only_consecutive_line_overlap()
local growing = new_sub_list()
local first = Subtitle:from_text("First line", 0, 1)
growing.insert(first)
growing.insert(Subtitle:from_text("First line\nSecond line", 1, 2))
h.assert_equals(growing.get_text(), "First line\nSecond line")
h.assert_equals(growing.get_n_text(first, 2), "First line\nSecond line")

local subs = new_sub_list()
subs.insert(Subtitle:from_text("Yes", 0, 1))
subs.insert(Subtitle:from_text("No", 1, 2))
subs.insert(Subtitle:from_text("Yes\nAgain", 2, 3))
h.assert_equals(subs.get_text(), "Yes\nNo\nYes\nAgain")
h.assert_equals(subs.get_n_text(subs.get_subs_list()[1], 3), "Yes\nNo\nYes\nAgain")
end

local function test_get_overlapping_text_uses_timing_and_removes_line_overlap()
local subs = new_sub_list()
subs.insert(Subtitle:from_text("Before", 0, 1))
subs.insert(Subtitle:from_text("First line", 1, 2))
subs.insert(Subtitle:from_text("First line\nSecond line", 2, 3))
subs.insert(Subtitle:from_text("After", 3, 4))
h.assert_equals(subs.get_overlapping_text(1, 3), "First line Second line")
end

local function run_tests()
test_insert_rejects_invalid_subs()
test_insert_rejects_duplicate_event()
Expand All @@ -220,6 +289,8 @@ local function run_tests()
test_insert_preserves_sorted_order()
test_get_time_returns_boundary_times()
test_get_subs_list_returns_array_copy()
test_text_collection_removes_only_consecutive_line_overlap()
test_get_overlapping_text_uses_timing_and_removes_line_overlap()
end

return {
Expand Down