Skip to content

Instantly share code, notes, and snippets.

@varenc
Last active February 11, 2024 07:48
Show Gist options
  • Select an option

  • Save varenc/1b117487f78836aa6a25c74cae4fbbed to your computer and use it in GitHub Desktop.

Select an option

Save varenc/1b117487f78836aa6a25c74cae4fbbed to your computer and use it in GitHub Desktop.
Combine sentences in a .srt input subtitle. Lua script that works as a command line/terminal utility.
#!/usr/bin/env lua
-------------------------------------
-- This script takes an .srt file as an argument, and outputs an srt file with the sentences combined.
-- Output is sent to STDOUT. So to convert a .srt file, you can do something like this
--
-- $ chmod +x ./srt_to_sentences.lua
-- $ ./srt_to_sentences.lua input.srt > input-sentences.srt
--
-- Optimized for my use case. Try tweaking the options below if you have issues.
-- note: the key property of this over other solutions I found is that the outputed srt timecodes are a strict
-- subset of the input timecodes.
--
-- based on this mpv script: https://github.com/kelciour/mpv-scripts/blob/master/sub-sentences.lua
--------------------------------------
local USE_CASING = true
local MAX_SENTENCE_TIME = 8
local COMBINE_NEWLINES = true
function round2(number, precision)
local fmtStr = string.format('%%0.%sf',precision)
number = string.format(fmtStr,number)
return number
end
function srt_time_to_seconds(time)
local major, minor = time:match("(%d%d:%d%d:%d%d),(%d%d%d)")
local hours, mins, secs = major:match("(%d%d):(%d%d):(%d%d)")
return hours * 3600 + mins * 60 + secs + minor / 1000
end
function seconds_to_srt_time(time)
local hours = math.floor(time / 3600)
local mins = math.floor(time / 60) % 60
local secs = math.floor(time % 60)
local milliseconds = (time * 1000) % 1000
-- print("TIME=",hours, mins, secs, round2(milliseconds,0))
return string.format("%02d:%02d:%02d,%03d", hours, mins, secs, round2(milliseconds,0))
end
function file_exists(file)
local isExist = io.popen(
'[[ -e "'.. tostring(file) ..'" ]] && { echo "true"; }')
local isIt = isExist:read("*a")
isExist:close()
isIt = string.gsub(isIt, '^%s*(.-)%s*$', '%1')
if isIt == "true" then
return true
end
end
function get_working_dir()
-- local path = mp.get_property("path", "")
local dir, filename = utils.split_path(path)
return dir
end
function open_subtitles_file(srt_file_exts)
--local srt_base_filename = mp.get_property("working-directory") .. "/" .. mp.get_property("filename/no-ext")
local dir = get_working_dir()
local srt_base_filename = utils.join_path(dir,mp.get_property("filename/no-ext"))
for i, ext in ipairs(srt_file_exts) do
local f, err = io.open(srt_base_filename .. ext, "r")
--mp.msg.warn("Attempting to open file ",srt_base_filename .. ext)
if f then
--mp.msg.warn("Success! f=",f)
return f
else
--mp.msg.warn("Failed to open...")
end
end
return false
end
function read_subtitles(srt_path)
-- local f = open_subtitles_file(srt_file_exts)
local f = io.open(srt_path,"r")
if not f then
print("FAILED TO OPEN SRT FILE!", srt_path)
return false
end
local data = f:read("*all")
data = string.gsub(data, "\r\n", "\n")
f:close()
local subs = {}
local subs_start = {}
local subs_end = {}
for start_time, end_time, text in string.gmatch(data, "(%d%d:%d%d:%d%d,%d%d%d) %-%-> (%d%d:%d%d:%d%d,%d%d%d)\n(.-)\n\n") do
table.insert(subs, text)
table.insert(subs_start, srt_time_to_seconds(start_time))
table.insert(subs_end, srt_time_to_seconds(end_time))
end
return subs, subs_start, subs_end
end
function convert_into_sentences(subs, subs_start, subs_end)
local sentences = {}
local sentences_start = {}
local sentences_end = {}
for i, sub_text in ipairs(subs) do
local sub_start = subs_start[i]
local sub_end = subs_end[i]
local sub_text = string.gsub(sub_text, "<[^>]+>", "")
-- sub_text = string.gsub(sub_text,"%.%.%.","…")
-- sub_text = string.gsub(sub_text,"%-%-","_____")
if COMBINE_NEWLINES then
sub_text = string.gsub(sub_text, "\n", "#")
sub_text = string.gsub(sub_text, "#%-", "\n-")
sub_text = string.gsub(sub_text, "#", " ")
end
if sub_text:find("^- ") ~= nil and sub_text:sub(3,3) ~= sub_text:sub(3,3):upper() then
sub_text = string.gsub(sub_text, "^- ", "")
end
if #sentences > 0 then
local prev_sub_start = sentences_start[#sentences]
local prev_sub_end = sentences_end[#sentences]
local prev_sub_text = sentences[#sentences]
-- print("-->",sub_text,(sub_start - prev_sub_end) <= 1000 and sub_text:sub(1,1) ~= '-' and
-- sub_text:sub(1,1) ~= '"' and sub_text:sub(1,1) ~= "'" and sub_text:sub(1,1) ~= '(' and
-- (prev_sub_text:sub(prev_sub_text:len()) ~= "." or prev_sub_text:sub(prev_sub_text:len()-2) == "...") and
-- prev_sub_text:sub(prev_sub_text:len()) ~= "?" and prev_sub_text:sub(prev_sub_text:len()) ~= "!" and
-- (sub_text:sub(1,1) == sub_text:sub(1,1):lower() or prev_sub_text:sub(prev_sub_text:len()) == ","))
if USE_CASING then
casing_result = sub_text:sub(1,1) == sub_text:sub(1,1):lower() or prev_sub_text:sub(prev_sub_text:len()) == "," or prev_sub_text:sub(prev_sub_text:len()-2) == "..."
else
casing_result = true
end
if (sub_start - prev_sub_end) <= MAX_SENTENCE_TIME and sub_text:sub(1,1) ~= '-' and
sub_text:sub(1,1) ~= '"' and sub_text:sub(1,1) ~= "'" and sub_text:sub(1,1) ~= '(' and
(prev_sub_text:sub(prev_sub_text:len()) ~= "." or prev_sub_text:sub(prev_sub_text:len()-2) == "...") and
prev_sub_text:sub(prev_sub_text:len()) ~= "?" and prev_sub_text:sub(prev_sub_text:len()) ~= "!" and casing_result then
local text = sentences[#sentences] .. " " .. sub_text
text = string.gsub(text, "\n", "#")
text = string.gsub(text, "%.%.%. %.%.%.", " ")
text = string.gsub(text, "#%-", "\n-")
text = string.gsub(text, "#", " ")
if text:match("\n%-") ~= nil and text:match("^%-") == nil then
text = "- " .. text
end
sentences[#sentences] = text
sentences_end[#sentences] = sub_end
else
table.insert(sentences, sub_text)
table.insert(sentences_start, sub_start)
table.insert(sentences_end, sub_end)
end
else
table.insert(sentences, sub_text)
table.insert(sentences_start, sub_start)
table.insert(sentences_end, sub_end)
end
end
return sentences, sentences_start, sentences_end
end
function write_subtitles(subs, subs_start, subs_end)
if subs ~= nil then
for i, sub_text in ipairs(subs) do
local sub_start = subs_start[i]
local sub_end = subs_end[i]
io.write(i, "\n")
io.write(seconds_to_srt_time(sub_start) .. " --> " .. seconds_to_srt_time(sub_end), "\n")
io.write(sub_text, "\n\n")
end
return true
end
return false
end
io.stderr:write("input_path=" .. arg[1] .. "\n")
local subs, subs_start, subs_end = read_subtitles(arg[1])
local sentences, sentences_start, sentences_end = convert_into_sentences(subs, subs_start, subs_end)
local ret = write_subtitles(sentences, sentences_start, sentences_end)

ghost commented Feb 11, 2024

Copy link
Copy Markdown

utf8 support please. renaming srt files to ansii pain

Error processing subtitle with Lua script: input_path=test\Böyle konusulur! Helal olsun [s4JHN9VPL8I].0.srt
lua: attempt to index a boolean value
stack traceback:
        [C]: in for iterator 'for iterator'
        C:\Users\Kaguya\scripts\srt_to_sentences.lua:112: in function 'convert_into_sentences'
        C:\Users\Kaguya\scripts\srt_to_sentences.lua:194: in main chunk
        [C]: in ?

Failed to process test\Böyle konuşulur! Helal olsun [s4JHN9VPL8I].0.srt with Lua script.
Processed Böyle konuşulur! Helal olsun [s4JHN9VPL8I].mkv

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment