Last active
February 11, 2024 07:48
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
utf8 support please. renaming srt files to ansii pain