diff --git a/automation/autoload/duetto-meika.lua b/automation/autoload/duetto-meika.lua new file mode 100644 index 000000000..9b99a9c06 --- /dev/null +++ b/automation/autoload/duetto-meika.lua @@ -0,0 +1,177 @@ +local tr = aegisub.gettext + +script_name = tr"Duetto Meika" +script_description = tr"The ultimate tool for karaoke duets" +script_author = "amoethyst" + +include("utils.lua") + + +function replace_style(line, style_name, style_string) + before_style, after_style = line.text:match("^(.-{[^}]-)\\?s:".. style_name .."(.*)$") + return before_style .. style_string .. after_style +end + + +function duetto(subs, sel) + styles = {} + + -- create the style map + for _, line in ipairs(subs) do + if line.class == "style" then + styles[line.name] = line + end + end + + -- duetto~ + for _, i in ipairs(sel) do + line = subs[i] + + current_style = styles[line.style] + -- match every `s:` marker + for style_name in line.text:gmatch("{[^}]*s:([^}\\]*)[^}]*}") do + if style_name ~= current_style.name then + + style = styles[style_name] + -- build the tags to use the new style + style_string = "" + if current_style.color1 ~= style.color1 then + style_string = style_string .. "\\c" .. style.color1 + end + if current_style.color2 ~= style.color2 then + style_string = style_string .. "\\2c" .. style.color2 + end + if current_style.color3 ~= style.color3 then + style_string = style_string .. "\\3c" .. style.color3 + end + if current_style.color4 ~= style.color4 then + style_string = style_string .. "\\4c" .. style.color4 + end + + -- set style + line.text = replace_style(line, style_name, style_string) + current_style = style + else + -- remove marker to not break everything + line.text = replace_style(line, style_name, "") + end + end + subs[i] = line + end + + aegisub.set_undo_point(script_name) +end + + +function test_colors(c1, c2) + return color_from_style(c1) == color_from_style(c2) +end + + +function get_script_style(style, styles) + for key, script_style in pairs(styles) do + if (test_colors(style.color1, script_style.color1) + and test_colors(style.color2, script_style.color2) + and test_colors(style.color3, script_style.color3) + and test_colors(style.color4, script_style.color4) + and tonumber(style.fontsize) == tonumber(script_style.fontsize) + and style.fontname == script_style.fontname) then + return script_style + end + end + return nil +end + + +function deduetto_meika(subs, sel) + local styles = {} + local last_style = -1 + + -- create the style map + for i, line in ipairs(subs) do + if line.class == "style" then + styles[line.name] = line + last_style = i + end + end + + local new_styles = {} + + for _, i in ipairs(sel) do + local line = subs[i] + local current_style = table.copy(styles[line.style]) + + local search_index = 1 + while search_index < #line.text do + local match_start, match_end = line.text:find("{[^}]*}", search_index) + if match_start == nil then + break + end + + local bracketed = line.text:sub(match_start, match_end) + local new_style = false + + -- change style's colors + for tag, value in bracketed:gmatch("\\([1-4]?c)([^}\\]*)") do + new_style = true + if tag == "c" or tag == "1c" then + current_style.color1 = value + elseif tag == "2c" then + current_style.color2 = value + elseif tag == "3c" then + current_style.color3 = value + elseif tag == "4c" then + current_style.color4 = value + end + end + + -- change style's font + for tag, value in bracketed:gmatch("\\(f[sn])([^}\\]*)") do + new_style = true + if tag == "fs" then + current_style.fontsize = value + elseif tag == "fn" then + current_style.fontname = value + end + end + + if new_style then + local script_style = get_script_style(current_style, styles) + if script_style == nil then + if get_script_style(current_style, new_styles) == nil then + new_styles[#new_styles+1] = table.copy(current_style) + end + else + -- remove inline colors + bracketed = bracketed:gsub("\\[1-4]?c[^\\}]*", "") + bracketed = bracketed:gsub("\\[1-4]?a[^\\}]*", "") + -- remove inline fonts + bracketed = bracketed:gsub("\\f[sn][^\\}]*", "") + + -- add style marker + if line.style ~= script_style.name then + bracketed = "{s:" .. script_style.name .. bracketed:sub(2, #bracketed) + end + line.text = line.text:sub(1, match_start-1) .. bracketed .. line.text:sub(match_end + 1, #line.text) + end + end + + search_index = match_start + 1 + end + + subs[i] = line + end + + if #new_styles > 0 then + for i, new_style in ipairs(new_styles) do + new_style.name = "Deduetto style " .. i + subs.insert(last_style, new_style) + last_style = last_style + 1 + aegisub.log("Created new style: " .. new_style.name .. "\n") + end + end + +end + +aegisub.register_macro(script_name, script_description, duetto) +aegisub.register_macro(tr"Deduetto Meika", tr"Create styles from inline color tags", deduetto_meika) diff --git a/automation/autoload/karaoke-adjust-1sec.lua b/automation/autoload/karaoke-adjust-1sec.lua new file mode 100644 index 000000000..73f01afe5 --- /dev/null +++ b/automation/autoload/karaoke-adjust-1sec.lua @@ -0,0 +1,172 @@ +local tr = aegisub.gettext + +script_name = tr"Karaoke 1sec adjust lead-in" +script_description = tr"Adjust karaoke leadin to 1sec" +script_author = "Flore" +script_version = "1.00" + +include("cleantags.lua") + +leadinmsec = 1000 --lead in time can be changed here + +ktag = "\\[kK][fo]?%d+" --pattern used to detect karaoke tags + +-- KM template line definition +km_template_effect = "template pre-line all keeptags" +km_templayte_text = '!retime("line",$start < 900 and -$start or -900,200)!{!$start < 900 and "\\\\k" .. ($start/10) or "\\\\k90"!\\fad(!$start < 900 and $start or 300!,200)}' + +function hasleadin(line)--check if there is an existing lead in (2 consecutive bracket with karaoke tags at the start of the line) + return line.text:find("^{[^{}]-" .. ktag .. "[^{}]-}%s*{[^{}]-" .. ktag .. "[^{}]-}") +end + + +function removeleadin(line) + if not hasleadin(line) then + return line + end + + leadin = tonumber( line.text:match("^{[^{}]-\\[kK][fo]?(%d+)[^{}]-}%s*{[^{}]-" .. ktag .. "[^{}]-}") ) --read lead-in value + line.text = line.text:gsub("^({[^{}]-)\\[kK][fo]?%d+(.-}%s*{[^{}]-" .. ktag .. ".-})","%1%2") --remove lead in + + line.text = cleantags(line.text) --clean tags + + line.start_time = line.start_time + leadin*10 --adjust start time + + --aegisub.log(line.text) + + return line +end + + +function adjust_1sec(subs, sel) + + for _, i in ipairs(sel) do + local line = subs[i] + + line.text = cleantags(line.text) + + if( line.text:find(ktag)) then--don't do anything if there is no ktags in this line + + --start by removing existing lead-in + while hasleadin(line) do + if aegisub.progress.is_cancelled() then return end + line = removeleadin(line) + end + + --then add our lead in + + if line.start_time >= leadinmsec then + line.text = string.format("{\\k%d}%s",leadinmsec/10, line.text) + line.start_time = line.start_time - leadinmsec + + else --if line starts too early to put the needed lead in, make the line start at time 0 and fill with appropriate lead in + line.text = string.format("{\\k%d}%s",line.start_time/10, line.text) + line.start_time = 0 + end + + subs[i] = line + end + end + + aegisub.set_undo_point(tr"1sec adjust lead-in") +end + + +function remove_tag(line, tag) + local expr = "^(.-{[^}]*)\\" .. tag .. "[^\\}]*(.*)" + while true do + before, after = line.text:match(expr) + if before == nil then + return line + else + line.text = cleantags(before .. after) + end + end +end + + +function is_template_line(line) + return (line.class == "dialogue" + and line.effect == km_template_effect + and line.text == km_templayte_text) +end + + +function mugenizer(subs) + local first = nil + local styles_different = false + local styles = 0 + local i_styles = {} + local template_present = false + + for i, line in ipairs(subs) do + if line.class == "info" then + if line.key == "PlayResX" or line.key == "PlayResY" then + line.value = "0" + end + end + + if line.class == "style" then + line.fontname = "Arial" + line.fontsize = "24" + line.outline = "1.5" + line.margin_l = "15" + line.margin_r = "15" + line.margin_t = "20" + line.margin_b = "20" + + i_styles[styles] = i + if styles > 0 then + styles_different = styles_different or line.color1 ~= subs[i_styles[styles-1]].color1 or line.color2 ~= subs[i_styles[styles-1]].color2 or line.color3 ~= subs[i_styles[styles-1]].color3 or line.color4 ~= subs[i_styles[styles-1]].color4 + end + styles = styles + 1 + end + + if is_template_line(line) then + line.comment = true + template_present = true + end + + if line.class == "dialogue" and not line.comment and line.effect ~= "fx" then + if first == nil then + first = i + end + + line.text = cleantags(line.text) + + while hasleadin(line) do + if aegisub.progress.is_cancelled() then return end + line = removeleadin(line) + end + + line = remove_tag(line, "fad") + end + + subs[i] = line + end + + if not styles_different then + for i = 0, styles-1, 1 do + line = subs[i_styles[i]] + line.color1 = "&H008AFF" + line.color2 = "&HFFFFFF" + line.color3 = "&H000000" + line.color4 = "&H000000" + subs[i_styles[i]] = line + end + end + + if not template_present then + -- add mugen's magic line + line = subs[first] + line.comment = true + line.start_time = 0 + line.end_time = 0 + line.effect = km_template_effect + line.text = km_templayte_text + subs.insert(first, line) + end +end + +aegisub.register_macro(script_name, script_description, adjust_1sec) +aegisub.register_macro(tr"Mugenizer", tr"Mugenize your subs", mugenizer) diff --git a/automation/autoload/karaoke-split.lua b/automation/autoload/karaoke-split.lua new file mode 100644 index 000000000..a428b7198 --- /dev/null +++ b/automation/autoload/karaoke-split.lua @@ -0,0 +1,48 @@ +local tr = aegisub.gettext + +script_name = tr"Split karaoke line" +script_description = tr"Split line at {split} marker according to ktags" +script_author = "amoethyst" +script_version = "1.0" + +function split_line(subs, sel) + + function getduration(line) + d = 0 + + kduration = "{[^}]-\\[kK][fo]?(%d+)[^}]-}" + for match in line:gmatch(kduration) do + d = d + tonumber(match) + end + + return d * 10 + end + + insertions = 0 + for _, i in ipairs(sel) do + i = i + insertions + line1 = subs[i] + line2 = subs[i] + + split_expr = "(.-)%s*{split}%s*(.*)" + line1.text, line2.text = line1.text:match(split_expr) + + while line1.text ~= nil do + line1.end_time = line1.start_time + getduration(line1.text) + line2.start_time = line1.end_time + + subs[i] = line1 + i = i + 1 + insertions = insertions + 1 + subs.insert(i, line2) + line1 = subs[i] + + line1.text, line2.text = line1.text:match(split_expr) + end + + end + + aegisub.set_undo_point(tr"Karaoke split") +end + +aegisub.register_macro(script_name, script_description, split_line) diff --git a/automation/autoload/ua.BlurAndGlow.lua b/automation/autoload/ua.BlurAndGlow.lua new file mode 100644 index 000000000..06a87c086 --- /dev/null +++ b/automation/autoload/ua.BlurAndGlow.lua @@ -0,0 +1,523 @@ +--[[ "Blur / Layers" creates layers with blur. Supports 2 borders, xbord, ybord, xshad, and yshad. Basic support for transforms and \r. + "Blur + Glow" - Same as above but with an extra layer for glow. Set blur amount and alpha for the glow. + The "double border" option additionally lets you change the size and colour of the 2nd border. + If blur is missing, default blur is added. + "Bottom blur" allows you to use different blur for the lowest non-glow layer than for top layer(s). + "fix \\1a for layers with border and fade" - Uses \1a&HFF& for the duration of a fade on layers with border. + "transition" - for \fad(500,0) with transition 80ms you get \1a&HFF&\t(420,500,\1a&H00&). + "only add glow" - will add glow to a line with a border, without messing with the primary / border. (Blur + Glow) + "only add 2nd border" - will add 2nd border, without messing with the primary / first border. (Blur / Layers) + "Fix fades" - Recalculates those \1a fades mentioned above. + Use this when you shift something like an episode title to a new episode and the duration of the sign is different. + "Change layer" - raises or lowers layer for all selected lines by the same amount. [This is separate from the other functions.] + + Full manual: http://unanimated.xtreemhost.com/ts/scripts-manuals.htm#blurglow +]] + +script_name="Blur and Glow" +script_description="Add blur and/or glow to signs" +script_author="unanimated" +script_url="http://unanimated.xtreemhost.com/ts/blur-and-glow.lua" +script_version="2.5" +script_namespace="ua.BlurAndGlow" + +local haveDepCtrl,DependencyControl,depRec=pcall(require,"l0.DependencyControl") +if haveDepCtrl then + script_version="2.5.0" + depRec=DependencyControl{feed="https://raw.githubusercontent.com/TypesettingTools/unanimated-Aegisub-Scripts/master/DependencyControl.json"} +end + + +function glow(subs,sel) + if not res.rep then al=res.alfa bl=res.blur end + if res.glowcol then glowc=res.glc:gsub("#(%x%x)(%x%x)(%x%x)","&H%3%2%1&") end + if res.autod then if res.clr or res.bsize then res.double=true end end + for z=#sel,1,-1 do + i=sel[z] + progress("Glowing line: "..(#sel-z+1).."/"..#sel) + line=subs[i] + text=line.text + if defaref and line.style=="Default" then sr=defaref + elseif lastref and laststyle==line.style then sr=lastref + else sr=stylechk(line.style) end + lastref=sr laststyle=line.style + duration=line.end_time-line.start_time + + -- get colors, border, shadow from style + stylinfo(text) + text=preprocess(text) + line.text=text + + if border~="0" or text:match("\\[xy]bord") then + + -- WITH TWO BORDERS + if res.double then + + -- second border + line1=line + line1.text=text + line1.text=borderline2(line1.text) + line1.layer=line1.layer+1 + subs.insert(i+1,line1) + + -- first border + line2=line + line2.text=text + line2.text=borderline(line2.text) + if shadow~="0" then line2.text=line2.text:gsub("^({\\[^}]+)}","%1\\shad"..shadow.."}") end + if not res.s_mid then line2.text=line2.text:gsub("^({\\[^}]-)}","%1\\4a&HFF&}") end + line2.layer=line2.layer+1 + subs.insert(i+2,line2) + + -- top line + line3=line + line3.text=text + line3.text=topline(line3.text) + line3.layer=line3.layer+1 + subs.insert(i+3,line3) + + -- bottom / glow + text=borderline2(text) + text=glowlayer(text,"3c","3") + if res.botalpha and line.text:match("\\fad%(") then text=botalfa(text) end + line.layer=line.layer-3 + line.text=text + sls=3 + + else + -- WITH ONE BORDER + + -- border + line2=line + if not res.onlyg then + line2.text=text + line2.text=borderline(line2.text) + end + line2.layer=line2.layer+1 + subs.insert(i+1,line2) + + -- top line + line3=line + line3.layer=line3.layer+1 + if not res.onlyg then + line3.text=text + line3.text=topline(line3.text) + subs.insert(i+2,line3) + end + + -- bottom / glow + text=glowlayer(text,"3c","3") + if res.botalpha and line.text:match("\\fad%(") then text=botalfa(text) end + line.layer=line.layer-2 + line.text=text + sls=2 + + end + + else + -- WITHOUT BORDER + + line2=line + line2.layer=line2.layer+1 + subs.insert(i+1,line2) + text=glowlayer(text,"c","1") + line.layer=line.layer-1 + line.text=text + sls=1 + + end + subs[i]=line + for s=z,#sel do sel[s]=sel[s]+sls end + end + progress("Blur & Glow: DONE") + return sel +end + +function layerblur(subs,sel) + if res.autod then if res.clr or res.bsize then res.double=true end end + for z=#sel,1,-1 do + i=sel[z] + progress("Blurring line: "..(#sel-z+1).."/"..#sel) + line=subs[i] + text=line.text + if defaref~=nil and line.style=="Default" then sr=defaref + elseif lastref~=nil and laststyle==line.style then sr=lastref + else sr=stylechk(line.style) end + lastref=sr laststyle=line.style + duration=line.end_time-line.start_time + + -- get colors, border, shadow from style + stylinfo(text) + text=preprocess(text) + line.text=text + + -- TWO BORDERS + if res.double then + + -- first border + line2=line + if not res.onlyb then + line2.text=text + line2.text=borderline(line2.text) + if not res.s_mid then line2.text=line2.text:gsub("^({\\[^}]-)}","%1\\4a&HFF&}") end + end + line2.layer=line2.layer+1 + subs.insert(i+1,line2) + + -- top line + line3=line + line3.layer=line3.layer+1 + if not res.onlyb then + line3.text=text + line3.text=topline(line3.text) + subs.insert(i+2,line3) + end + + -- second border + text=borderline2(text) + line.layer=line.layer-2 + line.text=text + sls=2 + + -- ONE BORDER + else + + -- top line + line3=line + line3.text=text + line3.text=topline(line3.text) + line3.layer=line3.layer+1 + subs.insert(i+1,line3) + + -- bottom line + text=borderline(text) + line.layer=line.layer-1 + line.text=text + sls=1 + end + + subs[i]=line + for s=z,#sel do sel[s]=sel[s]+sls end + end + progress("Blur: DONE") +end + +function topline(txt) + txt=txt + :gsub("(\\t%([^%)]*)\\bord[%d%.]+","%1") + :gsub("(\\t%([^%)]*)\\shad[%d%.]+","%1") + :gsub("\\t%([^\\]*%)","") + if not txt:match("^{[^}]-\\bord") then txt=txt:gsub("^{\\","{\\bord0\\") end + txt=txt + :gsub("\\bord[%d%.]+","\\bord0") + :gsub("(\\r[^}]-)}","%1\\bord0}") + txt=txt:gsub("(\\[xy]bord)[%d%.]+","") :gsub("\\3c&H%x+&","") + if shadow~="0" then txt=txt:gsub("^({\\[^}]+)}","%1\\shad"..shadow.."}") end + txt=txt + :gsub("^({\\[^}]-)}","%1\\4a&HFF&}") + :gsub("(\\r[^}]-)}","%1\\shad"..shadow.."\\4a&HFF&}") + :gsub("\\bord[%d%.%-]+([^}]-)(\\bord[%d%.%-]+)","%1%2") + :gsub("\\shad[%d%.%-]+([^}]-)(\\shad[%d%.%-]+)","%1%2") + if res.s_top then txt=txt:gsub("\\4a&HFF&","") end + txt=txt:gsub("{}","") + return txt +end + +function borderline(txt) + txt=txt:gsub("\\c&H%x+&","") + -- transform check + if txt:match("^{[^}]-\\t%([^%)]-\\3c") then + pretrans=text:match("^{(\\[^}]-)\\t") + if not pretrans:match("^{[^}]-\\3c") then txt=txt:gsub("^{\\","{\\c"..soutline.."\\") end + end + if not txt:match("^{[^}]-\\3c&[^}]-}") then + txt=txt:gsub("^({\\[^}]+)}","%1\\c"..soutline.."}") + :gsub("(\\r[^}]-)}","%1\\c"..routline.."}") + end + txt=txt:gsub("(\\3c)(&H%x+&)","%1%2\\c%2") + :gsub("(\\r[^}]-)}","%1\\c"..routline.."}") + :gsub("(\\r[^}]-\\3c)(&H%x+&)([^}]-)}","%1%2\\c%2%3") + :gsub("\\c&H%x+&([^}]-)(\\c&H%x+&)",function(a,b) if not a:match("\\t") then return a..b end end) + :gsub("{%*?}","") + if res.bbl and not res.double then txt=txt:gsub("\\blur[%d%.]+","\\blur"..res.bblur) end + if res.botalpha and txt:match("\\fad%(") then txt=botalfa(txt) end + return txt +end + +function borderline2(txt) + outlinetwo=primary + if res.clr then col3=res.c3:gsub("#(%x%x)(%x%x)(%x%x)","&H%3%2%1&") outlinetwo=col3 rimary=col3 end + bordertwo=border + if res.bsize then bordertwo=res.secbord end + -- transform check + if txt:match("^{[^}]-\\t%([^%)]-\\bord") then + pretrans=text:match("^{(\\[^}]-)\\t") + if not pretrans:match("^{[^}]-\\bord") then txt=txt:gsub("^{\\","{\\bord"..border.."\\") end + end + if not txt:match("^{[^}]-\\bord") then txt=txt:gsub("^{\\","{\\bord"..border.."\\") end + txt=txt:gsub("(\\r[^\\}]-)([\\}])","%1\\bord"..rbord.."%2") + :gsub("(\\r[^\\}]-)\\bord[%d%.%-]+([^}]-)(\\bord[%d%.%-]+)","%1%2%3") + :gsub("(\\bord)([%d%.]+)",function(a,b) if res.bsize then brd=bordertwo else brd=b end return a..b+brd end) + :gsub("(\\[xy]bord)([%d%.]+)",function(a,b) return a..b+b end) + :gsub("\\3c&H%x+&","") + :gsub("^({\\[^}]+)}","%1\\3c"..outlinetwo.."}") + :gsub("(\\3c)(&H%x+&)","%1"..outlinetwo) + if res.clr then txt=txt:gsub("\\c&H%x+&([^}]-)}","\\c"..rimary.."\\3c"..outlinetwo.."%1}") + else txt=txt:gsub("(\\c)(&H%x+&)([^}]-)}","%1%2%3\\3c%2}") end + txt=txt:gsub("(\\r[^}]+)}","%1\\3c"..rimary.."}") + :gsub("\\c&H%x+&([^}]-)(\\c&H%x+&)",function(a,b) if not a:match("\\t") then return a..b end end) + :gsub("\\3c&H%x+&([^}]-)(\\3c&H%x+&)",function(a,b) if not a:match("\\t") then return a..b end end) + :gsub("{%*?}","") + if res.bbl and res.double then txt=txt:gsub("\\blur[%d%.]+","\\blur"..res.bblur) end + if res.botalpha and txt:match("\\fad%(") then txt=botalfa(txt) end + return txt +end + +function glowlayer(txt,kol,alf) + txt=txt:gsub("\\alpha&H(%x%x)&",function(a) if a>al then return "\\alpha&H"..a.."&" else return "\\alpha&H"..al.."&" end end) + :gsub("\\"..alf.."a&H(%x%x)&",function(a) if a>al then return "\\"..alf.."a&H"..a.."&" else return "\\"..alf.."a&H"..al.."&" end end) + :gsub("(\\blur)[%d%.]*([\\}])","%1"..bl.."%2") + :gsub("(\\r[^}]-)}","%1\\alpha&H"..al.."&}") + if not txt:match("^{[^}]-\\alpha") then txt=txt:gsub("^({\\[^}]-)}","%1\\alpha&H"..al.."&}") end + if res.alfa=="00" then txt=txt:gsub("^({\\[^}]-)\\alpha&H00&","%1") end + txt=txt:gsub("{%*?}","") + if res.glowcol then + if txt:match("^{\\[^}]-\\"..kol.."&") then txt=txt:gsub("\\"..kol.."&H%x+&","\\"..kol..glowc) + else txt=txt:gsub("\\"..kol.."&H%x+&","\\"..kol..glowc) txt=txt:gsub("^({\\[^}]-)}","%1\\"..kol..glowc.."}") + end + end + return txt +end + +function botalfa(txt) + fadin,fadout=txt:match("\\fad%((%d+)%,(%d+)") + alfadin=res.alphade alfadout=res.alphade + if res.alphade=="max" then alfadin=fadin alfadout=fadout end + if fadin==nil or fadout==nil then aegisub.log("\n ERROR: Failed to capture fade times from line:\n "..text) end + if fadin~="0" then + txt=txt:gsub("^({\\[^}]-)}","%1\\1a&HFF&\\t("..fadin-alfadin..","..fadin..",\\1a&H00&)}") + end + if fadout~="0" then + txt=txt:gsub("^({\\[^}]-)}","%1\\t("..duration-fadout..","..duration-fadout+alfadout..",\\1a&HFF&)}") + end + return txt +end + +function stylinfo(text) + startags=text:match("^{\\[^}]-}") or "" + startags=startags:gsub("\\t%b()","") + + primary=startags:match("^{[^}]-\\c(&H%x+&)") or sr.color1:gsub("H%x%x","H") + soutline=sr.color3:gsub("H%x%x","H") + outline=startags:match("^{[^}]-\\3c(&H%x+&)") or soutline + border=startags:match("^{[^}]-\\bord([%d%.]+)") or tostring(sr.outline) + shadow=startags:match("^{[^}]-\\shad([%d%.]+)") or tostring(sr.shadow) + + if text:match("\\r%a") then + rstyle=text:match("\\r([^\\}]+)") + reref=stylechk(rstyle) + rimary=reref.color1:gsub("H%x%x","H") + routline=reref.color3:gsub("H%x%x","H") + rbord=tostring(reref.outline) + else routline=soutline rimary=primary rbord=border + end +end + +function preprocess(text) + if not text:match("^{\\") then text="{\\blur"..bdef.."}"..text -- default blur if no tags + text=text:gsub("(\\r[^}]-)}","%1\\blur"..bdef.."}") + end + if not text:match("\\blur") then text=text:gsub("^{\\","{\\blur"..bdef.."\\") -- default blur if missing in tags + text=text:gsub("(\\r[^}]-)}","%1\\blur"..bdef.."}") + end + if text:match("\\blur") and not text:match("^{[^}]*blur[^}]*}") then -- add blur if missing in first tag block + text=text:gsub("^{\\","{\\blur"..bdef.."\\") + end + if text:match("^{[^}]-\\t[^}]-}") and not text:match("^{[^}]-\\3c[^}]-\\t") then -- \t workaround + text=text:gsub("^{\\","{\\3c"..soutline.."\\") + end + text=text:gsub("\\1c","\\c") + return text +end + +function fixfade(subs,sel) + for z=#sel,1,-1 do + i=sel[z] + line=subs[i] + text=line.text + sr=stylechk(line.style) + duration=line.end_time-line.start_time + border=tostring(sr.outline) + bord=text:match("^{[^}]-\\bord([%d%.]+)") + if bord then border=bord end + + if border~="0" and line.text:match("\\fad%(") then + text=text:gsub("\\1a&H%x+&","") :gsub("\\t%([^\\%(%)]-%)","") + text=botalfa(text) + end + line.text=text + subs[i]=line + end +end + +function layeraise(subs,sel) + for z=#sel,1,-1 do + i=sel[z] + line=subs[i] + if line.layer+res["layer"]>=0 then line.layer=line.layer+res["layer"] else t_error("You're dumb. Layers can't go below 0.",1) end + subs[i]=line + end +end + +function styleget(subs) + styles={} + for i=1,#subs do + if subs[i].class=="style" then + table.insert(styles,subs[i]) + end + if subs[i].class=="dialogue" then break end + end +end + +function stylechk(sn) + for s=1,#styles do + if sn==styles[s].name then + sr=styles[s] + if styles[s].name=="Default" then defaref=styles[s] end + end + end + if sr==nil then t_error("Style '"..sn.."' doesn't exist.",1) end + return sr +end + +function saveconfig() +bgconf="Blur & Glow config\n\n" + for key,val in ipairs(GUI) do + if val.class=="floatedit" or val.class=="dropdown" or val.class=="color" then + bgconf=bgconf..val.name..":"..res[val.name].."\n" + end + if val.class=="checkbox" and val.name~="save" then + bgconf=bgconf..val.name..":"..tf(res[val.name]).."\n" + end + end +blurkonfig=ADP("?user").."\\blurandglow.conf" +file=io.open(blurkonfig,"w") +file:write(bgconf) +file:close() +ADD({{class="label",label="Config saved to:\n"..blurkonfig}},{"OK"},{close='OK'}) +end + +function loadconfig() +blurkonfig=ADP("?user").."\\blurandglow.conf" +file=io.open(blurkonfig) + if file~=nil then + konf=file:read("*all") + io.close(file) + for key,val in ipairs(GUI) do + if val.class=="floatedit" or val.class=="checkbox" or val.class=="dropdown" or val.class=="color" then + if konf:match(val.name) then val.value=detf(konf:match(val.name..":(.-)\n")) end + end + end + end +end + +function tf(val) + if val==true then ret="true" + elseif val==false then ret="false" + else ret=val end + return ret +end + +function detf(txt) + if txt=="true" then ret=true + elseif txt=="false" then ret=false + else ret=txt end + return ret +end + +function logg(m) m=tf(m) or "nil" aegisub.log("\n "..m) end + +function progress(msg) + if aegisub.progress.is_cancelled() then ak() end + aegisub.progress.title(msg) +end + +function t_error(message,cancel) + ADD({{class="label",label=message}},{"OK"},{close='OK'}) + if cancel then ak() end +end + +function blurandglow(subs,sel) +ADD=aegisub.dialog.display +ADP=aegisub.decode_path +ak=aegisub.cancel +GUI={ + --left + {x=0,y=0,width=2,class="label",label=" = Blur and Glow version "..script_version.." ="}, + {x=0,y=1,class="label",label="Glow blur:"}, + {x=0,y=2,class="label",label="Glow alpha:"}, + + {x=1,y=1,width=2,class="floatedit",name="blur",value=3}, + {x=1,y=2,width=2,class="dropdown",name="alfa",items={"00","20","30","40","50","60","70","80","90","A0","B0","C0","D0","F0"},value="80"}, + + {x=0,y=3,class="checkbox",name="glowcol",label="glow c.:",hint="glow colour"}, + {x=1,y=3,width=2,class="color",name="glc"}, + + {x=0,y=4,width=2,class="checkbox",name="s_top",label="keep shadow on top layer"}, + + {x=0,y=5,width=5,class="checkbox",name="botalpha",label="fix \\1a for layers with border and fade --> transition:",value=true, + hint="uses \\1a&HFF& for bottom layer during fade"}, + {x=5,y=5,class="dropdown",name="alphade",items={0,45,80,120,160,200,"max"},value=45}, + {x=6,y=5,width=2,class="label",label="ms"}, + + {x=0,y=6,width=4,class="checkbox",name="onlyg",label="only add glow (layers w/ border)"}, + + -- right + {x=4,y=0,class="checkbox",name="double",label="double border"}, + {x=5,y=0,width=2,class="checkbox",name="onlyb",label="only add 2nd border"}, + + {x=4,y=1,class="checkbox",name="bbl",label="bottom blur:", + hint="Blur for bottom layer \n[not the glow layer] \nif different from top layer."}, + {x=5,y=1,width=2,class="floatedit",name="bblur",value=1}, + + {x=4,y=2,class="checkbox",name="bsize",label="2nd b. size:", + hint="Size for 2nd border \n[counts from first border out] \nif different from the current border."}, + {x=5,y=2,width=2,class="floatedit",name="secbord",value=2}, + + {x=4,y=3,class="checkbox",name="clr",label="2nd b. colour:",hint="Colour for 2nd border \nif different from primary."}, + {x=5,y=3,width=2,class="color",name="c3"}, + + {x=4,y=4,width=4,class="checkbox",name="s_mid",label="keep shadow on middle layer"}, + + {x=4,y=6,class="label",label=" Change layer:"}, + {x=5,y=6,class="dropdown",name="layer",items={"-5","-4","-3","-2","-1","+1","+2","+3","+4","+5"},value="+1"}, + + + {x=0,y=7,width=2,class="checkbox",name="rep",label="repeat with last settings"}, + {x=4,y=7,class="checkbox",name="autod",label="auto double",value=true, + hint="automatically use double border\nif 2nd colour or 2nd border size is checked"}, + {x=6,y=6,class="dropdown",name="def",items={"0.3","0.4","0.5","0.6","0.7","0.8","0.9","1"},value="0.6",hint="config: default blur"}, + {x=5,y=7,width=2,class="checkbox",name="save",label="save configuration"}, +} + loadconfig() + buttons={"Blur / Layers","Blur + Glow","Fix fades","Change layer","cancel"} + pressed,res=ADD(GUI,buttons,{ok='Blur / Layers',close='cancel'}) + if pressed=="cancel" then ak() end + bdef=res.def + if res.onlyg then res.double=false end + if res.onlyb then res.double=true end + if res.save then saveconfig() + else + if res.rep then res=lastres end + styleget(subs) + if pressed=="Blur / Layers" then layerblur(subs,sel) end + if pressed=="Blur + Glow" then sel=glow(subs,sel) end + if pressed=="Fix fades" then fixfade(subs,sel) end + if pressed=="Change layer" then layeraise(subs,sel) end + end + if res.rep==false then lastres=res end + aegisub.set_undo_point(script_name) + return sel +end + +if haveDepCtrl then depRec:registerMacro(blurandglow) else aegisub.register_macro(script_name,script_description,blurandglow) end \ No newline at end of file diff --git a/automation/autoload/ua.ChangeCase.lua b/automation/autoload/ua.ChangeCase.lua new file mode 100644 index 000000000..774c81a97 --- /dev/null +++ b/automation/autoload/ua.ChangeCase.lua @@ -0,0 +1,148 @@ +script_name="Change Case" +script_description="Capitalises text or makes it lowercase / uppercase" +script_author="unanimated" +script_version="3.0" +script_namespace="ua.ChangeCase" + +local haveDepCtrl,DependencyControl,depRec=pcall(require,"l0.DependencyControl") +if haveDepCtrl then + script_version="3.0.0" + depRec=DependencyControl{feed="https://raw.githubusercontent.com/TypesettingTools/unanimated-Aegisub-Scripts/master/DependencyControl.json"} +end + +re=require'aegisub.re' +unicode=require'aegisub.unicode' + +function case(subs,sel) + for z,i in ipairs(sel) do + line=subs[i] + t=line.text + if P=="lowercase" then t=lowercase(t) end + if P=="UPPERCASE" then t=uppercase(t) end + if P=="Lines" then t=capitalines(t) end + if P=="Sentences" then + if res.mod then res.mod=false t=lowercase(t) res.mod=true end + t=sentences(t) + end + if P=="Words" then + if not res.mod then t=lowercase(t) end + t=capitalise(t) + end + line.text=t + subs[i]=line + end +end + +function lowercase(t) + t=t + :gsub("\\[Nnh]","{%1}") + :gsub("^([^{]*)",function(l) + if res.mod then l=re.sub(l,[[\b(\u\u+'?\u*)]],function(u) return ulower(u) end) return l + else return ulower(l) end end) + :gsub("}([^{]*)",function(l) + if res.mod then l=re.sub(l,[[\b(\u\u+'?\u*)]],function(u) return ulower(u) end) return "}"..l + else return "}"..ulower(l) end end) + :gsub("{(\\[Nnh])}","%1") + return t +end + +function uppercase(t) + t=t + :gsub("\\[Nnh]","{%1}") + :gsub("^([^{]*)",function(u) return uupper(u) end) + :gsub("}([^{]*)",function(u) return "}"..uupper(u) end) + :gsub("{(\\[Nnh])}","%1") + return t +end + +function capitalines(t) + t=re.sub(t,[[^(["']?\l)]],function(l) return uupper(l) end) + t=re.sub(t,[[^\{[^}]*\}(["']?\l)]],function(l) return uupper(l) end) + if not res.mod then + t=t:gsub(" i([' %?!%.,])"," I%1"):gsub("\\Ni([' ])","\\NI%1") + end + return t +end + +function sentences(t) +somewords={"English","Japanese","American","British","German","French","Spanish","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday","January","February","April","June","July","August","September","October","November","December"} +hnrfx={"%-san","%-kun","%-chan","%-sama","%-dono","%-se[nm]pai","%-on%a+an"} + t=re.sub(t,[[^(["']?\l)]],function(l) return uupper(l) end) + t=re.sub(t,[[^\{[^}]*\}(["']?\l)]],function(l) return uupper(l) end) + t=re.sub(t,[[[\.\?!](\s|\s\\N|\\N)["']?(\l)]],function(l) return uupper(l) end) + t=t + :gsub(" i([' %?!%.,])"," I%1") + :gsub("\\Ni([' ])","\\NI%1") + :gsub(" m(arch %d)"," M%1") + :gsub(" a(pril %d)"," A%1") + for l=1,#somewords do t=t:gsub(somewords[l]:lower(),somewords[l]) end + for h=1,#hnrfx do + t=t:gsub("([ %p]%l)(%l*"..hnrfx[h]..")",function(h,f) return h:upper()..f end) + t=t:gsub("(\\N%l)(%l*"..hnrfx[h]..")",function(h,f) return h:upper()..f end) + end + t=re.sub(t,"\\b(of|in|from|\\d+st|\\d+nd|\\d+rd|\\d+th) m(arch|ay)\\b","\\1 M\\2") + t=re.sub(t,"\\bm(r|rs|s)\\.","M\\1.") + t=re.sub(t,"\\bdr\\.","Dr.") + return t +end + +function capitalise(txt) +word={"A","About","Above","Across","After","Against","Along","Among","Amongst","An","And","Around","As","At","Before","Behind","Below","Beneath","Beside","Between","Beyond","But","By","Despite","During","Except","For","From","In","Inside","Into","Near","Nor","Of","On","Onto","Or","Over","Per","Sans","Since","Than","The","Through","Throughout","Till","To","Toward","Towards","Under","Underneath","Unlike","Until","Unto","Upon","Versus","Via","With","Within","Without","According to","Ahead of","Apart from","Aside from","Because of","Inside of","Instead of","Next to","Owing to","Prior to","Rather than","Regardless of","Such as","Thanks to","Up to","and Yet"} +onore={"%-San","%-Kun","%-Chan","%-Sama","%-Dono","%-Se[nm]pai","%-On%a+an"} +nokom={"^( ?)([^{]*)","(})([^{]*)"} + for n=1,2 do + txt=txt:gsub(nokom[n],function(no_t,t) + t=t:gsub("\\[Nnh]","{%1}") + t=re.sub(t,[[\b\l]],function(l) return uupper(l) end) + t=re.sub(t,[[[I\l]'(\u)]],function(l) return ulower(l) end) + + for r=1,#word do w=word[r] + t=t + :gsub("^ "..w.." "," "..w:lower().." ") + :gsub("([^%.:%?!]) "..w.." ","%1 "..w:lower().." ") + :gsub("([^%.:%?!]) (%b{})"..w.." ","%1 %2"..w:lower().." ") + :gsub("([^%.:%?!]) (%*Large_break%* ?)"..w.." ","%1 %2"..w:lower().." ") + end + + -- Roman numbers (this may mismatch some legit words - sometimes there just are 2 options and it's a guess) + t=t + :gsub("$","#") + :gsub("(%s?)([IVXLCDM])([ivxlcdm]+)([%s%p#])",function(s,r,m,e) return s..r..m:upper()..e end) + :gsub("([DLM])ID","%1id") + :gsub("DIM","Dim") + :gsub("MIX","Mix") + :gsub("Ok([%s%p#])","OK%1") + for h=1,#onore do + t=t:gsub(onore[h].."([%s%p#])",onore[h]:lower().."%1") + end + t=t + :gsub("#$","") + :gsub("{(\\[Nnh])}","%1") + return no_t..t end) + end + return txt +end + +ulower=unicode.to_lower_case +uupper=unicode.to_upper_case + +function logg(m) m=m or "nil" aegisub.log("\n "..m) end + +function capital(subs,sel) + GUI={ + {x=1,y=0,class="label",label="Words - Capitalise Words Like in Titles"}, + {x=1,y=1,class="label",label=" Lines - Capitalise first word in selected lines"}, + {x=1,y=2,class="label",label=" Sentences - Capitalise first word in each sentence"}, + {x=1,y=3,class="label",label=" Lowercase - make text in selected lines lowercase"}, + {x=1,y=4,class="label",label=" Uppercase - MAKE TEXT IN SELECTED LINES UPPERCASE"}, + {x=2,y=5,class="label",label=script_name.." v "..script_version}, + {x=1,y=5,class="checkbox",name="mod",label="mod",hint="Words - leave uppercase words\nLines - don't capitalize 'i'\nSentences - run lowercase first\nlowercase - only for uppercase words"}, + } + P,res=aegisub.dialog.display(GUI,{"Words","Lines","Sentences","lowercase","UPPERCASE","Cancel"},{ok='Words',close='Cancel'}) + if P=="Cancel" then aegisub.cancel() end + case(subs,sel) + aegisub.set_undo_point(script_name) + return sel +end + +if haveDepCtrl then depRec:registerMacro(capital) else aegisub.register_macro(script_name,script_description,capital) end \ No newline at end of file diff --git a/automation/autoload/ua.Colorize.lua b/automation/autoload/ua.Colorize.lua new file mode 100644 index 000000000..b5b603631 --- /dev/null +++ b/automation/autoload/ua.Colorize.lua @@ -0,0 +1,1422 @@ +script_name="Colourise" +script_description="RGB Magic and HSL Sorcery" +script_author="unanimated" +script_version="5.0" +script_namespace="ua.Colourise" + +local haveDepCtrl,DependencyControl,depRec=pcall(require,"l0.DependencyControl") +if haveDepCtrl then + script_version="5.0.0" + depRec=DependencyControl{feed="https://raw.githubusercontent.com/unanimated/luaegisub/master/DependencyControl.json"} +end + +info={} +info.colourise=[[ +"Colourise letter by letter" +Alternates between 2-6 colours character by character, like 121212, or 123412341234. +Works for all colour types (one at a time), based on the 'Apply to' menu. +Handles inline tags and comments. + +"Colourise by word" +Colourises by word instead of by letter. + +"Bounce Back" will create a sequence like 123432123432 instead of 12341234.]] +info.shift=[[ +"Shift" +Shift can be used on an already colourised line to shift the colours by one letter. +You have to set the right number of colours for it to work correctly. +You'll be shitfing the colour type in the 'Apply to' menu. +If "shift base" is "line", then it takes the colour for the first character from the last one. +This way it can shift colours no matter how few/many tags there are and where. + +"Shift line by line" +If you select a bunch of the same colourised lines, this shifts the colours line by line. +First line is shifted by one letter, second one by two, etc.]] +info.tunecolours=[[ +"Tune colours" +Loads all colours from a line into a GUI and lets you change them from there. +Useful for changing colours in transforms or just tuning lines with multiple colours. + +"All selected" loads all 'unique' colours from all selected lines, rather than all from each line. +This is much more useful for tuning/replacing colours in a larger selection. + +You can select "all/nontf/transf" to affect colours only from transforms, only those not from transforms, or all.]] +info.setcolours=[[ +"Set colours across whole line" +This is like a preparation for a gradient by character. +Select number of colours, and choose the colours to be used. +For 3 colours, it will place one tag at the start, one in the middle, and one before the last character. +For 2 colours, it'll be the first and last characters. +Works for 2-10 colours and sets them evenly across the line.]] +info.gradient=[[ +"Gradient" +Creates a gradient by character. (Uses Colourise button.) +There are two modes: RGB and HSL. RGB is the standard, like lyger's GBC; +HSL interpolates Hue, Saturation, and Lightness separately. +Use the \c, \3c, \4c, \2c checkboxes on the right to choose which colours to gradient. + +"Short hue" makes sure that hue is interpolated in the shorter direction. +Unchecking it will give you a different gradient in 50% cases. + +"Double HSL gradient" will make an extra round through Hue. Note that neither of these two options applies to RGB. + +"Use asterisks" places asterisks like lyger's GBC so that you can ungradient the line with his script. + +"Restart after each \N" will create the full gradient for each line if there are linebreaks. + +The edit box below that is for acceleration. You can still type accel in Effect, the old way, +in the form: accel1.5, and this will override the GUI setting, ensuring you can have +different accel for different lines. + +There are several differences from lyger's GBC: + - RGB / HSL option + - You can choose which types of colour you want to gradient + - Other tags don't interfere with the colour gradients + - You can use acceleration + +The hotkeyable macros run with \c and \3c checked, and 'short hue' for HSL.]] +info.reverse=[[ +"Reverse gradient" +On the right, select types of colours to apply this to. +For each colour type, colours from all tags in the line outside transforms are collected +and returned in the opposite direction. +A full gradient gets thus reversed. +(This is separate from the Gradient function, so no need to check that.)]] +info.match=[[ +"Match Colours" +This should apply to all colour tags in the line. + +c -> 3c: primary colour is copied to outline +3c -> c: outline colour is copied to primary +c -> 4c: primary colour is copied to shadow +3c -> 4c: outline colour is copied to shadow +c <-> 3c: primary and outline are switched +Invert: all colours are inverted (red->green, yellow->blue, black->white)]] +info.RGBHSL=[[ +"Adjust RGB / HSL" +Adjusts Red/Green/Blue or Hue/Saturation/Lightness. +This works for lines with multiple same-type colour tags, including gradient by character. +You can select from -255 to 255. +Check types of colours you want it to apply to. +"Apply to missing" means it will be applied to the colour set in style if there's no tag in the line. +"Randomise" - if you set Lightness (or any RGB/HSL) to 20, the resulting colour will have anywhere between -20 and +20 of the original Lightness.]] +info.general=[[ +"Remember last" - Remembers last settings of checkboxes and dropdown menus. + +"Repeat last" - Repeat the function with last settings. + +"Save config" - Saves a config file in your Application Data folder with current settings. + +"Colourise" functions: if more selected, the one lowest in the GUI is run. + +Full manual: http://unanimated.hostfree.pw/ts/scripts-manuals.htm#colourise +]] + +re=require'aegisub.re' + +function cuts(subs) +STAG="^{>?\\[^}]-}" +ATAG="{[*>]?\\[^}]-}" +COMM="{[^\\}]-}" +ACLR="&H%x+&" +ADD=aegisub.dialog.display +ADP=aegisub.decode_path +ak=aegisub.cancel +for i=1,#subs do if subs[i].class=="dialogue" then line0=i-1 break end end +end + +function colourise(subs,sel) + cuts(subs) + validateCol(subs,sel) + GUI={ + {x=0,y=0,class="label",label="Colours"}, + {x=1,y=0,width=3,class="dropdown",name="clrs",items={"2","3","4","5","6","7"},value="2",hint="number of colours for\n'colourise letter by letter'"}, + + {x=0,y=1,class="label",label="Apply to: "}, + {x=1,y=1,width=3,class="dropdown",name="kol",items={"\\c","\\3c","\\4c","\\2c"},value="\\c",hint="relevant for colourisng by letter, shifting, set across"}, + + {x=0,y=2,class="label",label="Shift base:"}, + {x=1,y=2,width=3,class="dropdown",name="shit",items={"# of colours","line","1st2start","last2start","all2start"},value="# of colours",hint="shift by the number of colours the line had been colourised with,\nor shift the whole line (last colour becomes first)"}, + + {x=4,y=0,class="label",label="壹"}, + {x=4,y=1,class="label",label="貳"}, + {x=4,y=2,class="label",label="參"}, + {x=4,y=3,class="label",label="肆"}, + {x=4,y=4,class="label",label="伍"}, + {x=4,y=5,class="label",label="陸"}, + {x=4,y=6,class="label",label="漆"}, + + {x=5,y=0,class="color",name="c1"}, + {x=5,y=1,class="color",name="c2"}, + {x=5,y=2,class="color",name="c3"}, + {x=5,y=3,class="color",name="c4"}, + {x=5,y=4,class="color",name="c5"}, + {x=5,y=5,class="color",name="c6"}, + {x=5,y=6,class="color",name="c7"}, + + {x=0,y=3,width=3,class="checkbox",name="word",label="Colourise by word"}, + {x=0,y=4,width=4,class="checkbox",name="bounce",label="Bounce back (123454321)",hint="colourise: sequence 1234321 instead of 12341234"}, + {x=0,y=5,width=4,class="checkbox",name="cont",label="Shift line by line",hint="shift more with each line"}, + {x=0,y=6,width=4,class="checkbox",name="across",label="Set colours across line"}, + + {x=6,y=0,class="label",label=" "}, + + {x=7,y=2,class="label",label="Red "}, + {x=8,y=2,width=3,class="intedit",name="R",value=0,min=-255,max=255}, + {x=7,y=3,class="label",label="Green "}, + {x=8,y=3,width=3,class="intedit",name="G",value=0,min=-255,max=255}, + {x=7,y=4,class="label",label="Blue "}, + {x=8,y=4,width=3,class="intedit",name="B",value=0,min=-255,max=255}, + + {x=7,y=5,class="label",label="Hue"}, + {x=8,y=5,width=3,class="intedit",name="huehue",value=0,min=-255,max=255}, + {x=7,y=6,class="label",label="Saturation"}, + {x=8,y=6,width=3,class="intedit",name="satur",value=0,min=-255,max=255}, + {x=7,y=7,class="label",label="Lightness"}, + {x=8,y=7,width=3,class="intedit",name="light",value=0,min=-255,max=255}, + + {x=7,y=8,class="checkbox",name="k1",label="\\c",value=true}, + {x=8,y=8,class="checkbox",name="k3",label="\\3c"}, + {x=9,y=8,class="checkbox",name="k4",label="\\4c"}, + {x=10,y=8,class="checkbox",name="k2",label="\\2c"}, + {x=7,y=9,width=2,class="checkbox",name="mktag",label="Apply to missing",hint="Apply even to colours without tags in line"}, + {x=9,y=9,width=2,class="checkbox",name="randoom",label="Randomise",hint="randomise RGB/HSL within the\nspecified range in each direction"}, + + -- Match + {x=7,y=0,height=2,class="label",label="Match\ncolours:"}, + {x=8,y=0,class="checkbox",name="match13",label="c->3c",hint="copy primary to outline"}, + {x=9,y=0,class="checkbox",name="match31",label="3c->c",hint="copy outline to primary"}, + {x=8,y=1,class="checkbox",name="match14",label="c->4c",hint="copy primary to shadow"}, + {x=9,y=1,class="checkbox",name="match34",label="3c->4c",hint="copy outline to shadow"}, + {x=10,y=0,class="checkbox",name="match131",label="c<->3c",hint="switch primary and outline"}, + {x=10,y=1,class="checkbox",name="invert",label="Invert",hint="invert colours (applies to the types chacked below)"}, + + -- Gradient + {x=0,y=7,width=2,class="checkbox",name="grad",label="Gradient "}, + {x=2,y=7,width=3,class="checkbox",name="hueshort",label="Shorter hue",value=true}, + {x=5,y=7,class="dropdown",name="grtype",items={"RGB","HSL"},value="HSL"}, + {x=0,y=8,width=4,class="checkbox",name="gradn",label="Restart after each \\N",hint="Restart gradient after each linebreak"}, + {x=0,y=9,width=3,class="checkbox",name="double",label="Double HSL gradient"}, + {x=0,y=10,width=3,class="checkbox",name="ast",label="Use asterisks"}, + {x=0,y=11,width=3,class="floatedit",name="acc",value=1,min=0,hint="Acceleration for gradients"}, + + -- Tune/Reverse + {x=3,y=9,width=3,class="checkbox",name="tuneall",label="Tune all selected",hint="load from / apply to all selected lines\nrather than one by one"}, + {x=5,y=8,class="dropdown",name="tfmode",items={"all tags","regular","transf"},value="all tags", + hint="all tags / regular tags / tags in transforms\napplies to 'Tune colours' and RGB/HSL"}, + {x=3,y=10,width=3,class="checkbox",name="reverse",label="Reverse colours", + hint="Reverse the direction of non-transform colours across the line \nfor the types checked on the right"}, + + {x=7,y=10,width=2,class="checkbox",name="rem",label="Remember last",hint="Remember last settings"}, + {x=9,y=10,width=2,class="checkbox",name="rept",label="Repeat last",hint="Repeat with last settings"}, + {x=7,y=11,width=2,class="checkbox",name="save",label="Save config",hint="Saves current configuration\n(for most things)"}, + {x=9,y=11,width=2,class="label",label="Colourise version "..script_version..""}, + + {x=3,y=11,width=3,class="dropdown",name="help",value=": Help Menu :",hint="Choose a topic. Use any button that's not 'Cancel'.", + items={": Help Menu :","colourise","shift","setcolours","gradient","tunecolours","reverse","match","RGBHSL","general"}} + } + loadconfig() + if colourblind and res.rem then + for key,val in ipairs(GUI) do + if val.class=="checkbox" or val.class=="dropdown" or val.class=="color" or val.class:match"edit" then val.value=res[val.name] end + if val.name=="save" then val.value=false end + if val.name=="help" then val.value=": Help Menu :" end + end + end + HELP=false + NOHELP=true + repeat + if HELP then + if NOHELP then table.insert(GUI,{x=0,y=12,width=11,height=7,class="textbox",name="helpbox"}) end + for key,val in ipairs(GUI) do + if val.class=="checkbox" or val.class=="dropdown" or val.class=="color" then val.value=res[val.name] end + if val.name=="save" then val.value=false end + if val.name=="rept" then val.value=false end + if val.name=="helpbox" then val.value=info[res.help] end + if val.name=="help" then val.value=": Help Menu :" end + end + NOHELP=false + end + P,res=ADD(GUI,{"Colourise","Shift","Tune/Rvrs","Match Colours","RGB/HSL","Black Out"},{ok='Colourise',close='Black Out'}) + if res.help~=": Help Menu :" then HELP=true else HELP=false end + until P=="Black Out" or res.help==": Help Menu :" + if P=="Black Out" then ak() end + if res.save then saveconfig() ak() end + + randomise=res.randoom + if res.tfmode=="all tags" or res.tfmode=="regular" then modereg=true else modereg=false end + if res.tfmode=="all tags" or res.tfmode=="transf" then modetf=true else modetf=false end + if P=="Colourise" then repetition() + if res.grad then gradient(subs,sel) + elseif res.across then gcolours(subs,sel) + else colors(subs,sel) end + end + if P=="Tune/Rvrs" then repetition() + if res.reverse then rvrsgrad(subs,sel) else ctune(subs,sel) end + end + if P=="Shift" then repetition() if res.shit:match'start' then shift2(subs,sel) else shift(subs,sel) end end + if P=="Match Colours" or P=="RGB/HSL" then repetition() styleget(subs) match_col(subs,sel) end + + lastres=res + colourblind=true + return sel +end + + +-- Colour Ice -- +function colors(subs,sel) + local c={} + for k=1,7 do + c[k]=res["c"..k]:gsub("#(%x%x)(%x%x)(%x%x)","&H%3%2%1&") + end + for z,i in ipairs(sel) do + progress("Colourising line #"..i-line0.." ["..z.."/"..#sel.."]") + line=subs[i] + text=line.text + tags=text:match(STAG) or "" + if not tags:match("\\p1") then + text=text:gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end) + visible=nobrea(text) + + local kl=res.kol + if kl=="\\c" then text=text:gsub("\\1?c[^l\\})]*([\\}])","%1") end + if kl=="\\3c" then text=text:gsub("\\3c[^\\})]*","") end + if kl=="\\4c" then text=text:gsub("\\4c[^\\})]*","") end + if kl=="\\2c" then text=text:gsub("\\2c[^\\})]*","") end + text=text:gsub("{}","") + + local col={} -- table with colours to use + for t=1,res.clrs do table.insert(col,wrap(kl..c[t])) end + if res.bounce and tonumber(res.clrs)>2 then + for t=res.clrs-1,2,-1 do table.insert(col,wrap(kl..c[t])) end + end + + orig=text:gsub(STAG,""):gsub("\\N","{\\N}") + -- save positions for inline tags and comments + inTags=inline_pos(orig) + if not res.word then + -- by letter + local letrz=re.find(visible,".") or {} + letrz=re_test(letrz,visible) + nt="" + local p=1 + for k=2,#letrz do + local ltr=letrz[k].str + -- add colour tag positions to table + if ltr~=" " then + p=p%#col+1 -- cycle colours + table.insert(inTags,{n=k-1,t=col[p]}) + end + end + table.sort(inTags,function(a,b) return a.n0 then text=insertxt(text,BPL,"\\N") end + LBP=BPL or 0 + end + LBP=nil + visible2=nobrea(text) + c=c+1 + until visible==visible2 or c==666 + txt_check(visible,visible2,i) + line.text=text + subs[i]=line + end + end +end + +function insertxt(text,txtpos,thing) + pos=0 + tcount=0 + for tg,tx in text:gmatch("("..ATAG..")([^{]*)") do + sl=tx:len() tl=tg:len() + if sl+posklrs then count=1 end + + text=text:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) + line.text=text + subs[i]=line + end +end + +function shift2(subs,sel) + local k=res.kol + for z,i in ipairs(sel) do + progress("Processing line #"..i-line0.." ["..z.."/"..#sel.."]") + line=subs[i] + text=line.text + tags=text:match(STAG) or "" + inline=text:gsub(STAG,"") + local kol + if res.shit:match"1st" then kol=inline:match(k.."%b&&") inline=inline:gsub(k.."%b&&","",1) + else kol=inline:match(".*("..k.."%b&&)") end + if res.shit:match'last' then inline=inline:gsub("(.*)"..k.."%b&&","%1") end + if res.shit:match'all' then inline=inline:gsub(k.."%b&&","") end + inline=inline:gsub("{}","") + if kol then + if tags=="" then tags=wrap(kol) else tags=addtag3(kol,tags) end + text=tags..inline + line.text=text + subs[i]=line + end + end +end + +-- Match colours -- +function match_col(subs,sel) + local MC + if P=="Match Colours" then _=0 MC=1 + for key,val in ipairs(GUI) do + if val.name and val.name:match"match" and res[val.name] then _=_+1 end + if val.name and val.name=="invert" and res[val.name] then _=_+1 end + end + if _>1 then t_error("Multiple checkboxes for matching checked.\nResults may be unpredictable.") end + end + local RGB, HSL + lvlr=res.R lvlg=res.G lvlb=res.B + hue=res.huehue sat=res.satur lite=res.light + if lvlr~=0 or lvlg~=0 or lvlb~=0 then RGB=true end + if hue~=0 or sat~=0 or lite~=0 then HSL=true end + + for z,i in ipairs(sel) do + progress("Colourising line #"..i-line0.." ["..z.."/"..#sel.."]") + line=subs[i] + text=line.text + if defaref and line.style=="Default" then sr=defaref + elseif lastref and laststyle==line.style then sr=lastref + else sr=stylechk(line.style) end + lastref=sr laststyle=line.style + stylecol=stylecolours() + text=text:gsub("\\1c","\\c") + if not text:match("^{\\") then text=text:gsub("^","{\\}") end + tags=text:match(STAG) + notftags=tags:gsub("\\t%b()","") + + -- Matching + if MC then + if res.match13 then text=macchi(text,"\\c","\\3c",primary) end + if res.match31 then text=macchi(text,"\\3c","\\c",outline) end + if res.match14 then text=macchi(text,"\\c","\\4c",primary) end + if res.match34 then text=macchi(text,"\\3c","\\4c",outline) end + end + + -- switch primary and border + if MC and res.match131 then + if not notftags:match("\\c&") then text=addtag3("\\c"..primary,text) end + if not notftags:match("\\3c") then text=addtag3("\\3c"..outline,text) end + text=text:gsub("\\c&","\\tempc&"):gsub("\\3c","\\c"):gsub("\\tempc","\\3c") + end + + -- Invert All Colours + if MC and res.invert then + match="[" + for n=1,4 do + ctg="\\"..n.."c" + ctg=ctg:gsub("1","") + if not notftags:match(ctg) and n~=2 then text=addtag3(ctg..stylecol[n],text) end + if n>1 and res['k'..n] then match=match..n end + end + match=match..']' + if res.k1 then match=match..'?' end + match=match:gsub("%[%]%?","") + text=text:gsub("(\\"..match.."c&H)(%x%x%x%x%x%x)&",function(tg,col) + invcol="" + for kol in col:gmatch("(%x%x)") do + dkol=tonumber(kol,16) + idkol=255-dkol + ikol=tohex(idkol) + invcol=invcol..ikol + end + return tg..invcol.."&" + end) + end + + -- RGB / HSL + if P=="RGB/HSL" then + corols={} + if res.k1 then table.insert(corols,1) end + if res.k2 then table.insert(corols,2) end + if res.k3 then table.insert(corols,3) end + if res.k4 then table.insert(corols,4) end + for i=1,#corols do + kl="\\"..corols[i].."c" + kl=kl:gsub("1","") + if res.mktag and not notftags:match(kl) then text=addtag3(kl..stylecol[corols[i]],text) end + if RGB then text=rgbhslmod(text,kl,rgbm) end + if HSL then text=rgbhslmod(text,kl,hslm) end + end + end + + text=text:gsub("\\([\\}])","%1") :gsub("\\t%([^\\%)]*%)","") :gsub("{}","") + line.text=text + subs[i]=line + end +end + +function macchi(text,c1,c2,kv) +if not notftags:match(c1.."&") then text=addtag3(c1..kv,text) end +text=text:gsub(ATAG,function(ctags) ctags=ctags:gsub(c2..ACLR,""):gsub(c1.."("..ACLR..")",c1.."%1"..c2.."%1") return ctags end) +return text +end + +function rgbhslmod(text,kl,ef) + local segments={} + if text:match("\\t%b()") then + for seg1,seg2 in text:gmatch("(.-)(\\t%b())") do table.insert(segments,seg1) table.insert(segments,seg2) end + table.insert(segments,text:match("^.*\\t%b()(.-)$")) + else table.insert(segments,text) + end + for q=1,#segments do + if segments[q]:match("\\t%b()") and modetf then segments[q]=ef(segments[q],kl) end + if not segments[q]:match("\\t%b()") and modereg then segments[q]=ef(segments[q],kl) end + end + nt="" + for q=1,#segments do nt=nt..segments[q] end + return nt +end + +function rgbm(text,kl) + text=text:gsub(kl.."&H(%x%x)(%x%x)(%x%x)&",function(kol1,kol2,kol3) + kol1n=brightness(kol1,lvlb) + kol2n=brightness(kol2,lvlg) + kol3n=brightness(kol3,lvlr) + return kl.."&H"..kol1n..kol2n..kol3n.."&" + end) + return text +end + +function hslm(text,kl) + text=text:gsub(kl.."&H(%x%x)(%x%x)(%x%x)&",function(kol1,kol2,kol3) + H1,S1,L1=RGB_to_HSL(kol3,kol2,kol1) + H1,S1,L1=RGB_to_HSL(kol3,kol2,kol1) + H=H1+hue/255 + S=S1+sat/255 + L=L1+lite/255 + if randomise then + H2=H1-hue/255 + S2=S1-sat/255 + L2=L1-lite/255 + H=math.random(H*1000,H2*1000)/1000 + S=math.random(S*1000,S2*1000)/1000 + L=math.random(L*1000,L2*1000)/1000 + end + H,S,L=HSLround(H,S,L) + kol3n,kol2n,kol1n=HSL_to_RGB(H,S,L) + kol3n=tohex(round(kol3n)) + kol2n=tohex(round(kol2n)) + kol1n=tohex(round(kol1n)) + return kl.."&H"..kol1n..kol2n..kol3n.."&" + end) + return text +end + + +-- GRADIENT -- +function gradient(subs,sel) + styleget(subs) + if res.grtype=="RGB" then GRGB=true else GRGB=false end + if res.ast then ast="*" else ast="" end + for z,i in ipairs(sel) do + progress("Colourising line #"..i-line0.." ["..z.."/"..#sel.."]") + line=subs[i] + re_check=0 + repeat + text=line.text + if text:match '\\p1' then break end + text=text:gsub("\\N (%w)","\\N%1"):gsub(" +\\N"," \\N"):gsub("{%*\\[^}]+}",""):gsub("{}","") + visible=nobrea1(text) + -- comments + local komments='' + for cm in text:gmatch("{[^\\{}]-}") do komments=komments..cm end + -- something to [try to] deal with spaces and linebreaks because dumb renderer inconsistency + breaks={} + for br in text:gmatch(" ?\\[Nh]") do + table.insert(breaks,br) + end + acc=line.effect:match("accel ?(%d+%.?%d*)") or res.acc + text=text:gsub("\\c&","\\1c&"):gsub("\\c([}\\])","\\1c%1") :gsub(" *(\\[Nh]) *","{%1}") + :gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end) + text=tagmerge(text) + after=text:gsub(STAG,"") + nc=text:gsub(COMM,"") + if text:match(ATAG.."$") then text=text.."wtfwhywouldyoudothis" end + -- colours from style + sr=stylechk(line.style) + stylecol=stylecolours() + -- which types will be used + applycol={} + if res.k1 and after:match("\\1c") then table.insert(applycol,1) end + if res.k2 and after:match("\\2c") then table.insert(applycol,2) end + if res.k3 and after:match("\\3c") then table.insert(applycol,3) end + if res.k4 and after:match("\\4c") then table.insert(applycol,4) end + + for g=1,#applycol do + ac=applycol[g] + sc=stylecol[ac] + tags=text:match(STAG) or "" + -- linebreak adjustment + if res.gradn then + startc=tags:match("\\"..ac.."c&H%x+&") or "\\"..ac.."c"..sc + endc=nc:match("(\\"..ac.."c&H%x+&)[^}]-}%S+$") or "" + text=text:gsub("([%S])%s*({\\N.-})","{"..endc.."}%1%2{"..startc.."}"):gsub("{}","") + end + -- set style colour to reset c tags + text=text:gsub("(\\"..ac.."c)([}\\])","%1"..sc.."%2") + -- back up original + orig=text + text=text:gsub("{[^\\}]-}","") + -- leave only releavant colour tags, nuke all other ones, add colour from style if missing at the start + ctext=text:gsub("\\N","") --:gsub("\\[ibusaqk]%d?([\\}])","%1") :gsub("\\[^1234][^c][^\\}]*","") :gsub("\\[^"..ac.."]c[^\\}]*","") :gsub("{%**}","") + ctext=ctext:gsub("\\[^\\}]+",function(tag) if tag:match("\\"..ac.."c%b&&") then return tag else return "" end end) :gsub("{%**}","") + if not ctext:match("^{\\") then ctext="{\\kolor}"..ctext end + if not ctext:match("^{[^}]-\\"..ac.."c") then + ctext=ctext:gsub("^({\\[^}]-)}","%1\\"..ac.."c"..sc.."}") end + -- make tables of colour tags and text after them + linecol={} + posi={} + coltext={} + pos=0 + for k,t in ctext:gmatch("{[^}]-\\"..ac.."c&H(%x+)&[^}]-}([^{]+)") do + table.insert(posi,pos) + table.insert(linecol,k) + table.insert(coltext,t) + ps=re.find(t,".") + pos=#ps + end + -- text for each colour + gradtext="" + + -- sequence for each colour tag / text + for c=1,#linecol-1 do + -- get RBG [and HSL if needed] + B1,G1,R1=linecol[c]:match("(%x%x)(%x%x)(%x%x)") + B2,G2,R2=linecol[c+1]:match("(%x%x)(%x%x)(%x%x)") + if not GRGB then + H1,S1,L1=RGB_to_HSL(R1,G1,B1) + H2,S2,L2=RGB_to_HSL(R2,G2,B2) + if res.hueshort then + if H2>H1 and H2-H1>0.5 then H1=H1+1 end + if H2

0.5 then H2=H2+1 end + end + if res.double then + if H2>H1 then H2=H2+1 else H1=H1+1 end + if H1>2 or H2>2 then H2=H2-1 H1=H1-1 end + end + end + + -- letters of this sequence + textseq={} + ltrmatches=re.find(coltext[c],".") + for l=1,#ltrmatches do + table.insert(textseq,ltrmatches[l].str) + end + -- new text starting with original colour tag and first letter + ntxt="{\\"..ac.."c&H"..linecol[c].."&}"..textseq[1] + -- calculate colours for the other letters in sequence + for l=2,posi[c+1] do + if textseq[l]~=" " then + if GRGB then -- RGB + NC=acgrad(linecol[c],linecol[c+1],posi[c+1],l,acc) + else -- HSL + local acc_fac=(l-1)^acc/(posi[c+1])^acc + H=acc_fac*(H2-H1)+H1 + S=acc_fac*(S2-S1)+S1 + L=acc_fac*(L2-L1)+L1 + R,G,B=HSL_to_RGB(H,S,L) + R=tohex(round(R)) + G=tohex(round(G)) + B=tohex(round(B)) + NC="&H"..B..G..R.."&" + end + ncol="{"..ast.."\\"..ac.."c"..NC.."}" + -- colour + letter + ntxt=ntxt..ncol..textseq[l] + else + -- spaces (no tags) + ntxt=ntxt..textseq[l] + end + end + gradtext=gradtext..ntxt + end + -- add final tag + text + gradtext=gradtext.."{\\"..ac.."c&H".. + linecol[#linecol].."&}".. + coltext[#coltext] + text=tags..gradtext + -- merge with original + text=textmod(orig,text) + end + text=text:gsub(ATAG,function(tg) return colkill(tg) end) + text=text:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) + :gsub("wtfwhywouldyoudothis","") + repeat text,r=text:gsub("{([^}]-)(\\[Nh])([^}]-)}","%2{%1%3}") until r==0 + text=text:gsub("{%**}",""):gsub("(%S)(\\[Nh])","%1 %2") + :gsub("([^{])%*\\","%1\\") + local b=0 + text=text:gsub(" \\[Nh]",function() b=b+1 return breaks[b] end)..komments + visible2=nobrea1(text) + if visible~=visible2 then re_check=re_check+1 end + until visible==visible2 or re_check==256 + txt_check(visible,visible2,i) + line.text=text + subs[i]=line + end +end + + +-- Reverse Colours -- +function rvrsgrad(subs,sel) + for z,i in ipairs(sel) do + progress("Colourising line #"..i-line0.." ["..z.."/"..#sel.."]") + line=subs[i] + text=line.text + text=text:gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end) :gsub("\\c&","\\1c&") + after=text:gsub(STAG,"") + applycol={} + if res.k1 and after:match("\\1c") then table.insert(applycol,1) end + if res.k2 and after:match("\\2c") then table.insert(applycol,2) end + if res.k3 and after:match("\\3c") then table.insert(applycol,3) end + if res.k4 and after:match("\\4c") then table.insert(applycol,4) end + + for g=1,#applycol do + ac=applycol[g] + tagtab={} + coltab={} + for tt,cc in text:gmatch("(.-)(\\"..ac.."c&H%x+&)") do table.insert(tagtab,tt..cc) table.insert(coltab,cc) end + END=text:match("^.*\\"..ac.."c&H%x+&(.-)$") + for t=1,#tagtab do o=#tagtab-t+1 + tagtab[t]=tagtab[t]:gsub("\\"..ac.."c&H%x+&",coltab[o]) + end + nt=END + for a=#tagtab,1,-1 do nt=tagtab[a]..nt end + text=nt + end + + text=text:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) :gsub("\\1c&","\\c&") + line.text=text + subs[i]=line + end +end + +function stylecolours() +stylecol={} notf=text:gsub("\\t%b()","") +table.insert(stylecol,(sr.color1:gsub("H%x%x","H"))) primary=notf:match("^{[^}]-\\c(&H%x+&)") or stylecol[1] +table.insert(stylecol,(sr.color2:gsub("H%x%x","H"))) secondary=notf:match("^{[^}]-\\3c(&H%x+&)") or stylecol[2] +table.insert(stylecol,(sr.color3:gsub("H%x%x","H"))) outline=notf:match("^{[^}]-\\3c(&H%x+&)") or stylecol[3] +table.insert(stylecol,(sr.color4:gsub("H%x%x","H"))) shadow=notf:match("^{[^}]-\\c(&H%x+&)")or stylecol[4] +return stylecol +end + +function acgrad(C1,C2,total,l,acc) + local acc_fac=(l-1)^acc/(total)^acc + B1,G1,R1=C1:match("(%x%x)(%x%x)(%x%x)") + B2,G2,R2=C2:match("(%x%x)(%x%x)(%x%x)") + A1=C1:match("(%x%x)") R1=R1 or A1 + A2=C2:match("(%x%x)") R2=R2 or A2 + nR1=(tonumber(R1,16)) nR2=(tonumber(R2,16)) + R=acc_fac*(nR2-nR1)+nR1 + R=tohex(round(R)) + CC="&H"..R.."&" + if B1 then + nG1=(tonumber(G1,16)) nG2=(tonumber(G2,16)) + nB1=(tonumber(B1,16)) nB2=(tonumber(B2,16)) + G=acc_fac*(nG2-nG1)+nG1 + B=acc_fac*(nB2-nB1)+nB1 + G=tohex(round(G)) + B=tohex(round(B)) + CC="&H"..B..G..R.."&" + end +return CC +end + +-- reamnimatools --------------------------------------------- +function esc(str) str=str:gsub("[%%%(%)%[%]%.%-%+%*%?%^%$]","%%%1") return str end +function round(num) num=math.floor(num+0.5) return num end +function tagmerge(t) repeat t,r=t:gsub("({\\[^}]-)}{(\\[^}]-})","%1%2") until r==0 return t end +function wrap(str) return "{"..str.."}" end +function nobra(t) return t:gsub("%b{}","") end +function nobrea(t) return t:gsub("%b{}",""):gsub("\\N","") end +function nobrea1(t) return t:gsub("%b{}",""):gsub(" *\\N *"," ") end +function logg(m) m=tf(m) or "nil" aegisub.log("\n "..m) end +function logg2(m) + local lt=type(m) + aegisub.log("\n >> "..lt) + if lt=='table' then + aegisub.log(" (#"..#m..")") + if not m[1] then + for k,v in pairs(m) do + if type(v)=='table' then vvv='[table]' elseif type(v)=='number' then vvv=v..' (n)' else vvv=v end + aegisub.log("\n "..k..': '..vvv) + end + elseif type(m[1])=='table' then aegisub.log("\n nested table") + else aegisub.log("\n {"..table.concat(m,', ').."}") end + else + m=tf(m) or "nil" aegisub.log("\n "..m) + end +end +function loggtab(m) m=tf(m) or "nil" aegisub.log("\n {"..table.concat(m,';').."}") end +function progress(msg) if aegisub.progress.is_cancelled() then ak() end aegisub.progress.title(msg) end +function t_error(message,cancel) ADD({{class="label",label=message}},{"OK"},{close='OK'}) if cancel then ak() end end + +function tf(val) + if val==true then ret="true" + elseif val==false then ret="false" + else ret=val end + return ret +end + +function detf(txt) + if txt=="true" then ret=true + elseif txt=="false" then ret=false + else ret=txt end + return ret +end + +function addtag3(tg,txt) + no_tf=txt:gsub("\\t%b()","") + tgt=tg:match("(\\%d?%a+)[%d%-&]") if not tgt then t_error("Adding tag '"..tg.."' failed.") end + if no_tf:match("^({[^}]-)"..tgt.."[%d%-&]") then txt=txt:gsub("^({[^}]-)"..tgt.."[%d%-&][^\\}]*","%1"..tg) + elseif not txt:match("^{\\") then txt="{"..tg.."}"..txt + elseif txt:match("^{[^}]-\\t") then txt=txt:gsub("^({[^}]-)\\t","%1"..tg.."\\t") + else txt=txt:gsub("^({\\[^}]-)}","%1"..tg.."}") end + return txt +end + +-- save inline tags +function inline_pos(t) + inTags={} + tl=t:len() + if tl==0 then return {} end + p=0 + t1='' + repeat + seg=t:match("^(%b{})") -- try to match tags/comments + if seg then + table.insert(inTags,{n=p,t=seg}) + else + seg=t:match("^([^{]+)") -- or match text + if not seg then t_error("Error: There appears to be a problem with the brackets here...\n"..t1..t,1) end + SL=re.find(seg,".") + p=p+#SL -- position of next '{' [or end] + end + t1=t1..seg + t=t:gsub("^"..esc(seg),"") + tl=t:len() + until tl==0 + return inTags +end + +-- rebuild inline tags +function inline_ret(t,tab) + tl=t:len() + nt='' + kill='_Z#W_' -- this is supposed to never match + for k,v in ipairs(tab) do + N=tonumber(v.n) + if N==0 then nt=nt..v.t + else + m='.' + -- match how many chars at the start + m=m:rep(N) + RS=re.find(t,m) + seg=RS[1].str + seg=re.sub(seg,'^'..kill,'') + nt=nt..seg..v.t + kill=m -- how many matched in the last round + end + end + -- the rest + seg=re.sub(t,'^'..kill,'') + nt=nt..seg + return nt +end + +function retextmod(orig,text) + local v1,v2,c,t2 + v1=nobrea(orig) + c=0 + repeat + t2=textmod(orig,text) + v2=nobrea(text) + c=c+1 + until v1==v2 or c==666 + if v1~=v2 then logg("Something went wrong with the text...") logg(v1) logg(v2) end + return t2 +end + +function textmod(orig,text) +if text=="" then return orig end + tk={} + tg={} + text=text:gsub("{\\\\k0}","") + text=tagmerge(text) + vis=nobra(text) + ltrmatches=re.find(vis,".") + if not ltrmatches then logg("text: "..text..'\nvisible: '..vis) + logg("If you're seeing this, something really weird is happening with the re module.\nTry this again or rescan Autoload.") + end + for l=1,#ltrmatches do + table.insert(tk,ltrmatches[l].str) + end + stags=text:match(STAG) or "" + text=text:gsub(STAG,"") :gsub("{[^\\}]-}","") + orig=orig:gsub("{([^\\}]+)}",function(c) return wrap("\\\\"..c.."|||") end) + count=0 + for seq in orig:gmatch("[^{]-{%*?\\[^}]-}") do + chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") + pos=re.find(chars,".") + if pos==nil then ps=0+count else ps=#pos+count end + tgl={p=ps,t=tak,a=as} + table.insert(tg,tgl) + count=ps + end + count=0 + for seq in text:gmatch("[^{]-{%*?\\[^}]-}") do + chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") + pos=re.find(chars,".") + if pos==nil then ps=0+count else ps=#pos+count end + tgl={p=ps,t=tak,a=as} + table.insert(tg,tgl) + count=ps + end + newline="" + for i=1,#tk do + newline=newline..tk[i] + newt="" + for n,t in ipairs(tg) do + if t.p==i then newt=newt..t.a..t.t end + end + if newt~="" then newline=newline.."{"..as..newt.."}" end + end + newtext=stags..newline:gsub("(|||)(\\\\)","%1}{%2"):gsub("({[^}]-)\\\\([^\\}]-)|||","{%2}%1") + text=newtext:gsub("{}","") + return text +end + + +function duplikill(tagz) + local tags1={"blur","be","bord","shad","xbord","xshad","ybord","yshad","fs","fsp","fscx","fscy","frz","frx","fry","fax","fay"} + local tags2={"c","2c","3c","4c","1a","2a","3a","4a","alpha"} + tagz=tagz:gsub("\\t%b()",function(t) return t:gsub("\\","|") end) + for i=1,#tags1 do + tag=tags1[i] + repeat tagz,c=tagz:gsub("|"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%1%2") until c==0 + repeat tagz,c=tagz:gsub("\\"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%2%1") until c==0 + end + tagz=tagz:gsub("\\1c&","\\c&") + for i=1,#tags2 do + tag=tags2[i] + repeat tagz,c=tagz:gsub("|"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%1%2") until c==0 + repeat tagz,c=tagz:gsub("\\"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%2%1") until c==0 + end + repeat tagz,c=tagz:gsub("\\fn[^\\}]+([^}]-)(\\fn[^\\}]+)","%2%1") until c==0 + repeat tagz,c=tagz:gsub("(\\[ibusq])%d(.-)(%1%d)","%2%3") until c==0 + repeat tagz,c=tagz:gsub("(\\an)%d(.-)(%1%d)","%3%2") until c==0 + tagz=tagz:gsub("(|i?clip%(%A-%))(.-)(\\i?clip%(%A-%))","%2%3") + :gsub("(\\i?clip%b())(.-)(\\i?clip%b())",function(a,b,c) + if a:match("m") and c:match("m") or not a:match("m") and not c:match("m") then return b..c else return a..b..c end end) + tagz=tagz:gsub("|","\\"):gsub("\\t%([^\\%)]-%)","") + return tagz +end + +function colkill(tagz) + tagz=tagz:gsub("\\1c&","\\c&") + local tags2={"c","2c","3c","4c"} + for i=1,#tags2 do + tag=tags2[i] + tagz=tagz:gsub("\\"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%2%1") + end + return tagz +end + +function RGB_to_HSL(Red,Green,Blue) + R=(tonumber(Red,16)/255) + G=(tonumber(Green,16)/255) + B=(tonumber(Blue,16)/255) + + Min=math.min(R,G,B) + Max=math.max(R,G,B) + del_Max=Max-Min + + L=(Max+Min)/2 + + if del_Max==0 then H=0 S=0 + else + if L<0.5 then S=del_Max/(Max+Min) + else S=del_Max/(2-Max-Min) + end + + del_R=(((Max-R)/6)+(del_Max/2))/del_Max + del_G=(((Max-G)/6)+(del_Max/2))/del_Max + del_B=(((Max-B)/6)+(del_Max/2))/del_Max + + if R==Max then H=del_B-del_G + elseif G==Max then H=(1/3)+del_R-del_B + elseif B==Max then H=(2/3)+del_G-del_R + end + + if H<0 then H=H+1 end + if H>1 then H=H-1 end + end + return H,S,L +end + +function HSL_to_RGB(H,S,L) + if S==0 then + R=L*255 + G=L*255 + B=L*255 + else + if L<0.5 then var_2=L*(1+S) + else var_2=(L+S)-(S*L) + end + var_1=2*L-var_2 + R=255*Hue_to_RGB(var_1,var_2,H+(1/3)) + G=255*Hue_to_RGB(var_1,var_2,H) + B=255*Hue_to_RGB(var_1,var_2,H-(1/3)) + end + return R,G,B +end + +function Hue_to_RGB(v1,v2,vH) + if vH<0 then vH=vH+1 end + if vH>1 then vH=vH-1 end + if (6*vH)<1 then return(v1+(v2-v1)*6*vH) end + if (2*vH)<1 then return(v2) end + if (3*vH)<2 then return(v1+(v2-v1)*((2/3)-vH)*6) end + return(v1) +end + +function HSLround(H,S,L) + if H>1 then H=H-1 end + if H<0 then H=H+1 end + if randomise and S>1 then S=1-S end + if S>1 then S=1 end + if randomise then S=math.abs(S) end + if S<0 then S=0 end + if randomise and L>1 then L=1-L end + if L>1 then L=1 end + if randomise then L=math.abs(L) end + if L<0 then L=0 end + return H,S,L +end + +function brightness(klr,lvl) + klr=tonumber(klr,16) + if randomise then + rAn=math.random(klr-lvl,klr+lvl) + klr=round(rAn) + if klr<0 then klr=math.abs(klr) end + if klr>255 then klr=255-(klr-255) end + else + klr=klr+lvl + end + if klr<0 then klr=0 end + if klr<10 then klr="0"..klr else klr=tohex(klr) end +return klr +end + +function tohex(num) + n1=math.floor(num/16) + n2=math.floor(num%16) + num=tohex1(n1)..tohex1(n2) + return num +end + +function tohex1(num) + HEX={"1","2","3","4","5","6","7","8","9","A","B","C","D","E"} + if num<1 then num="0" elseif num>14 then num="F" else num=HEX[num] end + return num +end + +function styleget(subs) + styles={} + for i=1,#subs do + if subs[i].class=="style" then + table.insert(styles,subs[i]) + end + if subs[i].class=="dialogue" then break end + end +end + +function stylechk(sn) + for i=1,#styles do + if sn==styles[i].name then + sr=styles[i] + if styles[i].name=="Default" then defaref=styles[i] end + break + end + end + if sr==nil then t_error("Style '"..sn.."' doesn't exist.",1) end + return sr +end + +function txt_check(t1,t2,i) + if t1~=t2 then + local bad + for s=1,#badsel do + if badsel[s]==i then bad=1 end + end + if bad then + logg("Line #"..i-line0..": Operation failed, probably because of malformed tags. Undo (Ctrl+Z) and fix the line before trying again.\n") + else + logg("Line #"..i-line0..": It appears that characters have been lost or added. \n If the problem isn't obvious from the two lines below, it's probably a failure of the re module.\n Undo (Ctrl+Z) and try again (Repeat Last might work). If the problem persists, rescan Autoload Dir.\n If you keep getting the SAME strange results, then it may be a bug in Colourise.\n>> "..t1.."\n--> "..t2.."\n") + end + end +end + +function re_test(letrz,visible) + local count=0 + repeat + local nt='' + for l=1,#letrz do + local ltr=letrz[l].str + nt=nt..ltr + end + count=count+1 + if nt~=visible then letrz=re.find(visible,".") or {} end + until nt==visible or count==100 + return letrz +end + +function validateCol(subs,sel) + local err + badsel={} + for z,i in ipairs(sel) do + local line=subs[i] + tx=line.text + for c in tx:gmatch("\\[1234]?c[^l\\})]+") do + if not c:match("c&H%x%x%x%x%x%x&") then logg("Line #"..i-line0..": "..c) err=1 table.insert(badsel,i) end + end + end + if err==1 then t_error("Some malformed colour tags have been found in selected lines.\nThis means the values don't match the standard format of\n&&HFFFFFF&&. Some things are likely to break.\nSee the log for a list of errors found.") end +end + +function repetition() + if lastres and res.rept then res=lastres end +end + +function saveconfig() +colconf="Colourise Configutation\n\n" +for key,val in ipairs(GUI) do + if val.class=="checkbox" and val.name~="save" then colconf=colconf..val.name..":"..tf(res[val.name]).."\n" end + if val.class=="dropdown" and val.name~=": Help Menu :" or val.class=="color" then colconf=colconf..val.name..":"..res[val.name].."\n" end +end +colourconfig=ADP("?user").."\\colourise.conf" +file=io.open(colourconfig,"w") +file:write(colconf) +file:close() +ADD({{class="label",label="Config Saved to:\n"..colourconfig}},{"OK"},{close='OK'}) +end + +function loadconfig() +colourconfig=ADP("?user").."\\colourise.conf" +file=io.open(colourconfig) + if file~=nil then + konf=file:read("*all") + io.close(file) + for key,val in ipairs(GUI) do + if val.class=="checkbox" or val.class=="dropdown" or val.class=="color" then + if konf:match(val.name) and val.name~="help" then val.value=detf(konf:match(val.name..":(.-)\n")) end + end + end + end +end + +function grad_rgb(subs,sel) + cuts(subs) + validateCol(subs,sel) + res={grtype="RGB",acc=1,k1=true,k3=true} + gradient(subs,sel) + return sel +end + +function grad_hsl(subs,sel) + cuts(subs) + validateCol(subs,sel) + res={grtype="HSL",acc=1,k1=true,k3=true,hueshort=true} + gradient(subs,sel) + return sel +end + +if haveDepCtrl then + depRec:registerMacros({ + {script_name,script_description,colourise}, + {": Non-GUI macros :/Colourise: Gradient by Character, RGB","GBC RGB",grad_rgb}, + {": Non-GUI macros :/Colourise: Gradient by Character, HSL","GBC HSL",grad_hsl}, + },false) +else + aegisub.register_macro(script_name,script_description,colourise) + aegisub.register_macro(": Non-GUI macros :/Colourise: Gradient by Character, RGB","GBC RGB",grad_rgb) + aegisub.register_macro(": Non-GUI macros :/Colourise: Gradient by Character, HSL","GBC HSL",grad_hsl) +end \ No newline at end of file diff --git a/automation/autoload/ua.Cycles.lua b/automation/autoload/ua.Cycles.lua new file mode 100644 index 000000000..ea9e5dfea --- /dev/null +++ b/automation/autoload/ua.Cycles.lua @@ -0,0 +1,116 @@ +-- Manual: http://unanimated.hostfree.pw/ts/scripts-manuals.htm#cycle + +script_name="Cycles" +script_description="Cycles blur, border, shadow, alpha, alignment, font spacing" +script_author="unanimated" +script_version="2.0" +script_namespace="ua.Cycles" + +local haveDepCtrl,DependencyControl,depRec=pcall(require,"l0.DependencyControl") +if haveDepCtrl then + script_version="2.0.0" + depRec=DependencyControl{feed="https://raw.githubusercontent.com/unanimated/luaegisub/master/DependencyControl.json"} +end + +-- SETTINGS - You can change these sequences +blur_sequence={"0.6","0.8","1","1.2","1.5","2","2.5","3","4","5","6","8","10","0.4","0.5"} +bord_sequence={"0","1","1.5","2","2.5","3","4","5","6","7","8","9","10","11","12","15","20"} +shad_sequence={"0","1","1.5","2","2.5","3","4","5","6","7","8","9","10","11","12"} +alpha_sequence={"FF","00","10","30","60","80","A0","C0","E0"} +align_sequence={"1","2","3","4","5","6","7","8","9"} +fsp_sequence={"0","1","2","3","4","5","6","7","8","10","12","15","20","30"} + +--[[ Adding more tags +You could make this also work for the following tags: frz, frx, fry, fax, fay, fs, fscx, fscy, be, xbord, xshad, ybord, yshad +by doing 3 things: +1. add a new sequence to the settings above for the tag you want to add +2. add a function below here based on what the others look like (it's adjusted for negative values too) +3. add "aegisub.register_macro("Cycles/YOUR_SCRIPT_NAME","Cycles WHATEVER_YOU_CHOOSE",FUNCTION_NAME_HERE)" at the end of the script +If you at least roughly understand the basics, this should be easy. The main cycle function remains the same for all tags. +Should you want to add other tags with different value patterns, check the existing exceptions for alpha in the cycle function.]] + +function blur(subs,sel) cycle(subs,sel,"blur",blur_sequence) end +function bord(subs,sel) cycle(subs,sel,"bord",bord_sequence) end +function shad(subs,sel) cycle(subs,sel,"shad",shad_sequence) end +function alph(subs,sel) cycle(subs,sel,"alpha",alpha_sequence) end +function algn(subs,sel) cycle(subs,sel,"an",align_sequence) end +function fsp(subs,sel) cycle(subs,sel,"fsp",fsp_sequence) end + +function cycle(subs,sel,tag,sequence) + if tag=="alpha" then base=16 else base=10 end + for z,i in ipairs(sel) do + line=subs[i] + text=line.text + local back + if line.comment or text:match'{switch}$' then back=true end + text=text:gsub("\\t(%b())",function(t) return "\\t"..t:gsub("\\","|") end) + + if tag=="alpha" then val1=text:match("^{[^}]-\\alpha&H(%x%x)&") else val1=text:match("^{[^}]-\\"..tag.."(%-?[%d%.]+)") end + if val1 then + for n=1,#sequence do + N=n+1 + if back then N=n-1 end + if N==0 then N=#sequence end + if val1==sequence[n] then val2=sequence[N] or sequence[1] break end + end + if val2==nil then + for n=1,#sequence do + if n>1 or sequence[1]~="FF" then + local N=n + if back then N=n-1 end + if N==0 then N=#sequence end + if tonumber(val1,base)1 starts slow"}, + {x=4,y=3,width=2,class="floatedit",name="utt",value=1,min=0,hint="accel out - <1 starts fast, >1 starts slow"}, + {x=4,y=0,width=2,class="checkbox",name="alf",label="Alpha/Colour"}, + {x=4,y=1,class="checkbox",name="crl",label="From:"}, + {x=4,y=2,class="checkbox",name="clr",label="To:"}, + {x=5,y=1,class="color",name="c1"}, + {x=5,y=2,class="color",name="c2"}, + {x=0,y=3,class="label",label="Accel:"}, + {x=0,y=4,width=5,class="checkbox",name="keepthefade",label="Keep fade along with colour transforms"}, + {x=0,y=5,width=4,class="checkbox",name="mult",label="Fade across multiple lines"}, + {x=4,y=5,width=2,class="checkbox",name="time",label="Global time"}, + + {x=0,y=6,width=7,class="label",label="\nFades from/to tags will be applied if valid tags && fade in or out are present",}, + {x=0,y=7,class="label",label="Tags:",}, + {x=1,y=7,width=5,class="edit",name="tags",value="\\blur3", + hint="(transformable) tags to be faded from/to\n(bord, shad, xbord, ybord, xshad, yshad, blur, be,\nfs, fscx, fscy, fsp, frz, frx, fry, fax, fay)"}, + {x=6,y=7,class="label",label="e.g. \\blur3\\fs10"}, + {x=0,y=8,class="label",label="In/out:",}, + {x=1,y=8,width=3,class="floatedit",name="tgin",value=0,min=0,hint="fade in for tags"}, + {x=4,y=8,width=2,class="floatedit",name="tgout",value=0,min=0,hint="fade out for tags"}, + {x=6,y=8,class="label",label="times like for \\fad"}, + {x=0,y=9,class="label",label="Accel:",}, + {x=1,y=9,width=3,class="floatedit",name="tai",value=1,min=0,hint="accel for tags in"}, + {x=4,y=9,width=2,class="floatedit",name="tao",value=1,min=0,hint="accel for tags out"}, + {x=6,y=9,class="label",label="acc<1 slows down"}, + {x=6,y=10,class="label",label="acc>1 speeds up"}, + + {x=0,y=10,class="label",label="By letter:"}, + {x=1,y=10,class="dropdown",name="letterfade",items={"40","80","120","160","200","250","300","350","400","450","500","750","1000","1250","1500"},value="120"}, + {x=2,y=10,width=2,class="label",label="ms/letter"}, + {x=4,y=10,class="checkbox",name="rtl",label="RTL",hint="right to left"}, + {x=5,y=10,class="checkbox",name="del",label="Delete",hint="delete letter-by-letter"}, + + {x=0,y=11,width=4,class="checkbox",name="ko",label="Letter by letter using \\ko"}, + {x=4,y=11,width=2,class="checkbox",name="word",label="\\ko by word"}, + {x=6,y=11,class="checkbox",name="mir",label="Mirror tags",hint="fade out to negative value (0-x)\nfor frz, fry, fax, xshad"}, + + {x=0,y=12,width=3,class="checkbox",name="vin",label="Fade in to current frame"}, + {x=4,y=12,width=3,class="checkbox",name="vout",label="Fade out from current frame"}, + + {x=6,y=0,class="checkbox",name="hlp",label="[Help]",hint="Apply Help"}, + {x=6,y=1,class="checkbox",name="rem",label="Remember last"}, + {x=6,y=2,class="checkbox",name="rep",label="Repeat last"}, + {x=6,y=3,class="checkbox",name="save",label="[Save config]"}, + } + loadconfig() + if faded and res.rem then + for key,val in ipairs(GUI) do + if val.class=="checkbox" or val.class=="dropdown" or val.class:match"edit" or val.class=="color" then val.value=res[val.name] end + if val.name=='hlp' then val.value=false end + end + end + P,res=ADD(GUI,{"Apply Fade","Letter by Letter",">Fadeworks<","Fade Away"},{ok='Apply Fade',cancel='Fade Away'}) + fr2ms=aegisub.ms_from_frame + ms2fr=aegisub.frame_from_ms + if rep and res.rep then res=rep end + if res.rep and not rep then t_error("Nothing to repeat",1) end + tagcheck() + fadin=res.fadein + fadout=res.fadeout + faded=true + if TAGS and fadin>0 or TAGS and fadout>0 then res.alf=true end + if res.hlp then P="Apply Fade" end + if P=="Apply Fade" then + if res.hlp then fadehelp(subs,sel) + elseif res.save then saveconfig() + elseif res.alf or TAGS or res.clr or res.crl then fadalpha(subs,sel) + elseif res.mult then fadeacross(subs,sel) + elseif res.vin or res.vout then vfade(subs,sel) + else fade(subs,sel) end + end + if P=="Letter by Letter" then if res.ko or res.word then koko_da(subs,sel) else fade(subs,sel) end end + if P==">Fadeworks<" then sel=fadeworks(subs,sel) end + rep=res + return sel +end + +function tagcheck() + TAGS=nil + ttags=res.tags + if ttags:len()>3 and ttags:match"^\\" then TAGS=1 end + if res.tgin==0 and res.tgout==0 then TAGS=nil end + reftags={"bord","shad","fs","fscx","fscy","fsp","blur","be","frz","frx","fry","fax","fay","xshad","yshad","xbord","ybord"} + checktags="|bord|shad|fs|fscx|fscy|fsp|blur|be|frz|frx|fry|fax|fay|xshad|yshad|xbord|ybord|" + if TAGS then + for tg in ttags:gmatch("\\[^\\]+") do + tg1,tgval=tg:match("\\(%a+)(.*)") + rem=nil + if not checktags:match("|"..tg1.."|") then ttags=ttags:gsub(esc(tg),"") t_error("Wrong tag: "..tg) rem=1 end + if not rem and not tgval:match("^%-?%d+%.?%d*$") then ttags=ttags:gsub(esc(tg),"") t_error("Missing value: "..tg) end + end + if not ttags:match"^\\" then TAGS=nil end + end +end + +function def(fadin,fadout) +if fadin<=1 and fadin>0 then fadin=round(dur*fadin) end +if fadout<=1 and fadout>0 then fadout=round(dur*fadout) end +if fadin<0 then fadin=dur+fadin end +if fadout<0 then fadout=dur+fadout end +if fadin<0 then fadin=0 end +if fadout<0 then fadout=0 end +return fadin,fadout +end + +function fade(subs,sel) + for z,i in ipairs(sel) do + progress("Processing line #"..i-line0.." ["..z.."/"..#sel.."]") + line=subs[i] + text=line.text + dur=line.end_time-line.start_time + fadin,fadout=def(fadin,fadout) + + -- remove existing fade + text=text:gsub("\\fad%b()","") + + -- standard fade + if P=="Apply Fade" then + text="{\\fad("..fadin..","..fadout..")}"..text + text=text:gsub("%)}{\\",")\\") :gsub("{}","") + end + + -- letter by letter + if P=="Letter by Letter" then + + -- delete old letter-by-letter if present + if text:match("\\t%([%d,]+\\alpha[^%(%)]+%)}%S$") then + text=text:gsub("\\t%([%d,]*\\alpha[^%(%)]+%)","") :gsub("\\alpha&H%x+&","") :gsub("{}","") + end + + re_check=0 + if text:match "{[^}]*{" or text:match "}[^{]*}" or text:match "^[^{]*}" or text:match "{[^}]*$" then brackets=true end + local tback=text + visible=text:gsub("%b{}",""):gsub("^ *(.-) *$","%1"):gsub(" *\\N *"," "):gsub("^ +$","") + if not res.del and visible~="" and not brackets and not text:match '\\p1' then repeat text=tback + + -- fail if letter fade is larger than total fade + lf=tonumber(res.letterfade) + if fadin>0 and fadin<=lf or fadout>0 and fadout<=lf then ADD({{class="label", + label="The fade for each letter must be smaller than overall fade."}},{"Fuck! Sorry, I'm stupid. Won't happen again."}) + ak() end + + -- mode: in, out, both + if fadout==0 then mode=1 elseif fadin==0 then mode=2 else mode=3 end + + -- save linebreak patterns + breaks={} + for br in text:gmatch(" ?\\N") do + table.insert(breaks,br) + end + + -- save initial tags; remove other tags/comments + tags=text:match("^({\\[^}]*})") or "" + orig=text:gsub("^({\\[^}]*})","") :gsub("{[^\\}]-}","") :gsub("\\N","{\\N}") + text=text:gsub("%b{}","") :gsub("%s*$","") :gsub("\\N","") + + -- letter-by-letter fade happens here + outfade=dur-fadout + count=0 + text3="" + al=tags:match("^{[^}]-\\alpha&H(%x%x)&") or "00" + + matches=re.find(text,"\\S\\s*") + length=#matches + ftime1=((fadin-lf)/(length-1)) + ftime2=((fadout-lf)/(length-1)) + for _,match in ipairs(matches) do + ch=match.str + -- aegisub.log(ch) + if res.rtl then fin1=math.floor(ftime1*(#matches-count-1)) else fin1=math.floor(ftime1*count) end + fin2=fin1+lf + if res.rtl then fout1=math.floor(ftime2*(#matches-count-1)+outfade) else fout1=math.floor(ftime2*count+outfade) end + fout2=fout1+lf + if mode==1 then text2m="{\\alpha&HFF&\\t("..fin1..","..fin2..",\\alpha&H"..al.."&)}"..ch end + if mode==2 then text2m="{\\alpha&H"..al.."&\\t("..fout1..","..fout2..",\\alpha&HFF&)}"..ch end + if mode==3 then + text2m="{\\alpha&HFF&\\t("..fin1..","..fin2..",\\alpha&H"..al.."&)\\t("..fout1..","..fout2..",\\alpha&HFF&)}"..ch end + text3=text3..text2m + count=count+1 + end + + -- join saved tags + new text with transforms + text=tags..text3 + text=text:gsub("}{","") + if orig:match("{\\") then text=textmod(orig,text) end + + -- fix linebreaks + repeat text,c=text:gsub("{\\N","\\N{") until c==0 + text=text:gsub("{([^}]-)\\N([^}]-)}","\\N{%1%2}"):gsub("{%**}",""):gsub("(%S)\\N","%1 \\N") + local b=0 + text=text:gsub(" \\N",function() b=b+1 return breaks[b] end) + + visible2=text:gsub("%b{}",""):gsub(" *\\N *"," ") + if visible~=visible2 then re_check=re_check+1 end + + until visible==visible2 or re_check==256 + + if visible~=visible2 then + logg("Line #"..i-line0..": It appears that characters have been lost or added. \n If the problem isn't obvious from the two lines below, it's probably a failure of the re module.\n Undo (Ctrl+Z) and try again (Repeat Last might work). If the problem persists, rescan Autoload Dir.\n>> "..visible.."\n--> "..visible2.."\n") + end + end + if brackets then logg("Line #"..i-line0..": Messed up curly brackets. Skipping.\n -> "..text..'\n') end + end + + text=text:gsub("\\fad%(0,0%)","") :gsub("{}","") + if line.text~=text and not brackets then + line.text=text + subs[i]=line + end + brackets=nil + end +end + + +-- ALPHA / COLOURS / TAGS -- ############################################### +function fadalpha(subs,sel) +if res.clr or res.crl then res.alf=true end +if res.vin or res.vout then vfcheck() vt=math.floor((fr2ms(vframe+1)+fr2ms(vframe))/2) end +mirrors="|frz|fry|fax|xshad|" + for z,i in ipairs(sel) do + progress("Processing line: "..z.."/"..#sel) + line=subs[i] + text=line.text + ortext=text + sr=stylechk(subs,line.style) + st,et,dur=times() + fadin,fadout=def(fadin,fadout) + if res.vin then fadin=vt-st end + if res.vout then fadout=et-vt end + + if not text:match("^{\\[^}]-}") then text="{\\arfa}"..text end + + col1=res.c1:gsub("#(%x%x)(%x%x)(%x%x)","&H%3%2%1&") + col2=res.c2:gsub("#(%x%x)(%x%x)(%x%x)","&H%3%2%1&") + SC1=sr.color1:gsub("H%x%x","H") + SC3=sr.color3:gsub("H%x%x","H") + SC4=sr.color4:gsub("H%x%x","H") + + text=text:gsub("\\1c","\\c") + notra=text:gsub("\\t%b()","") + primary=notra:match("^{[^}]-\\c(&H%x+&)") or SC1 + outline=notra:match("^{[^}]-\\3c(&H%x+&)") or SC3 + shadcol=notra:match("^{[^}]-\\4c(&H%x+&)") or SC4 + primary2=text:match("^{[^}]*\\c(&H%x+&)[^}]-}") or SC1 + outline2=text:match("^{[^}]*\\3c(&H%x+&)[^}]-}") or SC3 + shadcol2=text:match("^{[^}]*\\4c(&H%x+&)[^}]-}") or SC4 + border=tonumber(text:match("^{[^}]-\\bord([%d%.]+)")) or sr.outline + shadow=tonumber(text:match("^{[^}]-\\shad([%d%.]+)")) or sr.shadow + + kolora1="\\c"..col1 kolora3="\\3c"..col1 kolora4="\\4c"..col1 kolora,see1,see3,see4="","","","" + kolorb1="\\c"..col2 kolorb3="\\3c"..col2 kolorb4="\\4c"..col2 kolorb="" + if col1~=primary then kolora=kolora..kolora1 see1="\\c"..primary end + if col1~=outline then kolora=kolora..kolora3 see3="\\3c"..outline end + if col1~=shadcol then kolora=kolora..kolora4 see4="\\4c"..shadcol end + if col2~=primary2 then kolorb=kolorb..kolorb1 end + if col2~=outline2 then kolorb=kolorb..kolorb3 end + if col2~=shadcol2 then kolorb=kolorb..kolorb4 end + a00="\\alpha&H00&" aff="\\alpha&HFF&" lb="" + + if res.alf then + if fadin~=0 then + -- fade from colour + if res.crl then + if kolora~="" then + text=text:gsub("^{\\[^}]-}",function(a) + if a:match("\\t") then + nt="" + for n,t in a:gmatch("(.-)(\\t%b())") do nt=nt..n:gsub("\\[34]?c&H%x+&","")..t end + nt=nt..a:match(".*\\t%b()(.-)$"):gsub("\\[34]?c&H%x+&","") + return nt + else return a:gsub("\\[34]?c&H%x+&","") end end) + text=text:gsub("^{}","{\\arfa}") + tfc=kolora.."\\t(0,"..fadin..","..res.inn..","..see1..see3..see4..")" + text=text:gsub("^({\\[^}]-)}",function(a) + if a:match("\\t") then + return a:gsub("^(.-)(\\t.*)","%1"..tfc.."%2}") + else return a..tfc.."}" end end) + if col1==primary and col1~=SC1 then text=addtag3(kolora1,text) end + if col1==outline and col1~=SC3 then text=addtag3(kolora3,text) end + if col1==shadcol and col1~=SC4 then text=addtag3(kolora4,text) end + end + -- inline colour tags + for t in text:gmatch(".({\\[^}]-})") do + det=t:gsub("\\t%b()","") + if det:match("\\[13]?c") then + col1=det:match("(\\c&H%x+&)") or "" if col1==kolora1 then col1="" end + col3=det:match("(\\3c&H%x+&)") or "" if col3==kolora3 then col3="" end + col4=det:match("(\\4c&H%x+&)") or "" if col4==kolora4 then col4="" end + if (col1..col3..col4):len()>0 then + tfic="\\t(0,"..fadin..","..res.inn..","..col1..col3..col4..")" + if t:match("\\t") then + t2="" + for n,tf in t:gmatch("(.-)(\\t%b())") do + t2=t2..n:gsub("\\c&H%x+&",kolora1):gsub("\\3c&H%x+&",kolora3):gsub("\\4c&H%x+&",kolora4)..tf end + t2=t2..t:match(".*\\t%b()(.-)$"):gsub("\\c&H%x+&",kolora1):gsub("\\3c&H%x+&",kolora3):gsub("\\4c&H%x+&",kolora4) + t2=t2:gsub("^(.-)(\\t.*)","%1"..tfic.."%2") + else + t2=t:gsub("\\c&H%x+&",kolora1):gsub("\\3c&H%x+&",kolora3):gsub("\\4c&H%x+&",kolora4) + t2=t2:gsub("({[^}]-)}","%1"..tfic.."}") + end + text=text:gsub(esc(t),t2) + end + end + end + else + -- fade from alpha + subalf=false + st_alf=notra:match("^{[^}]-(\\alpha&H%x%x&)") + st_a1=notra:match("^{[^}]-(\\1a&H%x%x&)") + st_a3=notra:match("^{[^}]-(\\3a&H%x%x&)") + st_a4=notra:match("^{[^}]-(\\4a&H%x%x&)") + if st_alf==nil then toalf=a00 else toalf=st_alf end + tosub=toalf:match("&H%x%x&") + if st_a1==nil then toa1="\\1a"..tosub else subalf=true toa1=st_a1 end + if st_a3==nil then toa3="\\3a"..tosub else subalf=true toa3=st_a3 end + if st_a4==nil then toa4="\\4a"..tosub else subalf=true toa4=st_a4 end + if subalf then toalf=toa1..toa3..toa4 else toalf=toalf end + fromalf=toalf:gsub("&H%x%x&","&HFF&") + text=text:gsub("^{\\[^}]-}",function(a) + if a:match("\\t") then + nt="" + for n,t in a:gmatch("(.-)(\\t%b())") do nt=nt..n:gsub("\\%w+a&H%x+&","")..t end + nt=nt..a:match(".*\\t%b()(.-)$"):gsub("\\%w+a&H%x+&","") + return nt + else return a:gsub("\\%w+a&H%x+&","") end end) + tfa=fromalf.."\\t(0,"..fadin..","..res.inn..","..toalf..")" + text=text:gsub("^({\\[^}]-)}",function(a) + if a:match("\\t") then + return a:gsub("^(.-)(\\t.*)","%1"..tfa.."%2}") + else return a..tfa.."}" end end) + -- inline alpha tags + for t in text:gmatch(".({\\[^}]-})") do + det=t:gsub("\\t%b()","") + arfa=det:match("(\\alpha&H%x+&)") or "" + arf1=det:match("(\\1a&H%x+&)") or "" + arf3=det:match("(\\3a&H%x+&)") or "" + arf4=det:match("(\\4a&H%x+&)") or "" + toarfa=arfa..arf1..arf3..arf4 + if toarfa~="" then + fromarfa=toarfa:gsub("&H%x%x&","&HFF&") + tfia=fromarfa.."\\t(0,"..fadin..","..res.inn..","..toarfa..")" + if t:match("\\t") then + t2="" + for n,tf in t:gmatch("(.-)(\\t%b())") do + t2=t2..n:gsub("\\alpha&H%x+&","") :gsub("\\[134]a&H%x+&","")..tf end + t2=t2..t:match(".*\\t%b()(.-)$"):gsub("\\alpha&H%x+&","") :gsub("\\[134]a&H%x+&","") + t2=t2:gsub("^(.-)(\\t.*)","%1"..tfia.."%2") + else + t2=t:gsub("\\alpha&H%x+&","") :gsub("\\[134]a&H%x+&","") + t2=t2:gsub("({[^}]-)}","%1"..tfia.."}") + end + text=text:gsub(esc(t),t2) + end + end + end + end + + if fadout~=0 then + -- fade to colour + if res.clr then + if kolorb~="" then + text=text:gsub("^({\\[^}]-)}","%1"..lb.."\\t("..dur-fadout..",0,"..res.utt..","..kolorb..")}") + end + -- inline colour tags + for t in text:gmatch(".({\\[^}]-})") do + if t:match("\\[13]?c") or t:match("\\alpha") then + k_out="" + if t:match(".*(\\c&H%x+&)")~=kolorb1 and t:match("\\c&H%x+&") then k_out=k_out..kolorb1 end + if t:match(".*(\\3c&H%x+&)")~=kolorb3 and t:match("\\3c&H%x+&") then k_out=k_out..kolorb3 end + if t:match(".*(\\4c&H%x+&)")~=kolorb4 and t:match("\\4c&H%x+&") then k_out=k_out..kolorb4 end + t2=t:gsub("({\\[^}]-)}","%1\\t("..dur-fadout..",0,"..res.utt..","..k_out..")}") + text=text:gsub(esc(t),t2) + end + end + -- fade to alpha + else + if text:match("^{[^}]-(\\[134]a&H%x%x&)") then toarf="\\1a&HFF&".."\\3a&HFF&".."\\4a&HFF&" else toarf=aff end + text=text:gsub("^({\\[^}]-)}","%1"..lb.."\\t("..dur-fadout..",0,"..res.utt..","..toarf..")}") + -- inline alpha tags + for t in text:gmatch(".({\\[^}]-})") do + toarf="" + if t:match("\\alpha") then toarf=toarf..aff end + if t:match("\\1a") then toarf=toarf.."\\1a&HFF&" end + if t:match("\\3a") then toarf=toarf.."\\3a&HFF&" end + if t:match("\\4a") then toarf=toarf.."\\4a&HFF&" end + if toarf~="" then + t2=t:gsub("({\\[^}]-)}","%1\\t("..dur-fadout..",0,"..res.utt..","..toarf..")}") + text=text:gsub(esc(t),t2) + end + end + end + end + if border==0 then text=text:gsub("\\3c&H%x+&","") end + if shadow==0 then text=text:gsub("\\4c&H%x+&","") end + if not text:match("\\fad%(0,0%)") then text=text:gsub("\\fad%(%d+,%d+%)","") end -- nuke fade + if res.keepthefade then + if res.crl then f1=fadin else f1=0 end + if res.clr then f2=fadout else f2=0 end + if f1+f2>0 then text=text:gsub("^{\\","{\\fad("..f1..","..f2..")\\") end + end + text=text:gsub("\\t%([^\\]+%)","") + end + + -- TAGS ####################################################################################################### + if TAGS then + tgin,tgout=def(res.tgin,res.tgout) + origtags="" + tftagsIN="" + tftagsOUT="" + for tg in ttags:gmatch("\\[^\\]+") do + tag,tgval=tg:match("\\(%a+)(.*)") + midval=0 + STAG=text:match("^{\\[^}]-}") or "" + midval=getvalue(tag) + tftagsIN=tftagsIN.."\\"..tag..tgval + if res.mir and mirrors:match("|"..tag.."|") then tgval=0-tgval end + tftagsOUT=tftagsOUT.."\\"..tag..tgval + origtags=origtags.."\\"..tag..midval + STAG=STAG:gsub("\\"..tag.."[^})\\]*","") + end + if not STAG:match("^{\\") then STAG="{\\notarealtag}"..STAG end + if tgin~=0 then intra=tftagsIN.."\\t(0,"..tgin..","..res.tai..","..origtags..")" else intra=origtags end + if tgout~=0 then transfinal=intra.."\\t("..dur-tgout..",0,"..res.tao..","..tftagsOUT..")" else transfinal=intra end + STAG=STAG:gsub("}",transfinal.."}"):gsub("\\notarealtag","") + STAG=duplikill(STAG) + text=text:gsub("^{\\[^}]-}",STAG) + end + + text=text:gsub("\\arfa","") + line.text=text + subs[i]=line + end +end + +function getvalue(tag) + V=STAG:match("\\"..tag.."(%-?%d+%.?%d*)") + if not V then + if tag=="bord" then V=sr.outline end + if tag=="shad" then V=sr.shadow end + if tag=="fs" then V=sr.fontsize end + if tag=="fsp" then V=sr.spacing end + if tag=="fscx" then V=sr.scale_x end + if tag=="fscy" then V=sr.scale_y end + if tag=="frz" then V=sr.angle end + V=V or "0" + end + return V +end + +function koko_da(subs,sel) + if fadin<1 then t_error("Fade in must be at least 1",true) end + for x,i in ipairs(sel) do + progress("Processing line: "..x.."/"..#sel) + line=subs[i] + text=line.text + text=text:gsub("\\ko%d+","") :gsub("{}","") + + -- save initial tags; remove other tags/comments + tags=text:match("^{\\[^}]-}") or "" + orig=text:gsub("^({\\[^}]*})","") + text=text:gsub("{[^}]*}","") :gsub("%s*$","") :gsub("\\N","*") + + --letter + if not res.word then + matches=re.find(text,"[\\w[:punct:]][\\s\\\\*]*") + len=#matches + if fadin>=40 then ko=round(fadin/(len-1))/10 else ko=fadin end + text=re.sub(text,"([\\w[:punct:]])","{\\\\ko"..ko.."}\\1") + else + --word + matches=re.find(text,"[\\w[:punct:]]+[\\s\\\\*]*") + len=#matches + if fadin>=40 then ko=round(fadin/(len-1)/10) else ko=fadin end + text=re.sub(text,"([\\w[:punct:]]+)","{\\\\ko"..ko.."}\\1") + end + + -- join saved tags + new text with transforms + text=tags..text + if not text:match("\\2a&HFF&") then text=text:gsub("^({.-)}","%1\\2a&HFF&}") end + text=text:gsub("{(\\[^}]-)}{(\\[^}]-)}","{%1%2}") :gsub("%*","\\N") + if orig:match("{\\") then text=textmod(orig,text) end + line.text=text + subs[i]=line + end +end + +function fadeacross(subs,sel) + if fadin<0 then fadin=0 end + if fadout<0 then fadout=0 end + full=0 war=0 + S=subs[sel[1]].start_time + E=subs[sel[#sel]].end_time + -- get total duration + for z,i in ipairs(sel) do + line=subs[i] + dur=line.end_time-line.start_time + full=full+dur + if line.start_timeE then E=line.end_time war=1 end + end + -- Error if fades too long + if res.time and fadin+fadout>E-S or not res.time and fadin+fadout>full then + t_error("Error. Fades are longer than the duration of lines",true) + end + -- Warning if not sorted by time + if war==1 then t_error("Not sorted by time. \nDeal with the consequences.") end + -- Fade + full2=E-S + if res.time then full=full2 end + durs=0 durs1=0 + for z,i in ipairs(sel) do + progress("Processing line: "..z.."/"..#sel) + line=subs[i] + text=line.text + start,endt,dur=times() + startf=ms2fr(start) + endf=ms2fr(endt) + if endf-startf==1 then oneframe=true else oneframe=false end + -- check shadow alpha + sr=stylechk(subs,line.style) + shad=sr.color4:match("H(%x%x)") + if text:match("\\4a") then shad=text:match("\\4a&H(%x%x)") end + if shad~="00" then shade=1-(tonumber(shad,16)/256) end + kill=1 + -- fade in + if dursfadin then in_end=dur-(durs-fadin) tim="0,"..in_end.."," kill=0 else tim="" end + alfin_s=tohex(255-(round((durs-dur)/fadin*255))) + alfin_e=tohex(255-(round(durs/fadin*255))) + alfin_1=tohex(255-(round((durs-dur/2)/fadin*255))) + if shad~="00" then + shin_s=tohex(255-round((255-tonumber(alfin_s,16))*shade)) + shin_e=tohex(255-round((255-tonumber(alfin_e,16))*shade)) + shin_1=tohex(255-round((255-tonumber(alfin_1,16))*shade)) + instart="\\1a&H"..alfin_s.."&\\3a&H"..alfin_s.."&\\4a&H"..shin_s.."&" + inend="\\1a&H"..alfin_e.."&\\3a&H"..alfin_e.."&\\4a&H"..shin_e.."&" + onefadin="\\1a&H"..alfin_1.."&\\3a&H"..alfin_1.."&\\4a&H"..shin_1.."&" + else + instart="\\alpha&H"..alfin_s.."&" inend="\\alpha&H"..alfin_e.."&" + onefadin="\\alpha&H"..alfin_1.."&" + end + if alfin_s~=alfin_e then + if oneframe then text=text:gsub("^({\\[^}]-)}","%1"..onefadin.."}") + else text=text:gsub("^({\\[^}]-)}","%1"..instart.."\\t("..tim..inend..")}") + end + end + elseif fadin>start-S and z>1 and start==subs[i-1].start_time then killpha() + text=text:gsub("^({\\[^}]-)}","%1\\alpha&H"..alfin_s.."&\\t("..tim.."\\alpha&H"..alfin_e.."&)}") + end + -- fade out + dure1=full + dure2=E-start + if res.time then dure=dure2 full=dure2-dur else dure=dure1 full=full-dur end + if fullfadout then out_start=dure-fadout tim=out_start..","..dur.."," else tim="" end + alfout_s=tohex(255-(round(dure/fadout*255))) + alfout_e=tohex(255-(round(full/fadout*255))) + alfout_1=tohex(255-(round((dure+full)/2/fadout*255))) + if shad~="00" then + shout_s=tohex(255-round((255-tonumber(alfout_s,16))*shade)) + shout_e=tohex(255-round((255-tonumber(alfout_e,16))*shade)) + shout_1=tohex(255-round((255-tonumber(alfout_1,16))*shade)) + outstart="\\1a&H"..alfout_s.."&\\3a&H"..alfout_s.."&\\4a&H"..shout_s.."&" + outend="\\1a&H"..alfout_e.."&\\3a&H"..alfout_e.."&\\4a&H"..shout_e.."&" + onefadeout="\\1a&H"..alfout_1.."&\\3a&H"..alfout_1.."&\\4a&H"..shout_1.."&" + else + outstart="\\alpha&H"..alfout_s.."&" outend="\\alpha&H"..alfout_e.."&" + onefadeout="\\alpha&H"..alfout_1.."&" + end + if kill==1 then autstart=outstart else autstart="" end + if alfout_s~=alfout_e then + if oneframe then text=text:gsub("^({\\[^}]-)}","%1"..onefadeout.."}") + else text=text:gsub("^({\\[^}]-)}","%1"..autstart.."\\t("..tim..outend..")}") + end + end + end + text=text:gsub("\\fake","") :gsub("{}","") + line.text=text + subs[i]=line + end +end + +function vfade(subs,sel) + vfcheck() + for z,i in ipairs(sel) do + line=subs[i] + text=line.text + st,et=times() + vt=math.floor((fr2ms(vframe+1)+fr2ms(vframe))/2) + vfin=vt-st + vfut=et-vt + if not text:match("\\fad%(") then text="{\\fad(0,0)}"..text text=text:gsub("{\\fad%(0,0%)}{\\","{\\fad(0,0)\\") end + if res.vin and vfin>0 then text=text:gsub("\\fad%(%d+,(%d+)%)","\\fad("..vfin..",%1)") end + if res.vout and vfut>0 then text=text:gsub("\\fad%((%d+),%d+%)","\\fad(%1,"..vfut..")") end + text=text:gsub("\\fad%(0,0%)","") :gsub("{}","") + line.text=text + subs[i]=line + end +end + +function vfcheck() + if aegisub.project_properties==nil then + t_error("Current frame unknown.\nProbably your Aegisub is too old.\nMinimum required: r8374.",true) + end + vframe=aegisub.project_properties().video_position + if vframe==nil or fr2ms(1)==nil then t_error("Current frame unknown. Probably no video loaded.",true) end +end + + + +-- FADEWORKS ############################################################################################### +function fadeworks(subs,sel) + nsel={} for z,i in ipairs(sel) do table.insert(nsel,i) end + fadegui={ + {x=1,y=0,class="label",label="Lines before start time"}, + {x=2,y=0,class="label",label="Lines after end time"}, + {x=0,y=1,class="label",label="Lines to create:"}, + {x=1,y=1,class="intedit",name="LF",value=5,min=0}, + {x=2,y=1,class="intedit",name="RF",value=5,min=0}, + {x=0,y=2,class="label",label="Frames per line:"}, + {x=1,y=2,class="intedit",name="LFram",value=1,min=1}, + {x=2,y=2,class="intedit",name="RFram",value=1,min=1}, + {x=0,y=3,class="label",label="Shift each line by:"}, + {x=1,y=3,class="intedit",name="LFshift",value=1,min=1,hint="Should be same/lower than # of frames"}, + {x=2,y=3,class="intedit",name="RFshift",value=1,min=1,hint="Should be same/lower than # of frames"}, + {x=0,y=4,class="label",label="X distance:"}, + {x=1,y=4,class="floatedit",name="LFX",value=0}, + {x=2,y=4,class="floatedit",name="RFX",value=0}, + {x=0,y=5,class="label",label="Y distance:"}, + {x=1,y=5,class="floatedit",name="LFY"}, + {x=2,y=5,class="floatedit",name="RFY"}, + {x=0,y=6,class="label",label="X acceleration:"}, + {x=1,y=6,class="floatedit",name="LFacx",value=1,min=0}, + {x=2,y=6,class="floatedit",name="RFacx",value=1,min=0}, + {x=0,y=7,class="label",label="Y acceleration:"}, + {x=1,y=7,class="floatedit",name="LFacy",value=1,min=0}, + {x=2,y=7,class="floatedit",name="RFacy",value=1,min=0}, + {x=0,y=8,class="label",label="fbf transform*:"}, + {x=1,y=8,class="edit",name="Lfbf",value="\\",hint="tags to transform FROM"}, + {x=2,y=8,class="edit",name="Rfbf",value="\\",hint="tags to transform TO"}, + {x=0,y=9,class="label",label="fbf alpha tf:"}, + {x=1,y=9,class="edit",name="Lalf",hint="start alpha in hexadecimal"}, + {x=2,y=9,class="edit",name="Ralf",hint="end alpha in hexadecimal"}, + {x=0,y=10,class="label",label="\\t acceleration:"}, + {x=1,y=10,class="floatedit",name="LFact",value=1,min=0}, + {x=2,y=10,class="floatedit",name="RFact",value=1,min=0}, + + {x=0,y=11,class="label",label="Flickering:"}, + {x=1,y=11,class="checkbox",name="LFad",label="Fade in"}, + {x=2,y=11,class="checkbox",name="RFad",label="Fade out"}, + + {x=0,y=12,class="label",label=" * get colours:"}, + {x=1,y=12,class="color",name="col"}, + {x=2,y=12,class="edit",name="kol",hint="Paste the result to 'fbf transform'\n(modify the \\c type)"}, + + {x=0,y=13,class="label",label="Fade mode:"}, + {x=1,y=13,width=2,class="checkbox",name="Fade",label="Use existing \\fad for line length and start/end point",hint="experimental feature intended to make overlapping lines"}, + {x=0,y=14,class="label",label=" add transform:"}, + {x=1,y=14,class="edit",name="LFtra",value="\\",hint="tags to transform FROM"}, + {x=2,y=14,class="edit",name="RFtra",value="\\",hint="tags to transform TO"}, + + {x=3,y=3,class="label",label="frames"}, + {x=3,y=4,class="label",label="pixels"}, + {x=3,y=5,class="label",label="pixels"}, + } + if resfade then + for k,v in ipairs(fadegui) do + if v.name then v.value=rez[v.name] end + end + end + pres=nil + + repeat + if pres=="Save/Load" then + fwsafe() + if Pf=="Inhume" then + for k,v in ipairs(fadegui) do + if v.name then v.value=rez[v.name] end + end + end + end + if pres=="Get colour tag" then + K=rez.col:gsub("#(%x%x)(%x%x)(%x%x)","\\c&H%3%2%1&") + for k,v in ipairs(fadegui) do + if v.name=="kol" then v.value=K else v.value=rez[v.name] end + end + end + pres,rez=ADD(fadegui,{"OK","Save/Load","Get colour tag","Cancel"},{ok='OK',close='Cancel'}) + until pres=="OK" or pres=="Cancel" + + resfade=rez + if pres=="Cancel" then ak() end + if rez.Fade then rez.LFad=false rez.RFad=false end + + -- fadeworks lines -- + for z=#sel,1,-1 do + i=sel[z] + line=subs[i] + text=line.text + startf=ms2fr(line.start_time) + endf=ms2fr(line.end_time) + styleref=stylechk(subs,line.style) + if not text:match("\\pos%(") then text=getpos(subs,text) end + pX,pY=text:match("\\pos%(([^,]-),([^,]-)%)") + if rez.Lfbf:match 'fs[cp]' or rez.Rfbf:match 'fs[cp]' then text=text:gsub("^{\\","{\\q2\\") end + if rez.Fade then + f1,f2=text:match("\\fad%(([^,]-),([^,]-)%)") + if not f1 then t_error("Abort: No \\fad tag on line #"..i-line0,1) end + rez.LFram=ms2fr(f1) + rez.RFram=ms2fr(f2) + else + if rez.LFad then f1=fr2ms(rez.LFram) else f1=0 end + if rez.RFad then f2=fr2ms(rez.RFram) else f2=0 end + end + redshift=0 + blushift=0 + if not rez.Fade then + redshift=rez.LFram-rez.LFshift + blushift=rez.RFram-rez.RFshift + end + + -- replicating OUT + if rez.RF>0 then + for r=rez.RF,1,-1 do + l2=line + posx=numgrad(pX,pX+rez.RFX,rez.RF+1,r+1,rez.RFacx) + posy=numgrad(pY,pY+rez.RFY,rez.RF+1,r+1,rez.RFacy) + text2=text:gsub("\\pos%b()","\\pos("..posx..","..posy..")"):gsub("\\t%b()","") + stags=text:match("^%b{}") or "" + -- save tags from transforms for OUT lines + tratags="" + for tra in stags:gmatch("\\t%b()") do + tTags=tra:match("\\t%(.-(\\.*)%)") + tratags=tratags..tTags + end + nontra=text2:gsub("\\t%b()","") + if rez.Fade and rez.RFtra:len()>1 then + text2=text2:gsub("^({\\[^}]-)}","%1\\t("..rez.RFtra..")}") + end + if rez.RFad then text2=text2:gsub("\\fad%b()",""):gsub("^{","{\\fad(0,"..f2..")") end + -- tags from orig. line's transforms to text2 + for tag in tratags:gmatch("\\[^\\]+") do text2=addtag3(tag,text2) end + -- tags transforming line by line + if rez.Rfbf:len()>1 then + for tag in rez.Rfbf:gmatch("\\[^\\]+") do + tg,val=tag:match("(\\%d?%a+)([%d&%-][^\\}]*)") + -- from \t or from not \t or from style + endval=tratags:match(tg.."([%d&%-][^\\}]*)") or nontra:match(tg.."([%d&%-][^\\}]*)") or styleval(tg) + if tg=="\\c" or tg:match"%d" then + nval=acgrad(val,endval,rez.RF+1,rez.RF-r+1,1/rez.RFact) + else + nval=numgrad(val,endval,rez.RF+1,rez.RF-r+1,1/rez.RFact) + end + text2=addtag3(tg..nval,text2) + end + end + if rez.Ralf:len()>1 then + ralf=rez.Ralf:match("%x%x") + if ralf then + endval=nontra:match("alpha&H(%x%x)&") or "00" + nval=acgrad(ralf,endval,rez.RF+1,rez.RF-r+1,1/rez.RFact) + text2=addtag3("\\alpha"..nval,text2) + end + end + startf2=endf+rez.RFshift*(r-1) + endf2=startf2+rez.RFram + if rez.Fade then + text2=text2:gsub("\\fad%(([^,]+),([^,]+)%)","\\fad(0,%2)") + startf2=startf2-rez.RFram+rez.RFshift endf2=endf2-rez.RFram+rez.RFshift + end + l2.start_time=fr2ms(startf2-blushift) + l2.end_time=fr2ms(endf2-blushift) + l2.text=text2 + subs.insert(i+1,l2) + nsel=shiftsel2(nsel,i,1) + end + end + + -- main line + line.start_time=fr2ms(startf) + line.end_time=fr2ms(endf) + if rez.LFad or rez.RFad then + text=text:gsub("\\fad%b()",""):gsub("^{","{\\fad("..f1..","..f2..")") + end + line.text=text + subs.insert(i+1,line) + + -- replicating IN + if rez.LF>0 then + for r=1,rez.LF do + l2=line + posx=numgrad(pX,pX+rez.LFX,rez.LF+1,r+1,rez.LFacx) + posy=numgrad(pY,pY+rez.LFY,rez.LF+1,r+1,rez.LFacy) + text2=text:gsub("\\pos%b()","\\pos("..posx..","..posy..")"):gsub("\\t%b()","") + nontra=text2:gsub("\\t%b()","") + if rez.Fade and rez.LFtra:len()>1 then + Ltra="{}" + for tag in rez.LFtra:gmatch("\\[^\\]+") do + tg=tag:match("(\\%d?%a+)[%d&%-]") + if nontra:match(tg.."[%d&%-]") then + Ltra=Ltra:gsub("{","{"..nontra:match(tg.."[%d&%-][^\\}]*")) + else Ltra=fill_in(Ltra,tg) end + text2=addtag3(tag,text2) + end + Ltra=Ltra:gsub("[{}]","") + text2=text2:gsub("^({\\[^}]-)}","%1\\t("..Ltra..")}") + end + if rez.LFad then text2=text2:gsub("\\fad%b()",""):gsub("^{","{\\fad("..f1..",0)") end + if rez.Lfbf:len()>1 then + for tag in rez.Lfbf:gmatch("\\[^\\]+") do + tg,val=tag:match("(\\%d?%a+)([%d&%-][^\\}]*)") + endval=nontra:match(tg.."([%d&%-][^\\}]*)") or styleval(tg) + endval=tostring(endval) + if tg=="\\c" or tg:match"%d" then + nval=acgrad(val,endval,rez.LF+1,rez.LF-r+1,1/rez.LFact) + else + nval=numgrad(val,endval,rez.LF+1,rez.LF-r+1,1/rez.LFact) + end + text2=addtag3(tg..nval,text2) + end + end + if rez.Lalf:len()>1 then + lalf=rez.Lalf:match("%x%x") + if lalf then + endval=nontra:match("alpha&H(%x%x)&") or "00" + nval=acgrad(lalf,endval,rez.LF+1,rez.LF-r+1,1/rez.LFact) + text2=addtag3("\\alpha"..nval,text2) + end + end + + endf2=startf-rez.LFshift*(r-1) + startf2=endf2-rez.LFram + if rez.Fade then + text2=text2:gsub("\\fad%(([^,]+),([^,]+)%)","\\fad(%1,0)") + startf2=startf2+rez.LFram-rez.LFshift endf2=endf2+rez.LFram-rez.LFshift + end + l2.start_time=fr2ms(startf2+redshift) + l2.end_time=fr2ms(endf2+redshift) + l2.text=text2 + subs.insert(i+1,l2) + nsel=shiftsel2(nsel,i,1) + end + end + + subs.delete(i) + sel=nsel + end + return sel +end + +function retextmod(orig,text) + local v1,v2,c,t2 + v1=nobrea(orig) + c=0 + repeat + t2=textmod(orig,text) + v2=nobrea(text) + c=c+1 + until v1==v2 or c==666 + if v1~=v2 then logg("Something went wrong with the text...") logg(v1) logg(v2) end + return t2 +end + +-- reanimatools --------------------- +function esc(str) str=str:gsub("[%%%(%)%[%]%.%-%+%*%?%^%$]","%%%1") return str end +function round(n,dec) dec=dec or 0 n=math.floor(n*10^dec+0.5)/10^dec return n end +function wrap(str) return "{"..str.."}" end +function nobra(t) return t:gsub("%b{}","") end +function nobrea(t) return t:gsub("%b{}",""):gsub("\\[Nh]","") end +function nobrea1(t) return t:gsub("%b{}",""):gsub(" *\\[Nh] *"," ") end +function tagmerge(t) repeat t,r=t:gsub("({\\[^}]-)}{(\\[^}]-})","%1%2") until r==0 return t end +function logg(m) m=tf(m) or "nil" aegisub.log("\n "..m) end +function progress(msg) if aegisub.progress.is_cancelled() then ak() end aegisub.progress.title(msg) end +function t_error(message,cancel) ADD({{class="label",label=message}},{"OK"},{close='OK'}) if cancel then ak() end end + +function textmod(orig,text) +if text=="" then return orig end + tk={} + tg={} + text=text:gsub("{\\\\k0}","") + text=tagmerge(text) + vis=nobra(text) + ltrmatches=re.find(vis,".") + if not ltrmatches then logg("text: "..text..'\nvisible: '..vis) + logg("If you're seeing this, something really weird is happening with the re module.\nTry this again or rescan Autoload.") + end + for l=1,#ltrmatches do + table.insert(tk,ltrmatches[l].str) + end + stags=text:match(STAG) or "" + text=text:gsub(STAG,"") :gsub("{[^\\}]-}","") + orig=orig:gsub("{([^\\}]+)}",function(c) return wrap("\\\\"..c.."|||") end) + count=0 + for seq in orig:gmatch("[^{]-{%*?\\[^}]-}") do + chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") + pos=re.find(chars,".") + if pos==nil then ps=0+count else ps=#pos+count end + tgl={p=ps,t=tak,a=as} + table.insert(tg,tgl) + count=ps + end + count=0 + for seq in text:gmatch("[^{]-{%*?\\[^}]-}") do + chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") + pos=re.find(chars,".") + if pos==nil then ps=0+count else ps=#pos+count end + tgl={p=ps,t=tak,a=as} + table.insert(tg,tgl) + count=ps + end + newline="" + for i=1,#tk do + newline=newline..tk[i] + newt="" + for n,t in ipairs(tg) do + if t.p==i then newt=newt..t.a..t.t end + end + if newt~="" then newline=newline.."{"..as..newt.."}" end + end + newtext=stags..newline:gsub("(|||)(\\\\)","%1}{%2"):gsub("({[^}]-)\\\\([^\\}]-)|||","{%2}%1") + text=newtext:gsub("{}","") + return text +end + +function addtag3(tg,txt) + no_tf=txt:gsub("\\t%b()","") + tgt=tg:match("(\\%d?%a+)[%d%-&]") val="[%d%-&]" + if not tgt then tgt=tg:match("(\\%d?%a+)%b()") val="%b()" end + if not tgt then tgt=tg:match("\\fn") val="" end + if not tgt then t_error("adding tag '"..tg.."' failed.") end + if tgt:match("clip") then txt,r=txt:gsub("^({[^}]-)\\i?clip%b()","%1"..tg) + if r==0 then txt=txt:gsub("^({\\[^}]-)}","%1"..tg.."}") end + elseif no_tf:match("^({[^}]-)"..tgt..val) then txt=txt:gsub("^({[^}]-)"..tgt..val.."[^\\}]*","%1"..tg) + elseif not txt:match("^{\\") then txt="{"..tg.."}"..txt + elseif txt:match("^{[^}]-\\t") then txt=txt:gsub("^({[^}]-)\\t","%1"..tg.."\\t") + else txt=txt:gsub("^({\\[^}]-)}","%1"..tg.."}") end +return txt +end + +function styleval(tag) + if tag=="\\bord" then s_val=styleref.outline + elseif tag=="\\shad" then s_val=styleref.shadow + elseif tag=="\\fscx" then s_val=styleref.scale_x + elseif tag=="\\fscy" then s_val=styleref.scale_y + elseif tag=="\\fs" then s_val=styleref.fontsize + elseif tag=="\\fsp" then s_val=styleref.spacing + elseif tag=="\\alpha" then s_val="&H00&" + elseif tag=="\\1a" then s_val="&"..styleref.color1:match("H%x%x").."&" + elseif tag=="\\2a" then s_val="&"..styleref.color2:match("H%x%x").."&" + elseif tag=="\\3a" then s_val="&"..styleref.color3:match("H%x%x").."&" + elseif tag=="\\4a" then s_val="&"..styleref.color4:match("H%x%x").."&" + elseif tag=="\\c" then s_val=styleref.color1:gsub("H%x%x","H") + elseif tag=="\\2c" then s_val=styleref.color2:gsub("H%x%x","H") + elseif tag=="\\3c" then s_val=styleref.color3:gsub("H%x%x","H") + elseif tag=="\\4c" then s_val=styleref.color4:gsub("H%x%x","H") + else s_val="0" + end + return s_val +end + +function duplikill(tagz) + local tags1={"blur","be","bord","shad","xbord","xshad","ybord","yshad","fs","fsp","fscx","fscy","frz","frx","fry","fax","fay"} + local tags2={"c","2c","3c","4c","1a","2a","3a","4a","alpha"} + tagz=tagz:gsub("\\t%b()",function(t) return t:gsub("\\","|") end) + for i=1,#tags1 do + tag=tags1[i] + repeat tagz,c=tagz:gsub("|"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%1%2") until c==0 + repeat tagz,c=tagz:gsub("\\"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%2%1") until c==0 + end + tagz=tagz:gsub("\\1c&","\\c&") + for i=1,#tags2 do + tag=tags2[i] + repeat tagz,c=tagz:gsub("|"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%1%2") until c==0 + repeat tagz,c=tagz:gsub("\\"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%2%1") until c==0 + end + repeat tagz,c=tagz:gsub("\\fn[^\\}]+([^}]-)(\\fn[^\\}]+)","%2%1") until c==0 + repeat tagz,c=tagz:gsub("(\\[ibusq])%d(.-)(%1%d)","%2%3") until c==0 + repeat tagz,c=tagz:gsub("(\\an)%d(.-)(%1%d)","%3%2") until c==0 + tagz=tagz:gsub("(|i?clip%(%A-%))(.-)(\\i?clip%(%A-%))","%2%3") + :gsub("(\\i?clip%b())(.-)(\\i?clip%b())",function(a,b,c) + if a:match("m") and c:match("m") or not a:match("m") and not c:match("m") then return b..c else return a..b..c end end) + tagz=tagz:gsub("|","\\"):gsub("\\t%([^\\%)]-%)","") + return tagz +end + +function killpha() + if shad~="00" then text=text:gsub("\\[1234]a&H%x%x&","") end + text=text:gsub("\\fad%([%d%.%,]-%)",""):gsub("\\alpha&H%x%x&",""):gsub("\\t%([^\\%)]-%)",""):gsub("{}","") + if not text:match("^{\\") then text="{\\fake}"..text end +end + +function times() + st=line.start_time + et=line.end_time + dur=et-st + return st,et,dur +end + +function tohex(num) + n1=math.floor(num/16) + n2=math.floor(num%16) + num=tohex1(n1)..tohex1(n2) + return num +end + +function tohex1(num) + HEX={"1","2","3","4","5","6","7","8","9","A","B","C","D","E"} + if num<1 then num="0" elseif num>14 then num="F" else num=HEX[num] end + return num +end + +function numgrad(V1,V2,total,l,acc) + acc=acc or 1 + acc_fac=(l-1)^acc/(total-1)^acc + VC=round(acc_fac*(V2-V1)+V1,2) +return VC +end + +function acgrad(C1,C2,total,l,acc) + acc=acc or 1 + acc_fac=(l-1)^acc/(total-1)^acc + B1,G1,R1=C1:match("(%x%x)(%x%x)(%x%x)") + B2,G2,R2=C2:match("(%x%x)(%x%x)(%x%x)") + A1=C1:match("(%x%x)") R1=R1 or A1 + A2=C2:match("(%x%x)") R2=R2 or A2 + nR1=(tonumber(R1,16)) nR2=(tonumber(R2,16)) + R=acc_fac*(nR2-nR1)+nR1 + R=tohex(round(R)) + CC="&H"..R.."&" + if B1 then + nG1=(tonumber(G1,16)) nG2=(tonumber(G2,16)) + nB1=(tonumber(B1,16)) nB2=(tonumber(B2,16)) + G=acc_fac*(nG2-nG1)+nG1 + B=acc_fac*(nB2-nB1)+nB1 + G=tohex(round(G)) + B=tohex(round(B)) + CC="&H"..B..G..R.."&" + end +return CC +end + +function getpos(subs,text) + st=nil defst=nil + for g=1,#subs do + if subs[g].class=="info" then + local k=subs[g].key + local v=subs[g].value + if k=="PlayResX" then resx=v end + if k=="PlayResY" then resy=v end + end + if resx==nil then resx=0 end + if resy==nil then resy=0 end + if subs[g].class=="style" then + local s=subs[g] + if s.name==line.style then st=s break end + if s.name=="Default" then defst=s end + end + if subs[g].class=="dialogue" then + if defst then st=defst else t_error("Style '"..line.style.."' not found.\nStyle 'Default' not found. ",1) end + break + end + end + if st then + acleft=st.margin_l if line.margin_l>0 then acleft=line.margin_l end + acright=st.margin_r if line.margin_r>0 then acright=line.margin_r end + acvert=st.margin_t if line.margin_t>0 then acvert=line.margin_t end + acalign=st.align if text:match("\\an%d") then acalign=text:match("\\an(%d)") end + aligntop="789" alignbot="123" aligncent="456" + alignleft="147" alignright="369" alignmid="258" + if alignleft:match(acalign) then horz=acleft + elseif alignright:match(acalign) then horz=resx-acright + elseif alignmid:match(acalign) then horz=resx/2 end + if aligntop:match(acalign) then vert=acvert + elseif alignbot:match(acalign) then vert=resy-acvert + elseif aligncent:match(acalign) then vert=resy/2 end + end + if horz>0 and vert>0 then + if not text:match("^{\\") then text="{\\rel}"..text end + text=text:gsub("^({\\[^}]-)}","%1\\pos("..horz..","..vert..")}") :gsub("\\rel","") + end + return text +end + +function fill_in(tags,tag) + if tag=="\\bord" then tags=tags:gsub("^{","{"..tag..styleref.outline) + elseif tag=="\\shad" then tags=tags:gsub("^{","{"..tag..styleref.shadow) + elseif tag=="\\fscx" then tags=tags:gsub("^{","{"..tag..styleref.scale_x) + elseif tag=="\\fscy" then tags=tags:gsub("^{","{"..tag..styleref.scale_y) + elseif tag=="\\fs" or tag=="\\fsize" then tags=tags:gsub("^{","{"..tag..styleref.fontsize) + elseif tag=="\\fsp" then tags=tags:gsub("^{","{"..tag..styleref.spacing) + elseif tag=="\\alpha" then tags=tags:gsub("^{","{"..tag.."&H00&") + elseif tag=="\\1a" then tags=tags:gsub("^{","{"..tag.."&"..styleref.color1:match("H%x%x").."&") + elseif tag=="\\2a" then tags=tags:gsub("^{","{"..tag.."&"..styleref.color2:match("H%x%x").."&") + elseif tag=="\\3a" then tags=tags:gsub("^{","{"..tag.."&"..styleref.color3:match("H%x%x").."&") + elseif tag=="\\4a" then tags=tags:gsub("^{","{"..tag.."&"..styleref.color4:match("H%x%x").."&") + elseif tag=="\\c" then tags=tags:gsub("^{","{"..tag..styleref.color1:gsub("H%x%x","H")) + elseif tag=="\\2c" then tags=tags:gsub("^{","{"..tag..styleref.color2:gsub("H%x%x","H")) + elseif tag=="\\3c" then tags=tags:gsub("^{","{"..tag..styleref.color3:gsub("H%x%x","H")) + elseif tag=="\\4c" then tags=tags:gsub("^{","{"..tag..styleref.color4:gsub("H%x%x","H")) + else tags=tags:gsub("^{","{"..tag.."0") + end + return tags +end + +function shiftsel2(sel,i,mode) + if ii then sel[s]=sel[s]+1 end end + end + if mode==1 then table.insert(sel,i+1) end + table.sort(sel) +return sel +end + +function stylechk(subs,sn) + for i=1,#subs do + if subs[i].class=="style" then + local st=subs[i] + if sn==st.name then sr=st break end + end + end + if sr==nil then t_error("Style '"..sn.."' doesn't exist.",1) end + return sr +end + +herpderp=[[ +Regular Fade: type only 'Fade in' / 'Fade out' values. + +Checking 'Alpha/Colour' will use alpha transform instead, with the 'Fade in/out' values and 'accel'. +Checking 'From/To' colours will do colour transforms (with accel). If only one checked, the other will be alpha transform. +'Keep fade along with colour transforms' - when using colour transforms, the \fad tag will be kept as well. + +'Fade across multiple lines' will create a set of alpha transforms across lines. +This can be used if you want to fade out a whole part of a conversation. Like people walking away and talking, sound gets quieter... +This nukes all present alpha tags. It supports shadow alpha (\4a). + +'Global time' will use times relative to video, rather the combined times of lines. This makes a difference with gaps between lines. + + +Letter by Letter + +This fades each letter separately, in a sequence. +The dropdown menu is the fading time for each letter, while Fade in/out are for the overall fades. +So if you have 10 characters and use 120ms/letter and 1200ms Fade in, the fades will follow perfectly one after another. +If the Fade in is 1000 ms, they will overlap a little. If 500ms, you'll have about 3 letters fading at a time. + +'RTL' fades right to left. + +'Delete' removes a letter-by-letter fade (by removing all transforms and alphas). + +'Letter by letter using \ko' uses {\ko#} tags instead of transforms for fade in. +If the Fade in is under 40, it's used as \ko[value]; if it's 40+, it's considered to be the overall fade, i.e. when the last letter appears. +'\ko by word' fades in by word instead of by letter. +(Inline tags are supported, but if you fade by word and have tags in the middle of a word, it won't work as you want it to. +Also, \ko actually works with decimal values, like \ko4.6.) + +'Fade in to current frame' - sets fade in to current video frame. +'Fade out from current frame' - sets fade out from current video frame. +These are for setting fades very easily without requiring any numbers. +The current frame will be the first/last fully visible, so for Fade in, set to the first frame after the fade, not the last frame of the fade. + + +Tags + +This is for transforms from and to given tags. The difference from just using HYDRA for transforms is that this allows you to do it in a "fade" approach for the times, which is convenient mainly for the end transforms. Plus you can combine this with alpha/colour transforms with different settings in one run. (This was previously implemented for blur only, in a different manner.) + +You must input valid transformable tags, with values. Example: "\blur5\fs10\fsp4\fax0.2" + +Tags are used for both In and Out (if those values aren't "0"), but if you check 'Mirror tags', the values for Fade out will be mirrored for frz, fry, fax, and xshad, meaning they will be the opposite of Fade in. This allows for keeping some symmetry. +But you can always run it once for Fade in and once for Fade out, if you want different tags. + +This is activated by the Apply Fade button and can run along with alpha/colour transforms. What determines what runs and what doesn't is whether the in/out times are both zero or not. + + +'[Help]' - shows Help, which you're currently reading. + +'Remember last settings' - the GUI will remember your last used values (until automation reload). + +'Repeat last' - runs with values used last time. + +'[Save config]' - saves your current settings as defaults. + + +Extra functionality: +Fade between 0 and 1 gives you that fraction of the line's duration, so Fade in 0.2 with 1 second is \fad(200,0). +Fade value of 1 is the duration of the whole line. +Negative fade gives you the inverse with respect to duration, so if dur=3000 & fade in is -500, you get \fad(2500,0), i.e. 500 from end. + + + +>Fadeworks< + +This was created for various fbf fade effects that focus more on replicating lines than alpha fade. The idea was to add lines before start time and after end time and do things you can't do with transforms. The main part is shifting position, including using acceleration, so you can create linear movement with accel or all kinds of other moves. + +Each side of the GUI is separate; left for 'fade in', right for 'fade out'. + +'Lines to create' is how many lines will be created before and after the current line. If set to '0', the effect for that side is disabled. + +'Frames per line' is the duration of each created line. + +'Shift each line by' is the timing difference between the lines. +This is generally best to leave the same as the setting above it. With values of '1', you get regular fbf lines. Values of '2' would make consecutive 2-frame lines. If the duration is larger than this setting, lines will overlap (which may be used as a special effect). If this setting is larger than the duration, there will be time gaps between the lines (which probably isn't too useful). + +'X/Y distance' is where the starting/ending point will be relative to current \pos. +If X is -100 and 100 respectively, the line will start 100 pixels to the left of where it is now and end 100 to the right, no matter how many lines you put in between. + +'Acceleration' is separate for each element, as that allows for more effects. +In fact, the accel on the X/Y movement is one of the main points of this. +Due to how this is written, using the same accel on the left and right kind of mirrors it in the result, which is what you'll usually want. +(If left starts slow, right ends slow.) + +'fbf transform' - you can type some tags, like \bord10, and border will be 10 on the outer lines (first of fade in, last of fade out) and transform frame by frame to whatever value you have on the main line. The main line should always remain unchanged. + +'fbf alpha tf' is just an extra field to enter alpha values for the above (in hexadecimal, like "FF") to avoid having to type the whole thing. +The fbf transform field can actually handle even colours, but you have to type them. You can use the 'get colours' tool. +Set a colour, click on 'Get colour tag', and you'll get the tag in the field next to the colour picker. Copypaste from there. +Change \c to whichever colour type you need. +This has its own accel. + +'Fade mode' is pretty weird and hard to explain, but it should be used on lines that have fades. The replicated lines will use those fades, and the idea is to create fading lines that overlap with one another in different positions. +This mode disables 'Frames per line'. +You can 'add transforms' to this, but just like the fades, these will reset on each line, and the effect is kind of bizarre and requires some experimentation to get something useful out of it. +It does work combined with the fbf transforms, but the results may be a bit unpredictable and bad, and they will look different for fade in and fade out. + +If you find an effect you want to keep for later, there's a mini GUI for saving the settings. +You can Save, Load, and Delete presets.]] + +function fadehelp(subs,sel) + Pr=aegisub.dialog.display({{width=50,height=20,class="textbox",value=herpderp}},{"OK","Back"},{close='OK'}) + if Pr=="Back" then fadeconfig(subs,sel) end +end + +-- FW SAFE -- +function fwsafe() + FWS={} + fwconf=ADP("?user").."\\fadeworksafe.conf" + file=io.open(fwconf) + -- read saved + if file~=nil then + FWsaved=file:read("*all") + file:close() + for f in FWsaved:gmatch("FWS: ([^\n]+)") do table.insert(FWS,f) end + end + vault={ + {x=0,y=0,class="label",label="Save as:"}, + {x=1,y=0,class="edit",name="fwsave"}, + {x=0,y=1,class="label",label="Load/Delete:"}, + {x=1,y=1,class="dropdown",name="fwload",items=FWS,value=FWS[1]}, + {x=0,y=2,width=2,class="label",label="This is a GUI within a GUI within a GUI............................"}, + } + + repeat + -- DELETE + if Pf=="Expunge" and rs.fwload~="" then + delName=rs.fwload + file=io.open(fwconf) + FWsaved=file:read("*all") + file:close() + -- del from text + Wmod=FWsaved:gsub("FWS: "..esc(delName)..".-"..esc(delName).." END\n\n","") + -- resave file + file=io.open(fwconf,"w") + file:write(Wmod) + file:close() + -- remove from list + for i=1,#FWS do + if FWS[i]==delName then table.remove(FWS,i) break end + end + for key,val in ipairs(vault) do + if val.name=="fwload" then val.value=FWS[1] end + end + t_error("'"..delName.."' deleted.") + end + if Pf=="Expunge" and rs.fwload=="" then t_error("Nothing to delete") end + Pf,rs=ADD(vault,{"Inhume","Exhume","Expunge","Vanish"},{close='Vanish'}) + until Pf~="Expunge" + + -- SAVE + if Pf=="Inhume" then + FWN=rs.fwsave + saveOK=1 + -- name check + if FWN=='' then t_error("No name given") saveOK=nil end + for i=1,#FWS do + if FWN==FWS[i] then t_error("Name '"..FWN.."' already exists") saveOK=nil break end + end + -- save + if saveOK then + WorkS='FWS: '..FWN..'\n' + for key,val in ipairs(fadegui) do + if val.class~="label" then WorkS=WorkS..val.name..":"..tf(rez[val.name]).."\n" end + end + WorkS=WorkS..FWN..' END\n\n' + if file==nil then file=io.open(fwconf,"w") else file=io.open(fwconf,"a") end + file:write(WorkS) + file:close() + t_error("Saved as '"..FWN.."'") + end + end + -- LOAD + if Pf=="Exhume" then + toload=rs.fwload + if file==nil then t_error("Nothing to load") + elseif toload=='' then t_error("No name given") + else + fwloaded=FWsaved:match("FWS: "..esc(toload).."\n(.-\n)"..esc(toload).." END") + for key,val in ipairs(fadegui) do + if val.class~="label" then + if fwloaded:match(val.name) then val.value=detf(fwloaded:match(val.name..":(.-)\n")) end + end + end + end + end +end + +-- Config -- +function saveconfig() +fadconf="Fade config\n\n" + for key,val in ipairs(GUI) do + if val.class:match"edit" or val.class=="dropdown" then + fadconf=fadconf..val.name..":"..res[val.name].."\n" + end + if val.class=="checkbox" and val.name~="save" and val.name~="del" then + fadconf=fadconf..val.name..":"..tf(res[val.name]).."\n" + end + end +fadconfig=ADP("?user").."\\apply_fade.conf" +file=io.open(fadconfig,"w") +file:write(fadconf) +file:close() +ADD({{class="label",label="Config saved to:\n"..fadconfig}},{"OK"},{close='OK'}) +end + +function loadconfig() +fconfig=ADP("?user").."\\apply_fade.conf" +file=io.open(fconfig) + if file~=nil then + konf=file:read("*all") + io.close(file) + for key,val in ipairs(GUI) do + if val.class:match"edit" or val.class=="checkbox" or val.class=="dropdown" then + if konf:match(val.name) then val.value=detf(konf:match(val.name..":(.-)\n")) end + end + end + end +end + +function tf(val) + if val==true then ret="true" + elseif val==false then ret="false" + else ret=val end + return ret +end + +function detf(txt) + if txt=="true" then ret=true + elseif txt=="false" then ret=false + else ret=txt end + return ret +end + +function apply_fade(subs,sel) + ADD=aegisub.dialog.display + ADP=aegisub.decode_path + ak=aegisub.cancel + STAG="^{\\[^}]-}" + sel=fadeconfig(subs,sel) + aegisub.set_undo_point(script_name) + return sel +end + +if haveDepCtrl then + depRec:registerMacros({ + {script_name,script_description,apply_fade}, + {": HELP : / FadeWorkS","FadeWorkS",fadehelp}, + },false) +else + aegisub.register_macro(script_name,script_description,apply_fade) + aegisub.register_macro(": HELP : / FadeWorkS","FadeWorkS",fadehelp) +end \ No newline at end of file diff --git a/automation/autoload/ua.HYDRA.lua b/automation/autoload/ua.HYDRA.lua new file mode 100644 index 000000000..029a51559 --- /dev/null +++ b/automation/autoload/ua.HYDRA.lua @@ -0,0 +1,2064 @@ +-- Manual: http://unanimated.hostfree.pw/ts/scripts-manuals.htm#hydra + +script_name="HYDRA" +script_description="A multi-headed typesetting beast. Scary as Hell." +script_author="unanimated" +script_url1="http://unanimated.hostfree.pw/ts/hydra.lua" +script_url2="https://raw.githubusercontent.com/unanimated/luaegisub/master/hydra.lua" +script_version="6.1" +script_namespace="ua.HYDRA" + +local haveDepCtrl,DependencyControl,depRec=pcall(require,"l0.DependencyControl") +if haveDepCtrl then + script_version="6.1.0" + depRec=DependencyControl{feed="https://raw.githubusercontent.com/unanimated/luaegisub/master/DependencyControl.json"} +end + +re=require'aegisub.re' +clipboard=require'aegisub.clipboard' + +order="\\r\\fad\\fade\\an\\q\\blur\\be\\bord\\xbord\\ybord\\shad\\xshad\\yshad\\fn\\fs\\fsp\\fscx\\fscy\\frx\\fry\\frz\\fax\\fay\\c\\2c\\3c\\4c\\alpha\\1a\\2a\\3a\\4a\\pos\\move\\org\\clip\\iclip\\b\\i\\u\\s\\p" +noneg2="|bord|shad|xbord|ybord|fs|blur|be|fscx|fscy|" + +-- HYDRA HEAD 9 -- +function hh9(subs,sel) + -- get colours + tags from input + getcolours() + if res.italix or res.bolt or res.under or res.strike then styleget(subs) end + local shft=res.int or 0 + tags="" + tags=gettags(tags) + transform=tags + ortags=tags + ortrans=transform + retags=tags:gsub("\\","\\\\") + z0=-1 + + -- tag position + pl1,pl2,pl3=nil,nil,nil + place=res.linetext or "" + if place=="*" then t_error("You cannot have only \"*\" in Tag position.",1) end + if place:match("*") then pl1,pl2,pl3=place:match("(.*)(%*)(.*)") pla=1 else pla=0 end + if res.tagpres~="--- presets ---" and res.tagpres~=nil then pla=1 end + + for z,i in ipairs(sel) do + progress("Hydralizing line: "..z.."/"..#sel) + prog=math.floor((z+0.5)/#sel*100) aegisub.progress.set(prog) + line=subs[i] + text=line.text + visible=nobrea(text) + linecheck() + + if not text:match("^{\\") then text="{\\HYDRA}"..text end + if visible:match("*") and res.tagpres=="--- presets ---" then pla=0 end + + -- transforms + if trans==1 and GO then z0=z0+1 + tin=res.trin or 0 + tout=res.trout or 0 + if res.tend then + tin=line.end_time-line.start_time-res.trin + tout=line.end_time-line.start_time-res.trout + end + + if tmode==1 then + if res.int~=0 then TF=shft*z0 else TF=0 end + tnorm="\\t("..tin+TF..","..tout+TF..","..res.accel..",\\alltagsgohere)}" + if place:match("*") and pla==1 then + initags=text:match("^{\\[^}]-}") or "" + orig=text + replace=place:gsub("%*","{"..tnorm) + v1=nobra(text) + v2=nobra(replace) + if v1==v2 then text=initags..textmod(orig,replace) end + else + text=text:gsub("^({\\[^}]*)}","%1"..tnorm) + end + end + if tmode==2 then text=text:gsub("^(.-\\t%b())",function(t) return t:gsub("%)$","\\alltagsgohere)") end) end + if tmode==3 then text=text:gsub("(\\t%b())",function(t) return t:gsub("%)$","\\alltagsgohere)") end) end + if tmode==4 then + if res.int~=0 then + tagtab={} + for tt in text:gmatch(".-{\\[^}]*}") do table.insert(tagtab,tt) end + END=text:match("^.*{\\[^}]*}(.-)$") + for t=1,#tagtab do sf=t-1 + tagtab[t]=tagtab[t]:gsub("({\\[^}]*)}","%1\\t("..tin+shft*sf..","..tout+shft*sf..","..res.accel..",\\alltagsgohere)}") + end + nt=END + for a=#tagtab,1,-1 do nt=tagtab[a]..nt end + text=nt + else text=text:gsub("({\\[^}]*)}","%1\\t("..tin..","..tout..","..res.accel..",\\alltagsgohere)}") + end + end + + if res.add and res.add~=0 then transform=addbyline(transform,ortrans) end + + if tmode<4 and res.relative then + stags=text:match(STAG) or "" + for tag,val in transform:gmatch("(\\%a+)([%d%.%-]+)") do + if stags:match(tag) then oldval=stags:match(tag.."([%d%.%-]+)") + transform=transform:gsub(tag..esc(val),tag..oldval+val) + end + end + end + text=text:gsub("\\alltagsgohere",transform) + if tmode==4 and res.relative then + text=text:gsub(ATAG,function(tg) + for tag,val in transform:gmatch("(\\%a+)([%d%.%-]+)") do + if tg:match(tag) then oldval=tg:match(tag.."([%d%.%-]+)") + transform2=transform:gsub(tag..esc(val),tag..oldval+val) + tg=tg:gsub("(.*\\t.-)"..transform,"%1"..transform2) + end + end + return tg end) + end + text=text:gsub("\\t%(0,0,1,","\\t(") + :gsub("\\t(%b())",function(tr) return "\\t"..duplikill(tr) end) + :gsub(ATAG,function(tg) return cleantr(tg) end) + + -- non transform, ie. the regular stuff + elseif GO then z0=z0+1 + -- temporarily block transforms + text=text:gsub(ATAG,function(tg) return duplikill(tg) end) + :gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end) + + if res.add and res.add~=0 then tags=addbyline(tags,ortags) end + + if pla==1 and z==1 then + if res.strike then tags=tags.."\\s1" end + if res.under then tags=tags.."\\u1" end + if res.bolt then tags=tags.."\\b1" end + if res.italix then tags=tags.."\\i1" end + end + if tags~="" then + drawing=text:match("\\p1") + if drawing then fail("Some lines contain drawings.") end + if visible=="" then fail("No visible text.") end + if pla==1 and not drawing and visible~="" then + initags=text:match(STAG) or "" + text=text:gsub("(\\[Nh])","{%1}") + orig=text + v1=nobra(orig) + -- BEFORE LAST CHARACTER + if res.tagpres=="before last char." then + com_dump='' + repeat + end_com=text:match("(%b{})$") + if end_com then + com_dump=end_com..com_dump + text=text:gsub("%b{}$","") + else break end + until not end_com + text=re.sub(text,"(.)$","{§}\\1") + text=text:gsub("{§}",wrap(tags))..com_dump + -- SOMEWHERE IN THE MIDDLE + elseif res.tagpres=="in the middle" or res.tagpres:match("of text") then + clean=nobrea1(text) + char=re.find(clean,'.') + lngth=math.floor(#char*fak) + text="{·}"..text + text=text:gsub("{·}({\\[^}]-})","%1{·}") + m=0 + if lngth>0 then + repeat text=text:gsub("{·}(%b{})","%1{·}") text=re.sub(text,"{·}([^{])","\\1{·}") m=m+1 until m==lngth + end + text=text:gsub("{(·)}",wrap(tags)) :gsub("({"..esc(tags).."})(%b{})","%2%1") + -- PATTERN + elseif res.tagpres=="custom pattern" then + if place=="" then t_error("Custom pattern preset: No text given in Tag position.",1) end + if not pl1 then t_error("Asterisk missing in Tag position. ("..place..")",1) end + pl1=esc(pl1) pl3=esc(pl3) + text=nobrea(text):gsub(pl1..pl3,pl1.."{"..tags.."}"..pl3) + text=initags..retextmod(orig,text) + if text==orig then fail("Pattern '"..place.."' not found.") end + -- SECTION + elseif res.tagpres=="section" then + if place=="" then t_error("Section preset: No text given in Tag position.",1) end + tgs2="" + for tg in tags:gmatch("\\%d?%a+") do + txt1=text:match("^%b{}.-"..esc(place)) or text:match("^.-"..esc(place)) or "" + local tg2=txt1:match("^.*("..tg.."[^\\}]+).-$") or tg + if tg=='\\fs' then tg2=txt1:match("^.*(\\fs%d+).-$") or tg end + tg2=tg2:gsub("(\\[ibus])%d","%10") + tgs2=tgs2..tg2 + end + text=nobrea(text):gsub("^(.-)("..esc(place).."%s*)(.*)$","%1{"..tags.."}%2{"..tgs2.."}%3") + text=initags..retextmod(orig,text) + if text==orig then fail("Pattern '"..place.."' not found.") end + -- CHARACTER + elseif res.tagpres=="every char." then + replace=re.sub(nobra(text),"(.)","{"..retags.."}\\1") + v2=nobra(replace) + if visible=="" then fail("No visible text.") + elseif v1==v2 then text=initags..retextmod(orig,replace) end + text=text:gsub("({\\HYDRA})%1","%1") + -- WORD + elseif res.tagpres=="every word" then + replace=nobra(text):gsub("%S+","{"..tags.."}%1"):gsub("(%b{})\\N","\\N%1") + v2=nobra(replace) + if v1==v2 then text=initags..retextmod(orig,replace) end + text=text:gsub("({\\HYDRA})%1","%1") + -- TEXT POSITION + elseif res.tagpres=="text position" then + v2=nobra(text) + pmax=re.find(v2,".") or {} + krktrz=#pmax + pos=tonumber(place:match("^%-?%d+")) or 0 + addpos=tonumber(place:match(".([%+%-]%d+)")) or 0 + if pos<0 then pos=krktrz+pos end + split=pos+addpos*z0 + if split<0 then split=0 end + if split1 then IN=0 end + if IN==0 and OUT==0 then fGO=0 end + end + text=text:gsub("\\fad%([%d%.%,]-%)","") + if fGO==1 then text=text:gsub("^{\\","{\\fad("..IN..","..OUT..")\\") end + end + -- \q2 + if res.q2 then + if text:match("\\q2") then text=text:gsub("\\q2","") else text=text:gsub("^{\\","{\\q2\\") end + end + -- \an + if res.an1 then + if text:match("\\an%d") then text=text:gsub("\\an(%d)","\\an"..res.an2) else text=text:gsub("^{\\","{\\an"..res.an2.."\\") end + end + + if text==line.text then fail("Most likely tags already present.") end + end + + -- unblock transforms + text=text:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) + end + -- the end + + text=text:gsub("\\HYDRA","") :gsub("\\t%([^\\%)]-%)","") :gsub("{}","") + if line.text~=text then success=success+1 end + line.text=text + subs[i]=line + end + summary() +end + +function bolts(text,ttype,srtype) + if text:match("^{[^}]-"..ttype.."[01]") then text=text:gsub(ttype.."([01])",function(a) return ttype..(1-a) end) + else + local t_val=text:match(ttype.."([01])") + if not t_val then + if stylechk(sty)[srtype] then t_val="0" else t_val="1" end + end + text=text:gsub(ttype.."([01])",function(a) return ttype..(1-a) end) :gsub("^({\\[^}]*)}","%1"..ttype..t_val.."}") + end + return text +end + +function vis_replace(t,r1,r2) + local nt='' + repeat + seg,t2=t:match("^(%b{})(.*)") --tags/comms + if not seg then seg,t2=t:match("^([^{]+)(.*)") --text + if not seg then break end + seg=seg:gsub(r1,r2) + end + nt=nt..seg + t=t2 + until t=='' + return nt +end + +function getcolours() +col={} alfalfa={} + for c=1,4 do + local colur=res["c"..c]:gsub("#(%x%x)(%x%x)(%x%x).*","&H%3%2%1&") + table.insert(col,colur) + if res.alfas then + local alpa=res["c"..c]:match("#%x%x%x%x%x%x(%x%x)") + if alpa then + table.insert(alfalfa,alpa) + if res["k"..c] then res["arf"..c]=true res["alph"..c]=alfalfa[c] end + end + end + if res.aonly then res["k"..c]=false end + end +end + +function gettags(tags) + if res.reuse then + if lastags then + if res.show then showtags(lastags) end + return lastags + else t_error("No tags to reuse",1) end + end + if res.fsc1 then res.fscx1=true res.fscy1=true res.fscx2=res.fsc2 res.fscy2=res.fsc2 end + if res["blur1"] then tags=tags.."\\blur"..res["blur2"] end + if res["be1"] then tags=tags.."\\be"..res["be2"] end + if res["bord1"] then tags=tags.."\\bord"..res["bord2"] end + if res["shad1"] then tags=tags.."\\shad"..res["shad2"] end + if res["fs1"] then tags=tags.."\\fs"..res["fs2"] end + if res["spac1"] then tags=tags.."\\fsp"..res["spac2"] end + if res["fscx1"] then tags=tags.."\\fscx"..res["fscx2"] end + if res["fscy1"] then tags=tags.."\\fscy"..res["fscy2"] end + if res["xbord1"] then tags=tags.."\\xbord"..res["xbord2"] end + if res["ybord1"] then tags=tags.."\\ybord"..res["ybord2"] end + if res["xshad1"] then tags=tags.."\\xshad"..res["xshad2"] end + if res["yshad1"] then tags=tags.."\\yshad"..res["yshad2"] end + if res["frz1"] then tags=tags.."\\frz"..res["frz2"] end + if res["frx1"] then tags=tags.."\\frx"..res["frx2"] end + if res["fry1"] then tags=tags.."\\fry"..res["fry2"] end + if res["fax1"] then tags=tags.."\\fax"..res["fax2"] end + if res["fay1"] then tags=tags.."\\fay"..res["fay2"] end + if res["k1"] then tags=tags.."\\c"..col[1] end + if res["k2"] then tags=tags.."\\2c"..col[2] end + if res["k3"] then tags=tags.."\\3c"..col[3] end + if res["k4"] then tags=tags.."\\4c"..col[4] end + if res["arfa"] then tags=tags.."\\alpha&H"..res["alpha"].."&" end + if res["arf1"] then tags=tags.."\\1a&H"..res["alph1"].."&" end + if res["arf2"] then tags=tags.."\\2a&H"..res["alph2"].."&" end + if res["arf3"] then tags=tags.."\\3a&H"..res["alph3"].."&" end + if res["arf4"] then tags=tags.."\\4a&H"..res["alph4"].."&" end + lastags=tags + if res.show then showtags(tags) end + if res["moretags"] and res["moretags"]~="\\" then tags=tags..res["moretags"] end + return tags +end + +function addbyline(tags,ortags) + tags=ortags:gsub("\\(%a%a+)([%d%.%-]+)",function(t,v) + if t~="an" and t~="fn" then + local nv=round(v+res.add*z0,2) + if nv<0 and noneg2:match("|"..t.."|") then nv=0 end + return "\\"..t..nv + else return "\\"..t..v end + end) + return tags +end + +function linecheck() + lay=line.layer sty=line.style act=line.actor eff=line.effect + if not res.appltx then res.appltx="" end + GO=nil local lGO,sGO,aGO,eGO,tGO + if res.applay=="All Layers" or not res.exc and tonumber(res.applay)==lay or res.exc and tonumber(res.applay)~=lay then lGO=true end + if res.applst=="All Styles" or not res.exc and res.applst==sty or res.exc and res.applst~=sty then sGO=true end + if res.applac=="All Actors" or not res.exc and res.applac==act or res.exc and res.applac~=act then aGO=true end + if res.applef=="All Effects" or not res.exc and res.applef==eff or res.exc and res.applef~=eff then eGO=true end + if res.appltx=="Text..." or res.appltx=="" or line.text:match(esc(res.appltx)) then tGO=true end + if lGO and sGO and aGO and eGO and tGO then GO=true end + if loaded<3 then GO=true end + if not GO then fail("Some 'Apply to' restrictions set.") end +end + +-- GRADIENTS -- +function hydradient(subs,sel) + local GT=res.gtype:match("^....") + local strip=res.stripe + local acc=res.accel + styleget(subs) + getcolours() + tags="" + tags=gettags(tags) + if tags=="" then ak() end + ortags=tags + local gcpos=res.linetext gcl=nil + if res.middle and gcpos:match("*") then gc1,gc2=gcpos:match("^(.-)%*(.-)$") gcl=gc1:len() end + local GBCn=tonumber(gcpos:match("^%d$")) or 1 + text1=subs[sel[1]].text + tags_1=text1:match(STAG) or "" + tags_1=detra(tags_1) + if GT=="by l" then GBL=0 z1=0 + for z,i in ipairs(sel) do line=subs[i] linecheck() if GO then GBL=GBL+1 end end + -- GBL: values from last line + if res.last then + local l=subs[sel[#sel]] + local st=l.text:match(STAG) or "" + for tg in tags:gmatch("\\[^\\]+") do + local tag=tg:match '\\%d?%a+' + local tv=st:match(tag.."([^\\})]+)") + if tv then tg2=tag..tv tags=tags:gsub(esc(tg),tg2) end + end + end + table.sort(sel,function(a,b) return a>b end) + end + local bra={} + for z=#sel,1,-1 do + i=sel[z] + progress("Gradienting line #"..i-line0.." ["..#sel+1-z.."/"..#sel.."]") + line=subs[i] + text=line.text + orig=text + visible=nobrea(text) + text=text:gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end) :gsub("\\1c","\\c") + initags=text:match(STAG) or "" + sr=stylechk(line.style) + linecheck() + + -- hori/vert + if GO and GT:match("r") then + x1,y1,x2,y2=initags:match("clip%(([%d%.%-]+),([%d%.%-]+),([%d%.%-]+),([%d%.%-]+)") + if not x1 then + local note='' + if #sel>1 then note='\n(Note: Lines are processed from last to first.)' end + t_error(res.gtype.." gradient:\nMissing rectangular clip on line #"..i-line0..note.."\nAborting.",1) + end + x1=math.floor(x1) y1=math.floor(y1) x2=math.ceil(x2) y2=math.ceil(y2) local c_s + if GT=="vert" then total=math.ceil((y2-y1)/strip) c_s=y2-y1 else total=math.ceil((x2-x1)/strip) c_s=x2-x1 end + if total<2 then t_error("Error on line #"..i-line0..".\nThis won't create any gradient.\nDecrease the pxl/stripe setting.\nStripe: "..strip.."; Clip size: "..c_s.."\nAborting.",1) end + if not initags:match("\\pos") and not initags:match("\\move") then initags=getpos(subs,initags) end + + for l=1,total do + LN=l count=total + half=math.ceil(total/2) + if res.middle then count=half + if LN>half then LN=total-LN+1 end + end + stags=initags + text2=text + for tg,V2 in tags:gmatch("(\\%d?%a+)([^\\]+)") do + if tg:match("fr") and res.short then V2=shortrot(V2) end + V1=initags:match("^{[^}]-"..tg.."([%d%-&][^\\}]*)") or tag2style(tg,sr) + if tg:match("fr") and res.short then V1=shortrot(V1) end + if tg:match("\\[fbsxy]") then VC=numgrad(V1,V2,count,LN,acc) end + if tg:match("\\%d?a") then VC=agrad(V1,V2,count,LN,acc) end + if tg:match("\\%d?c") then + if res.hsl then VC=acgradhsl(V1,V2,count,LN,acc) else VC=acgrad(V1,V2,count,LN,acc) end + end + stags=addtag3(tg..VC,stags) + stags=stags:gsub("clip%(([%d%.%-]+),([%d%.%-]+),([%d%.%-]+),([%d%.%-]+)",function(a,b,c,d) + if GT=="vert" then b=y1+(l-1)*strip d=b+strip a=x1 c=x2 end + if GT=="hori" then a=x1+(l-1)*strip c=a+strip b=y1 d=y2 end + return "clip("..a..","..b..","..c..","..d end) + text2=text2:gsub("(.)("..ATAG..")",function(a,tblok) + V1i=tblok:match("^{[^}]-"..tg.."([%d%-&][^\\}]*)") + if V1i and tg:match("\\[fbsxy]") then VC=numgrad(V1i,V2,count,LN,acc) end + if V1i and tg:match("\\%d?a") then VC=agrad(V1i,V2,count,LN,acc) end + if V1i and tg:match("\\%d?c") then + if res.hsl then VC=acgradhsl(V1i,V2,count,LN,acc) else VC=acgrad(V1i,V2,count,LN,acc) end + end + tblok=addtag3(tg..VC,tblok) + return a..tblok end) + end + l2=line + l2.text=text2:gsub(STAG,stags) :gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) + if l==1 then text=l2.text + else subs.insert(i+l-1,l2) end + end + if z<#sel then for s=z+1,#sel do sel[s]=sel[s]+total-1 end end + for s=1,total-1 do table.insert(sel,i+s) end + end + + -- by character + letrz0=re.find(visible,".") or {} + if GT=="by c" and text:match '\\p1' then GO=nil fail("Some lines contain drawings.") end + if GT=="by c" and GBCn>#letrz0 then GO=nil end + if GO and GT=="by c" and #letrz0>1 then + orig=orig:gsub("(\\[Nh])","{%1}") + if text:match "{[^}]*{" or text:match "}[^{]*}" or text:match "^[^{]*}" or text:match "{[^}]*$" then brackets=true table.insert(bra,1,i-line0) end + re_check=0 + repeat + LTR={} + TAG={} + letrz=re.find(visible,".{"..GBCn.."}") + rest=re.sub(visible,".{"..GBCn.."}","") + for l=1,#letrz do + table.insert(LTR,letrz[l].str) + table.insert(TAG,"") + end + if rest~="" then table.insert(LTR,rest) table.insert(TAG,"") end + for tg,V2 in tags:gmatch("(\\%d?%a+)([^\\]+)") do + if tg:match("fr") and res.short then V2=shortrot(V2) end + V1=text:match("^{[^}]-"..tg.."([%d%-&][^\\}]*)") or tag2style(tg,sr) + if tg:match("fr") and res.short then V1=shortrot(V1) end + initags=addtag3(tg..V1,initags) + for l=2,#LTR do + LN=l count=#LTR + half=math.ceil(#LTR/2) + if gcl and gc1..gc2==visible then + if l<=gcl then count=gcl else count=#LTR-gcl LN=#LTR-l+1 end + elseif res.middle then count=half + if LN>half then LN=#LTR-LN+1 end + end + if tg:match("\\[fbsxy]") then VC=numgrad(V1,V2,count,LN,acc) end + if tg:match("\\%d?a") then VC=agrad(V1,V2,count,LN,acc) end + if tg:match("\\%d?c") then + if res.hsl then VC=acgradhsl(V1,V2,count,LN,acc) else VC=acgrad(V1,V2,count,LN,acc) end + end + TAG[l]=TAG[l]..tg..VC + end + end + nt=LTR[1] + for l=2,#LTR do nt=nt.."{"..TAG[l].."}"..LTR[l] end + text=initags..textmod(orig,nt) + text=text:gsub(ATAG,function(tg) return duplikill(tg) end) + repeat text,r=text:gsub("{([^}]-)(\\[Nh])([^}]-)}","%2{%1%3}") until r==0 + text=text:gsub("{}","") + visible2=nobrea(text) + if visible~=visible2 then re_check=re_check+1 end + until visible==visible2 or re_check>=100 + end + + -- by line + if GO and GT=="by l" then z1=z1+1 + LN=z1 total=GBL count=GBL + half=math.ceil(total/2) + if res.middle then count=half + if LN>half then LN=total-LN+1 end + end + stags=initags + for tg,V2 in tags:gmatch("(\\%d?%a+)([^\\]+)") do + if tg:match("fr") and res.short then V2=shortrot(V2) end + V1=tags_1:match("^{[^}]-"..tg.."([%d%-&][^\\}]*)") or tag2style(tg,sr) + if tg:match("fr") and res.short then V1=shortrot(V1) end + if tg:match("\\[fbsxy]") then VC=numgrad(V1,V2,count,LN,acc) end + if tg:match("\\%d?a") then VC=agrad(V1,V2,count,LN,acc) end + if tg:match("\\%d?c") then + if res.hsl then VC=acgradhsl(V1,V2,count,LN,acc) else VC=acgrad(V1,V2,count,LN,acc) end + end + stags=addtag3(tg..VC,stags) + end + text=text:gsub(STAG,stags) :gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) + if line.text==text then fail("Target values probably already present.") end + end + + text=text:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) + visible2=nobrea(text) + txt_check(visible,visible2,i) + if line.text~=text and visible==visible2 then success=success+1 end + line.text=text + subs[i]=line + end + if brackets then + local l='' + for k,v in ipairs(bra) do l=l..v..', ' end + l=l:gsub(', $','') + t_error("Some lines contain wrong sets of curly brackets. \nThis probably won't go well with gradients. \nLines # "..l) + brackets=nil + end + summary() + return sel +end + +-- SPECIAL FUNCTIONS -- +function special(subs,sel) + SF=res.spec + if res.spec=="back and forth transform" and res.int==0 then + BAFT={{class="label",label="Interval for back and forth transform missing.\nGive milliseconds."}, + {y=1,name="int2",class="intedit",min=0}} + pres,rez=ADD(BAFT,{"OK","Cancel"},{ok='OK',close='Cancel'}) + if pres=="Cancel" or rez.int2==0 then ak() end + res.int=rez.int2 + end + if res.spec:match"transform" or res.spec:match"strikeout" then + getcolours() + transphorm="" + transphorm=gettags(transphorm) + end + if res.spec=="split line in 3 parts" then + if res.trin==0 and res.trout==0 then t_error("No times given to split lines by.\nUse Transform t1 && t2 fields.",1) end + nsel={} for z,i in ipairs(sel) do table.insert(nsel,i) end + end + styleget(subs) + if res.spec=="select overlaps" then sel=selover(subs) + else + for z=#sel,1,-1 do + i=sel[z] + progress(res.spec..": "..#sel-z.."/"..#sel) + prog=math.floor((#sel-z+0.5)/#sel*100) + aegisub.progress.set(prog) + line=subs[i] + text=line.text + layer=line.layer + linecheck() + if GO then res.spec=SF else res.spec="nope" end + text=text:gsub("\\1c","\\c") + + if res.spec=="fscx -> fscy" then text=text:gsub("\\fscy[%d%.]+",""):gsub("\\fscx([%d%.]+)","\\fscx%1\\fscy%1") end + if res.spec=="fscy -> fscx" then text=text:gsub("\\fscx[%d%.]+",""):gsub("\\fscy([%d%.]+)","\\fscx%1\\fscy%1") end + if res.spec=="shad -> xshad+yshad" then + text=text:gsub("\\shad([%d%.]+)","\\xshad%1\\yshad%1"):gsub(ATAG,function(tg) return duplikill(tg) end) + if text==line.text then fail("No \\shad tag.") end + end + + if res.spec=="move colour tag to first block" then + tags=text:match(STAG) or "" + text=text:gsub(STAG,"") + klrs="" + for klr in text:gmatch("\\[1234]?c&H%x+&") do klrs=klrs..klr end + text=text:gsub("(\\[1234]?c&H%x+&)","") :gsub("{}","") + text=tags.."{"..klrs.."}"..text + text=tagmerge(text):gsub("{}","") + :gsub(ATAG,function(tg) return duplikill(tg) end) + end + + if res.spec=="convert clip <-> iclip" then + text=text:gsub("\\(i?)clip",function(k) if k=="" then return "\\iclip" else return "\\clip" end end) + if text==line.text then fail("No (i)clip found.") end + end + + -- CLEAN UP TAGS + if res.spec=="clean up tags" then + text=text:gsub("{\\\\k0}",""):gsub(">\\","\\"):gsub("{(\\[^}]-)} *\\N *{(\\[^}]-)}","\\N{%1%2}") + text=tagmerge(text) + text=text:gsub("({\\[^}]-){(\\[^}]-})","%1%2"):gsub("{.-\\r","{\\r"):gsub("^{\\r([\\}])","{%1") + text=text:gsub("\\fad%(0,0%)",""):gsub(ATAG.."$",""):gsub("^({\\[^}]-)\\frx0\\fry0","%1") + text=text:gsub(ATAG,function(tgs) + tgs2=tgs + :gsub("\\+([\\}])","%1") + :gsub("(\\[^\\})]+)",function(a) if not a:match'clip' and not a:match'\\fn' and not a:match'\\r' then a=a:gsub(' ','') end return a end) + :gsub("(\\%a+)([%d%-]+%.%d+)",function(a,b) if not a:match("\\fn") then b=round(b,2) end return a..b end) + :gsub("(\\%a+)%(([%d%.%-]+),([%d%.%-]+)%)",function(a,b,c) b=round(b,2) c=round(c,2) return a.."("..b..","..c..")" end) + :gsub("(\\%a+)%(([%d%.%-]+),([%d%.%-]+),([%d%.%-]+),([%d%.%-]+)",function(a,b,c,d,e) + b=round(b,2) c=round(c,2) d=round(d,2) e=round(e,2) return a.."("..b..","..c..","..d..","..e end) + tgs2=duplikill(tgs2) + tgs2=extrakill(tgs2) + tgs2=cleantr(tgs2) + return tgs2 + end) + end + + -- SORT TAGS + if res.spec=="sort tags in set order" then + text=text:gsub("\\a6","\\an8") :gsub("\\1c","\\c") + -- run for each set of tags + for tags in text:gmatch(ATAG) do + orig=tags + tags=tags:gsub("{.-\\r","{\\r") + -- save & nuke transforms + trnsfrm="" + for t in tags:gmatch("\\t%b()") do trnsfrm=trnsfrm..t end + tags=tags:gsub("\\t%b()","") + ord="" + -- go through tags, save them in order, and delete from tags + for tg in order:gmatch("\\[%a%d]+") do + tag=tags:match("("..tg.."[^\\}]-)[\\}]") + if tg=="\\fs" then tag=tags:match("(\\fs%d[^\\}]-)[\\}]") end + if tg=="\\fad" then tag=tags:match("(\\fad%([^\\}]-)[\\}]") end + if tg=="\\c" then tag=tags:match("(\\c&[^\\}]-)[\\}]") end + if tg=="\\i" then tag=tags:match("(\\i[^%a\\}]-)[\\}]") end + if tg=="\\s" then tag=tags:match("(\\s[^%a\\}]-)[\\}]") end + if tg=="\\p" then tag=tags:match("(\\p[^%a\\}]-)[\\}]") end + if tag then ord=ord..tag etag=esc(tag) tags=tags:gsub(etag,"") end + end + -- attach whatever got left + if tags~="{}" then ord=ord..tags:match("{(.-)}") end + ordered="{"..ord..trnsfrm.."}" + text=text:gsub(esc(orig),ordered) + end + end + + -- CLIP TO DRAWING + if res.spec=="convert clip to drawing" and text:match("\\i?clip%(m [%d%a%s%-]+") then + text=text:gsub("\\(i?)clip%(([%d%.%-]+),([%d%.%-]+),([%d%.%-]+),([%d%.%-]+)%)",function(ii,a,b,c,d) + return string.format("\\"..ii.."clip(m %d %d l %d %d %d %d %d %d)",round(a),round(b),round(c),round(b),round(c),round(d),round(a),round(d)) end) -- rect.2vector + text=text:gsub("^({\\[^}]-}).*","%1") + text=text:gsub("^({[^}]*)\\i?clip%(m(.-)%)([^}]*)}","%1%3\\p1}m%2") + if text:match("\\pos") or text:match("\\move") then + ctext=text:match("}m ([%d%a%s%-]+)") + local xx,yy=text:match("\\pos%(([%d%.%-]+),([%d%.%-]+)") + if not xx then xx,yy=text:match("\\move%(([%d%.%-]+),([%d%.%-]+)") end + xx=round(xx) yy=round(yy) + ctext2=ctext:gsub("([%d%-]+)%s([%d%-]+)",function(a,b) return a-xx.." "..b-yy end) + ctext=ctext:gsub("%-","%%-") + text=text:gsub(ctext,ctext2) + end + if not text:match("\\pos") and not text:match("\\move") then text=text:gsub("^{","{\\pos(0,0)") end + text=text:gsub("\\fsc[xy][%d%.]+",""):gsub("\\f[ar][xyz][^\\}]*",""):gsub("\\p1","\\fscx100\\fscy100%1"):gsub("\\an%d",""):gsub("^{","{\\an7") + end + + -- DRAWING TO CLIP + if res.spec=="convert drawing to clip" and text:match("\\p1") then + draw=text:match("}m ([^{]+)") + rota=text:match("^{[^}]-\\frz([-%d.]+)") + if rota then sr=stylechk(line.style) text=frz_redraw(text,rota,draw,sr) end + text=text:gsub("^({[^}]*)\\p1([^}]-})(m [^{]*)","%1\\clip(%3)%2") + scx=text:match("\\fscx([%d%.]+)") or 100 + scy=text:match("\\fscy([%d%.]+)") or 100 + if text:match("\\pos") or text:match("\\move") then + local xx,yy=text:match("\\pos%(([%d%.%-]+),([%d%.%-]+)") + if not xx then xx,yy=text:match("\\move%(([%d%.%-]+),([%d%.%-]+)") end + xx=round(xx) yy=round(yy) + ctext=text:match("\\clip%(m ([^%)]+)%)") + ctext2=ctext:gsub("([%d%-]+) ([%d%-]+)",function(a,b) return round(a*scx/100+xx).." "..round(b*scy/100+yy) end) + text=text:gsub(esc(ctext),ctext2) + end + if not text:match("\\pos") and not text:match("\\move") then text=text:gsub("^{","{\\pos(0,0)") end + end + + -- STRIKEOUT TO SELECTED + if res.spec=="convert strikeout to selected" then + selcheck() + ST1=transphorm ST2=transphorm:gsub("(\\%d?%a+)[^\\]+","%1") + text=text:gsub("\\s1",ST1):gsub("\\s0",ST2) + if text==line.text then fail("No \\s1 tag.") end + text=text:gsub(ATAG,function(tg) return duplikill(tg) end) + end + + -- CREATE SHADOW FROM CLIP + if res.spec=="create shadow from clip" then + local KX1,KY1,KX2,KY2=text:match("\\i?clip%(m (%-?[%d%.]+) (%-?[%d%.]+) l (%-?[%d%.]+) (%-?[%d%.]+)") + if not KX1 then t_error("Line #"..i-line0..": Vectorial clip not detected.\nUse two points of a clip to set shadow direction.)",1) end + sr=stylechk(line.style) + stag=text:match(STAG) or "{}" + sha=stag:match("\\shad([%d%.]+)") + if not sha then + sx=stag:match("\\xshad%-?([%d%.]+)") + sy=stag:match("\\yshad%-?([%d%.]+)") + if sx and sy then sha=math.sqrt((sx^2+sy^2)/2) end + if not sha then sha=sr.shadow end + end + if tonumber(sha)==0 then t_error("Line #"..i-line0..": Shadow seems to be 0. Setting to 4.\n(It is preferable to set a \\shad value first.)") sha=4 end + eks=KX2-KX1 + wai=KY2-KY1 + pyth=math.sqrt(eks^2+wai^2) + shratio=pyth/math.sqrt(2*sha^2) + shX=round(eks/shratio,1) + shY=round(wai/shratio,1) + stag=stag:gsub("\\.?shad([%d%.]+)",""):gsub("\\i?clip%b()",""):gsub("}","\\xshad"..shX.."\\yshad"..shY.."}") + text=stag..text:gsub(STAG,"") + end + + -- 3D SHADOW + if res.spec=="create 3D effect from shadow" then + if not text:match("\\[xy]shad") then + text,c=text:gsub("\\shad([%d.]+)","\\xshad%1\\yshad%1") + if c==0 then + sr=stylechk(line.style) + text="{\\xshad"..sr.shadow.."\\yshad"..sr.shadow.."}"..text + text=text:gsub("^({.*)}{","%1") + end + end + xshad=tonumber(text:match("^{[^}]-\\xshad([%d%.%-]+)")) or 0 ax=math.abs(xshad) + yshad=tonumber(text:match("^{[^}]-\\yshad([%d%.%-]+)")) or 0 ay=math.abs(yshad) + if ax>ay then lay=math.floor(ax) else lay=math.floor(ay) end + + text2=text:gsub("^({\\[^}]-)}","%1\\3a&HFF&}") :gsub("\\3a&H%x%x&([^}]-)(\\3a&H%x%x&)","%1%2") + + for l=lay,1,-1 do + line2=line f=l/lay + text2=addtag3('\\1a&HFE&',text2) + txt=text2 if l==1 then txt=text end + line2.text=txt + :gsub("\\xshad([%d%.%-]+)",function(a) xx=tostring(f*a) xx=xx:gsub("([%d%-]+%.%d%d)%d+","%1") return "\\xshad"..xx end) + :gsub("\\yshad([%d%.%-]+)",function(a) yy=tostring(f*a) yy=yy:gsub("([%d%-]+%.%d%d)%d+","%1") return "\\yshad"..yy end) + line2.layer=layer+(lay-l) + subs.insert(i+1,line2) + end + + if math.abs(xshad)>=1 or math.abs(yshad)>=1 then + subs.delete(i) + for s=z+1,#sel do + sel[s]=sel[s]+lay-1 + end + success=success+1 + else fail("No shadow to work with.") + end + end + + -- CLIP GRID + if res.spec=="chequerboard clip" then + cbklip="\\clip(m 100 100 l 140 100 l 140 180 l 180 180 l 180 140 l 100 140 m 180 100 l 220 100 l 220 180 l 260 180 l 260 140 l 180 140 m 260 100 l 300 100 l 300 180 l 340 180 l 340 140 l 260 140 m 340 100 l 380 100 l 380 180 l 420 180 l 420 140 l 340 140 m 420 100 l 460 100 l 460 180 l 500 180 l 500 140 l 420 140 m 500 100 l 540 100 l 540 180 l 580 180 l 580 140 l 500 140 m 580 100 l 620 100 l 620 180 l 660 180 l 660 140 l 580 140 m 660 100 l 700 100 l 700 180 l 740 180 l 740 140 l 660 140 m 740 100 l 780 100 l 780 180 l 820 180 l 820 140 l 740 140 m 820 100 l 860 100 l 860 180 l 900 180 l 900 140 l 820 140 m 900 100 l 940 100 l 940 180 l 980 180 l 980 140 l 900 140 m 980 100 l 1020 100 l 1020 180 l 1060 180 l 1060 140 l 980 140 m 100 180 l 140 180 l 140 260 l 180 260 l 180 220 l 100 220 m 180 180 l 220 180 l 220 260 l 260 260 l 260 220 l 180 220 m 260 180 l 300 180 l 300 260 l 340 260 l 340 220 l 260 220 m 340 180 l 380 180 l 380 260 l 420 260 l 420 220 l 340 220 m 420 180 l 460 180 l 460 260 l 500 260 l 500 220 l 420 220 m 500 180 l 540 180 l 540 260 l 580 260 l 580 220 l 500 220 m 580 180 l 620 180 l 620 260 l 660 260 l 660 220 l 580 220 m 660 180 l 700 180 l 700 260 l 740 260 l 740 220 l 660 220 m 740 180 l 780 180 l 780 260 l 820 260 l 820 220 l 740 220 m 820 180 l 860 180 l 860 260 l 900 260 l 900 220 l 820 220 m 900 180 l 940 180 l 940 260 l 980 260 l 980 220 l 900 220 m 980 180 l 1020 180 l 1020 260 l 1060 260 l 1060 220 l 980 220 m 100 260 l 140 260 l 140 340 l 180 340 l 180 300 l 100 300 m 180 260 l 220 260 l 220 340 l 260 340 l 260 300 l 180 300 m 260 260 l 300 260 l 300 340 l 340 340 l 340 300 l 260 300 m 340 260 l 380 260 l 380 340 l 420 340 l 420 300 l 340 300 m 420 260 l 460 260 l 460 340 l 500 340 l 500 300 l 420 300 m 500 260 l 540 260 l 540 340 l 580 340 l 580 300 l 500 300 m 580 260 l 620 260 l 620 340 l 660 340 l 660 300 l 580 300 m 660 260 l 700 260 l 700 340 l 740 340 l 740 300 l 660 300 m 740 260 l 780 260 l 780 340 l 820 340 l 820 300 l 740 300 m 820 260 l 860 260 l 860 340 l 900 340 l 900 300 l 820 300 m 900 260 l 940 260 l 940 340 l 980 340 l 980 300 l 900 300 m 980 260 l 1020 260 l 1020 340 l 1060 340 l 1060 300 l 980 300 m 100 340 l 140 340 l 140 420 l 180 420 l 180 380 l 100 380 m 180 340 l 220 340 l 220 420 l 260 420 l 260 380 l 180 380 m 260 340 l 300 340 l 300 420 l 340 420 l 340 380 l 260 380 m 340 340 l 380 340 l 380 420 l 420 420 l 420 380 l 340 380 m 420 340 l 460 340 l 460 420 l 500 420 l 500 380 l 420 380 m 500 340 l 540 340 l 540 420 l 580 420 l 580 380 l 500 380 m 580 340 l 620 340 l 620 420 l 660 420 l 660 380 l 580 380 m 660 340 l 700 340 l 700 420 l 740 420 l 740 380 l 660 380 m 740 340 l 780 340 l 780 420 l 820 420 l 820 380 l 740 380 m 820 340 l 860 340 l 860 420 l 900 420 l 900 380 l 820 380 m 900 340 l 940 340 l 940 420 l 980 420 l 980 380 l 900 380 m 980 340 l 1020 340 l 1020 420 l 1060 420 l 1060 380 l 980 380 m 100 420 l 140 420 l 140 500 l 180 500 l 180 460 l 100 460 m 180 420 l 220 420 l 220 500 l 260 500 l 260 460 l 180 460 m 260 420 l 300 420 l 300 500 l 340 500 l 340 460 l 260 460 m 340 420 l 380 420 l 380 500 l 420 500 l 420 460 l 340 460 m 420 420 l 460 420 l 460 500 l 500 500 l 500 460 l 420 460 m 500 420 l 540 420 l 540 500 l 580 500 l 580 460 l 500 460 m 580 420 l 620 420 l 620 500 l 660 500 l 660 460 l 580 460 m 660 420 l 700 420 l 700 500 l 740 500 l 740 460 l 660 460 m 740 420 l 780 420 l 780 500 l 820 500 l 820 460 l 740 460 m 820 420 l 860 420 l 860 500 l 900 500 l 900 460 l 820 460 m 900 420 l 940 420 l 940 500 l 980 500 l 980 460 l 900 460 m 980 420 l 1020 420 l 1020 500 l 1060 500 l 1060 460 l 980 460 m 100 500 l 140 500 l 140 580 l 180 580 l 180 540 l 100 540 m 180 500 l 220 500 l 220 580 l 260 580 l 260 540 l 180 540 m 260 500 l 300 500 l 300 580 l 340 580 l 340 540 l 260 540 m 340 500 l 380 500 l 380 580 l 420 580 l 420 540 l 340 540 m 420 500 l 460 500 l 460 580 l 500 580 l 500 540 l 420 540 m 500 500 l 540 500 l 540 580 l 580 580 l 580 540 l 500 540 m 580 500 l 620 500 l 620 580 l 660 580 l 660 540 l 580 540 m 660 500 l 700 500 l 700 580 l 740 580 l 740 540 l 660 540 m 740 500 l 780 500 l 780 580 l 820 580 l 820 540 l 740 540 m 820 500 l 860 500 l 860 580 l 900 580 l 900 540 l 820 540 m 900 500 l 940 500 l 940 580 l 980 580 l 980 540 l 900 540 m 980 500 l 1020 500 l 1020 580 l 1060 580 l 1060 540 l 980 540)" + text=text:gsub("^({[^}]-)\\i?clip%([^%)]+%)","%1") + :gsub("^({\\[^}]-)}","%1"..cbklip.."}") + if not text:match("^{") then text=wrap(cbklip)..text end + end + + -- size transform from clip + if res.spec=="size transform from clip" then + local klip=text:match("\\i?clip%(m %-?[%d%.]+ %-?[%d%.]+ l %-?[%d%.]+ %-?[%d%.]+ %-?[%d%.]+ %-?[%d%.]+ %-?[%d%.]+ %-?[%d%.]+") + if not klip then t_error("Line #"..i-line0..": Vectorial clip with 4 points required.",1) end + local K1x,K1y,K2x,K2y,K3x,K3y,K4x,K4y=klip:match("m (%-?[%d%.]+) (%-?[%d%.]+) l (%-?[%d%.]+) (%-?[%d%.]+) (%-?[%d%.]+) (%-?[%d%.]+) (%-?[%d%.]+) (%-?[%d%.]+)") + if defaref and line.style=="Default" then sr=defaref + else sr=stylechk(line.style) end + -- clean up existing transforms + if text:match("^{[^}]*\\t") then text=text:gsub(STAG,function(tg) return cleantr(tg) end) end + startags=text:match(STAG) or "" + scx=startags:match("\\fscx([%d%.]+)") or sr.scale_x + scy=startags:match("\\fscy([%d%.]+)") or sr.scale_y + xdist1=math.abs(K1x-K2x) + xdist2=math.abs(K3x-K4x) + ydist1=math.abs(K1y-K2y) + ydist2=math.abs(K3y-K4y) + dist1=math.sqrt(xdist1^2+ydist1^2) + dist2=math.sqrt(xdist2^2+ydist2^2) + size_ratio=dist2/dist1 + rescx=round(scx*size_ratio,2) + rescy=round(scy*size_ratio,2) + local trans="\\t(\\fscx"..rescx.."\\fscy"..rescy..")" + startags=startags:gsub("}",trans.."}") + startags=cleantr(startags) + text=text:gsub(STAG,startags):gsub("\\i?clip%b()","") + end + + -- BACK AND FORTH TRANSFORM + if res.spec=="back and forth transform" and res.int>0 then + selcheck() + if defaref and line.style=="Default" then sr=defaref + else sr=stylechk(line.style) end + -- clean up existing transforms + if text:match("^{[^}]*\\t") then text=text:gsub(STAG,function(tg) return cleantr(tg) end) end + startags=text:match(STAG) or "" + tags_1="" + for tg in transphorm:gmatch("\\[1234]?%a+") do + val1=nil + if not startags:match(tg.."[%d%-&%(]") then + if tg=="\\clip" then val1="(0,0,1280,720)" else val1=tag2style(tg,sr) end + if val1 then tags_1=tags_1..tg..val1 text=text:gsub("^({\\[^}]-)}","%1"..tg..val1.."}") end + else + val1=startags:match(tg.."([^\\}]+)") + tags_1=tags_1..tg..val1 + end + end + int=res.int + tgs2=transphorm + dur=line.end_time-line.start_time + count=math.ceil(dur/int) + t=1 tin=0 tout=tin+int + if not text:match("^{\\") then text="{\\}"..text end + -- main function + while t<=math.ceil(count/2) do + text=text:gsub("^({\\[^}]*)}","%1\\t("..tin..","..tout..","..tgs2..")}") + if tin+int=dur then fail("Split times for some lines are longer than duration of line.") + else + effect=line.effect + -- line 3 + if ST2>0 then + line3=line + line3.start_time=endt-ST2 + line3.effect=effect.." pt.3" + subs.insert(i+1,line3) + nsel=shiftsel2(nsel,i,1) + end + -- line 2 + line2=line + line2.start_time=start+ST1 + line2.end_time=endt-ST2 + line2.effect=effect.." pt.2" + subs.insert(i+1,line2) + nsel=shiftsel2(nsel,i,1) + -- line 1 + if ST1>0 then + line.start_time=start + line.end_time=start+ST1 + line.effect=effect.." pt.1" + else + subs.delete(i) + for s=#nsel,z,-1 do + if nsel[s]==i then table.remove(nsel,s) end + if nsel[s]>i then nsel[s]=nsel[s]-1 end + end + end + success=success+1 + end + end + + + if res.spec~="create 3D effect from shadow" then + if line.text~=text then + success=success+1 + line.text=text + subs[i]=line + end + end + end + end + if res.spec=="split line in 3 parts" then sel=nsel end + summary() + return sel +end + +function frz_redraw(t,rota,draw,sr) + local X,Y,x,y,width,height + local ox,oy,xmx,xmn,ymx,ymn,addx,addy=0,0,0,999999,0,999999,0,0 + draw1=draw + -- deal with align other than 7 + if align~=7 then + for px,py in draw:gmatch("([-%d.]+) ([-%d.]+)") do + px=tonumber(px) + py=tonumber(py) + if px>xmx then xmx=px end + if pxymx then ymx=py end + if py=end_time then + end_time=line.end_time + else + table.insert(overlaps,line.i) + end + end + sel=overlaps + return sel +end + + +-- reanimatools ----------------------------------------------------------------------- +function addtag3(tg,txt) + no_tf=txt:gsub("\\t%b()","") + tgt=tg:match("(\\%d?%a+)[%d%-&]") val="[%d%-&]" + if not tgt then tgt=tg:match("(\\%d?%a+)%b()") val="%b()" end + if not tgt then tgt=tg:match("\\fn") val="" end + if not tgt then t_error("adding tag '"..tg.."' failed.") end + if tgt:match("clip") then txt,r=txt:gsub("^({[^}]-)\\i?clip%b()","%1"..tg) + if r==0 then txt=txt:gsub("^({\\[^}]-)}","%1"..tg.."}") end + elseif no_tf:match("^({[^}]-)"..tgt..val) then txt=txt:gsub("^({[^}]-)"..tgt..val.."[^\\}]*","%1"..tg) + elseif not txt:match("^{\\") then txt="{"..tg.."}"..txt + elseif txt:match("^{[^}]-\\t") then txt=txt:gsub("^({[^}]-)\\t","%1"..tg.."\\t") + else txt=txt:gsub("^({\\[^}]-)}","%1"..tg.."}") end + return txt +end + +function esc(str) str=str:gsub("[%%%(%)%[%]%.%-%+%*%?%^%$]","%%%1") return str end +function round(n,dec) dec=dec or 0 n=math.floor(n*10^dec+0.5)/10^dec return n end +function wrap(str) return "{"..str.."}" end +function logg(m) m=tf(m) or "nil" aegisub.log("\n "..m) end +function loggtab(m) m=tf(m) or "nil" aegisub.log("\n {"..table.concat(m,', ').."}") end +function tagmerge(t) repeat t,r=t:gsub("({\\[^}]-)}{(\\[^}]-})","%1%2") until r==0 return t end +function detra(t) return t:gsub("\\t%b()","") end +function nobra(t) return t:gsub("%b{}","") end +function nobrea(t) return t:gsub("%b{}",""):gsub("\\[Nh]","") end +function nobrea1(t) return t:gsub("%b{}",""):gsub(" *\\[Nh] *"," ") end +function progress(msg) if aegisub.progress.is_cancelled() then ak() end aegisub.progress.title(msg) end +function t_error(message,cancel) ADD({{class="label",label=message}},{"OK"},{close='OK'}) if cancel then ak() end end + +function tag2style(tg,sr) + noneg="\\bord\\shad\\xbord\\ybord\\fs\\blur\\be\\fscx\\fscy" + val=0 + if tg=="\\fs" then val=sr.fontsize end + if tg=="\\fsp" then val=sr.spacing end + if tg=="\\fscx" then val=sr.scale_x end + if tg=="\\fscy" then val=sr.scale_y end + if tg:match"\\[xy]?bord" then val=sr.outline end + if tg:match"\\[xy]?shad" then val=sr.shadow end + if tg=="\\frz" then val=sr.angle end + if val<0 and noneg:match(tg) then val=0 end + if tg=="\\c" then val=sr.color1:gsub("H%x%x","H") end + if tg=="\\2c" then val=sr.color2:gsub("H%x%x","H") end + if tg=="\\3c" then val=sr.color3:gsub("H%x%x","H") end + if tg=="\\4c" then val=sr.color4:gsub("H%x%x","H") end + if tg=="\\1a" then val=sr.color1:match("H%x%x") end + if tg=="\\2a" then val=sr.color2:match("H%x%x") end + if tg=="\\3a" then val=sr.color3:match("H%x%x") end + if tg=="\\4a" then val=sr.color4:match("H%x%x") end + if tg=="\\alpha" then val="&H00&" end +return val +end + +function numgrad(V1,V2,total,l,acc) + acc=acc or 1 + acc_fac=(l-1)^acc/(total-1)^acc + VC=round(acc_fac*(V2-V1)+V1,2) +return VC +end + +function agrad(C1,C2,total,l,acc) + acc=acc or 1 + acc_fac=(l-1)^acc/(total-1)^acc + A1=C1:match("(%x%x)") + A2=C2:match("(%x%x)") + nA1=(tonumber(A1,16)) nA2=(tonumber(A2,16)) + A=acc_fac*(nA2-nA1)+nA1 + A=tohex(round(A)) + CC="&H"..A.."&" +return CC +end + +function acgrad(C1,C2,total,l,acc) + acc=acc or 1 + acc_fac=(l-1)^acc/(total-1)^acc + B1,G1,R1=C1:match("(%x%x)(%x%x)(%x%x)") + B2,G2,R2=C2:match("(%x%x)(%x%x)(%x%x)") + A1=C1:match("(%x%x)") R1=R1 or A1 + A2=C2:match("(%x%x)") R2=R2 or A2 + nR1=(tonumber(R1,16)) nR2=(tonumber(R2,16)) + R=acc_fac*(nR2-nR1)+nR1 + R=tohex(round(R)) + CC="&H"..R.."&" + if B1 then + nG1=(tonumber(G1,16)) nG2=(tonumber(G2,16)) + nB1=(tonumber(B1,16)) nB2=(tonumber(B2,16)) + G=acc_fac*(nG2-nG1)+nG1 + B=acc_fac*(nB2-nB1)+nB1 + G=tohex(round(G)) + B=tohex(round(B)) + CC="&H"..B..G..R.."&" + end +return CC +end + +function acgradhsl(C1,C2,total,l,acc) + acc=acc or 1 + acc_fac=(l-1)^acc/(total-1)^acc + B1,G1,R1=C1:match("(%x%x)(%x%x)(%x%x)") + B2,G2,R2=C2:match("(%x%x)(%x%x)(%x%x)") + H1,S1,L1=RGB_to_HSL(R1,G1,B1) + H2,S2,L2=RGB_to_HSL(R2,G2,B2) + if res.short then + if H2>H1 and H2-H1>0.5 then H1=H1+1 end + if H2

0.5 then H2=H2+1 end + end + Hdiff=(H2-H1)/(total-1) H=H1+Hdiff*(l-1) + Sdiff=(S2-S1)/(total-1) S=S1+Sdiff*(l-1) + Ldiff=(L2-L1)/(total-1) L=L1+Ldiff*(l-1) + R,G,B=HSL_to_RGB(H,S,L) + R=tohex(round(R)) + G=tohex(round(G)) + B=tohex(round(B)) + NC="&H"..B..G..R.."&" +return NC +end + +function RGB_to_HSL(Red,Green,Blue) + R=(tonumber(Red,16)/255) + G=(tonumber(Green,16)/255) + B=(tonumber(Blue,16)/255) + + Min=math.min(R,G,B) + Max=math.max(R,G,B) + del_Max=Max-Min + + L=(Max+Min)/2 + + if del_Max==0 then H=0 S=0 + else + if L<0.5 then S=del_Max/(Max+Min) + else S=del_Max/(2-Max-Min) + end + + del_R=(((Max-R)/6)+(del_Max/2))/del_Max + del_G=(((Max-G)/6)+(del_Max/2))/del_Max + del_B=(((Max-B)/6)+(del_Max/2))/del_Max + + if R==Max then H=del_B-del_G + elseif G==Max then H=(1/3)+del_R-del_B + elseif B==Max then H=(2/3)+del_G-del_R + end + + if H<0 then H=H+1 end + if H>1 then H=H-1 end + end + return H,S,L +end + +function HSL_to_RGB(H,S,L) + if S==0 then + R=L*255 + G=L*255 + B=L*255 + else + if L<0.5 then var_2=L*(1+S) + else var_2=(L+S)-(S*L) + end + var_1=2*L-var_2 + R=255*Hue_to_RGB(var_1,var_2,H+(1/3)) + G=255*Hue_to_RGB(var_1,var_2,H) + B=255*Hue_to_RGB(var_1,var_2,H-(1/3)) + end + return R,G,B +end + +function Hue_to_RGB(v1,v2,vH) + if vH<0 then vH=vH+1 end + if vH>1 then vH=vH-1 end + if (6*vH)<1 then return(v1+(v2-v1)*6*vH) end + if (2*vH)<1 then return(v2) end + if (3*vH)<2 then return(v1+(v2-v1)*((2/3)-vH)*6) end + return(v1) +end + +function tohex(num) +n1=math.floor(num/16) +n2=math.floor(num%16) +num=tohex1(n1)..tohex1(n2) +return num +end + +function tohex1(num) +HEX={"1","2","3","4","5","6","7","8","9","A","B","C","D","E"} +if num<1 then num="0" elseif num>14 then num="F" else num=HEX[num] end +return num +end + +function shortrot(v) + if tonumber(v)>180 then v=v-360 end + return v +end + +function trem(tags) + trnsfrm="" + for t in tags:gmatch("\\t%b()") do trnsfrm=trnsfrm..t end + tags=tags:gsub("\\t%b()","") + return tags +end + +function cleantr(tags) + trnsfrm="" + zerotf="" + for t in tags:gmatch("\\t%b()") do + if t:match("\\t%(\\") then + zerotf=zerotf..t:match("\\t%((.*)%)$") + else + trnsfrm=trnsfrm..t + end + end + zerotf="\\t("..zerotf..")" + tags=tags:gsub("\\t%b()",""):gsub("^({[^}]*)}","%1"..zerotf..trnsfrm.."}"):gsub("\\t%(%)","") + return tags +end + +function duplikill(tagz) + local tags1={"blur","be","bord","shad","xbord","xshad","ybord","yshad","fs","fsp","fscx","fscy","frz","frx","fry","fax","fay"} + local tags2={"c","2c","3c","4c","1a","2a","3a","4a","alpha"} + tagz=tagz:gsub("\\t%b()",function(t) return t:gsub("\\","|") end) + for i=1,#tags1 do + tag=tags1[i] + repeat tagz,c=tagz:gsub("|"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%1%2") until c==0 + repeat tagz,c=tagz:gsub("\\"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%2%1") until c==0 + end + tagz=tagz:gsub("\\1c&","\\c&") + for i=1,#tags2 do + tag=tags2[i] + repeat tagz,c=tagz:gsub("|"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%1%2") until c==0 + repeat tagz,c=tagz:gsub("\\"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%2%1") until c==0 + end + repeat tagz,c=tagz:gsub("\\fn[^\\}]+([^}]-)(\\fn[^\\}]+)","%2%1") until c==0 + repeat tagz,c=tagz:gsub("(\\[ibusq])%d(.-)(%1%d)","%2%3") until c==0 + repeat tagz,c=tagz:gsub("(\\an)%d(.-)(%1%d)","%3%2") until c==0 + tagz=tagz:gsub("(|i?clip%(%A-%))(.-)(\\i?clip%(%A-%))","%2%3") + :gsub("(\\i?clip%b())(.-)(\\i?clip%b())",function(a,b,c) + if a:match("m") and c:match("m") or not a:match("m") and not c:match("m") then return b..c else return a..b..c end end) + tagz=tagz:gsub("|","\\"):gsub("\\t%([^\\%)]-%)","") + return tagz +end + +function extrakill(text,o) + local tags3={"pos","move","org","fad"} + for i=1,#tags3 do + tag=tags3[i] + if o==2 then + repeat text,c=text:gsub("(\\"..tag.."[^\\}]+)([^}]-)(\\"..tag.."[^\\}]+)","%3%2") until c==0 + else + repeat text,c=text:gsub("(\\"..tag.."[^\\}]+)([^}]-)(\\"..tag.."[^\\}]+)","%1%2") until c==0 + end + end + repeat text,c=text:gsub("(\\pos[^\\}]+)([^}]-)(\\move[^\\}]+)","%1%2") until c==0 + repeat text,c=text:gsub("(\\move[^\\}]+)([^}]-)(\\pos[^\\}]+)","%1%2") until c==0 + return text +end + +function retextmod(orig,text) + local v1,v2,c,t2 + v1=nobrea(orig) + c=0 + repeat + t2=textmod(orig,text) + v2=nobrea(text) + c=c+1 + until v1==v2 or c==666 + if v1~=v2 then logg("Something went wrong with the text...") logg(v1) logg(v2) end + return t2 +end + +function textmod(orig,text) +if text=="" then return orig end + tk={} + tg={} + text=text:gsub("{\\\\k0}","") + text=tagmerge(text) + vis=nobra(text) + ltrmatches=re.find(vis,".") + if not ltrmatches then logg("text: "..text..'\nvisible: '..vis) + logg("If you're seeing this, something really weird is happening with the re module.\nTry this again or rescan Autoload.") + end + for l=1,#ltrmatches do + table.insert(tk,ltrmatches[l].str) + end + stags=text:match(STAG) or "" + text=text:gsub(STAG,"") :gsub("{[^\\}]-}","") + orig=orig:gsub("{([^\\}]+)}",function(c) return wrap("\\\\"..c.."|||") end) + count=0 + for seq in orig:gmatch("[^{]-{%*?\\[^}]-}") do + chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") + pos=re.find(chars,".") + if pos==nil then ps=0+count else ps=#pos+count end + tgl={p=ps,t=tak,a=as} + table.insert(tg,tgl) + count=ps + end + count=0 + for seq in text:gmatch("[^{]-{%*?\\[^}]-}") do + chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") + pos=re.find(chars,".") + if pos==nil then ps=0+count else ps=#pos+count end + tgl={p=ps,t=tak,a=as} + table.insert(tg,tgl) + count=ps + end + newline="" + for i=1,#tk do + newline=newline..tk[i] + newt="" + for n,t in ipairs(tg) do + if t.p==i then newt=newt..t.a..t.t end + end + if newt~="" then newline=newline.."{"..as..newt.."}" end + end + newtext=stags..newline:gsub("(|||)(\\\\)","%1}{%2"):gsub("({[^}]-)\\\\([^\\}]-)|||","{%2}%1") + text=newtext:gsub("{}","") + return text +end + +function fail(msg) + local q=1 + for i=1,#failures do + if msg==failures[i] then q=0 end + end + if q==1 then table.insert(failures,msg) end +end + +function summary() + if res.show then + local MSG='No problems encountered.' + if success0 then MSG=MSG.."Probable causes:" end + for i=1,#failures do + MSG=MSG.."\n> "..failures[i] + end + MSG=MSG.."\n\nPressed: "..P + if P=="Apply" and res.tagpres~="--- presets ---" then MSG=MSG.." (Preset: \""..res.tagpres.."\")" end + MSG=MSG:gsub("\n\n+","\n\n") + end + msgbox(tags_used,MSG) + end +end + +function showtags(tagc) tags_used=tagc end + +function msgbox(msg1,msg2,h,w) + pres,rez=ADD({ + {width=w or 24,height=1,name='msg',class="edit",value=msg1}, + {y=1,width=w or 24,height=h or 8,class="textbox",value=msg2}, + },{"OK","clip bored","~"},{ok='OK',close='~'}) + if pres=="clip bored" then clipboard.set(rez.msg) end +end + + +function styleget(subs) + styles={} + for i=1,#subs do + if subs[i].class=="style" then + table.insert(styles,subs[i]) + end + if subs[i].class=="dialogue" then break end + end +end + +function stylechk(sn) + for i=1,#styles do + if sn==styles[i].name then + sr=styles[i] + if sr.name=="Default" then defaref=styles[i] end + break + end + end + if sr==nil then t_error("Style '"..sn.."' doesn't exist.",1) end + return sr +end + +function tf(val) + if val==true then ret="true" + elseif val==false then ret="false" + else ret=val end + return ret +end + +function detf(txt) + if txt=="true" then ret=true + elseif txt=="false" then ret=false + else ret=txt end + return ret +end + +function selcheck() + SC=0 + for k,v in ipairs(hh_gui) do + if v.class=="checkbox" and v.y<10 and res[v.name] then SC=1 end + if v.name=="reuse" and res.reuse then SC=1 end + if v.name=="moretags" and res.moretags:len()>1 then SC=1 end + end + if SC==0 then t_error("No tags selected",1) end +end + +function shiftsel2(sel,i,mode) + if ii then sel[s]=sel[s]+1 end + end + end + if mode==1 then table.insert(sel,i+1) end + table.sort(sel) +return sel +end + +function txt_check(t1,t2,i) + if t1~=t2 then + fail("Some letters seem to have been lost or added.\n Check the log for more details.") + logg("Line #"..i-line0..": It appears that characters have been lost or added. \n If the problem isn't obvious from the two lines below, it's probably a failure of the re module.\n Undo (Ctrl+Z) and try again (Repeat Last might work). If the problem persists, rescan Autoload Dir.\n>> "..t1.."\n--> "..t2.."\n") + end +end + +function getpos(subs,text) + st=nil defst=nil + for g=1,#subs do + if subs[g].class=="info" then + local k=subs[g].key + local v=subs[g].value + if k=="PlayResX" then resx=v end + if k=="PlayResY" then resy=v end + end + if resx==nil then resx=0 end + if resy==nil then resy=0 end + if subs[g].class=="style" then + local s=subs[g] + if s.name==line.style then st=s break end + if s.name=="Default" then defst=s end + end + if subs[g].class=="dialogue" then + if defst then st=defst else t_error("Style '"..line.style.."' not found.\nStyle 'Default' not found. ",1) end + break + end + end + if st then + acleft=st.margin_l if line.margin_l>0 then acleft=line.margin_l end + acright=st.margin_r if line.margin_r>0 then acright=line.margin_r end + acvert=st.margin_t if line.margin_t>0 then acvert=line.margin_t end + acalign=st.align if text:match("\\an%d") then acalign=text:match("\\an(%d)") end + aligntop="789" alignbot="123" aligncent="456" + alignleft="147" alignright="369" alignmid="258" + if alignleft:match(acalign) then horz=acleft + elseif alignright:match(acalign) then horz=resx-acright + elseif alignmid:match(acalign) then horz=resx/2 end + if aligntop:match(acalign) then vert=acvert + elseif alignbot:match(acalign) then vert=resy-acvert + elseif aligncent:match(acalign) then vert=resy/2 end + end + if horz>0 and vert>0 then + if not text:match("^{\\") then text="{\\rel}"..text end + text=text:gsub("^({\\[^}]-)}","%1\\pos("..horz..","..vert..")}") :gsub("\\rel","") + end + return text +end + +-- Config Stuff -- +function saveconfig() +hydraconf="Hydra 4+ Config\n\n" + for key,v in ipairs(hh_gui) do + if v.class:match"edit" or v.class=="dropdown" or v.class=="coloralpha" then + if v.name~="linetext" and v.name~="herp" and v.name~="lastags" and v.name~="exc" and not v.name:match"app" then + hydraconf=hydraconf..v.name..":"..res[v.name].."\n" + end + end + if v.class=="checkbox" and v.name~="save" then + hydraconf=hydraconf..v.name..":"..tf(res[v.name]).."\n" + end + end +file=io.open(hydrakonfig,"w") +file:write(hydraconf) +file:close() +ADD({{class="label",label="Config saved to:\n"..hydrakonfig}},{"OK"},{close='OK'}) +end + +function loadconfig() +file=io.open(hydrakonfig) + if file~=nil then + konf=file:read("*all") + sm=tonumber(konf:match("smode:(.-)\n")) + io.close(file) + for k,v in ipairs(hh1) do + if v.class:match"edit" or v.class=="checkbox" or v.class=="coloralpha" then + if konf:match(v.name) then v.value=detf(konf:match(v.name..":(.-)\n")) end + if hydralast and res.rem then v.value=hydralast[v.name] end + if v.name=="lastags" then v.value=ortags end + end + end + for k,v in ipairs(hh2) do + if v.class:match"edit" or v.class=="checkbox" or v.class=="dropdown" then + if konf:match(v.name) then v.value=detf(konf:match(v.name..":(.-)\n")) end + if hydralast and res.rem and tf(hydralast[v.name]) then v.value=hydralast[v.name] end + + end + end + for k,v in ipairs(hh3) do + if v.class:match"edit" or v.class=="checkbox" or v.class=="dropdown" then + if konf:match(v.name) then v.value=detf(konf:match(v.name..":(.-)\n")) end + if hydralast then + if v.name=="gtype" and res.gtype then v.value=hydralast.gtype end + if v.name=="accel" and res.accel then v.value=hydralast.accel end + if v.name=="stripe" and res.stripe then v.value=hydralast.stripe end + if v.name=="spec" and res.spec then v.value=hydralast.spec end + if v.name=="middle" and tf(res.middle) then v.value=hydralast.middle end + if v.name=="short" and tf(res.short) then v.value=hydralast.short end + if v.name=="hsl" and tf(res.hsl) then v.value=hydralast.hsl end + if res.rem and tf(hydralast[v.name]) then v.value=hydralast[v.name] end + end + end + end + else sm=1 + end +end + +hydraulics={"A multi-headed typesetting tool","Nine heads typeset better than one.","Eliminating the typing part of typesetting","Mass-production of typesetting tags","Hydraulic typesetting machinery","Making sure your subtitles aren't dehydrated","Making typesetting so easy that even you can do it!","A monstrous typesetting tool","A deadly typesetting beast","Building monstrous scripts with ease","For irrational typesetting wizardry","Building a Wall of Tags","The Checkbox Onslaught","HYperactively DRAstic","HYperdimensional DRAma","I can has moar tagz?","Transforming the subtitle landscape"} + +function hydrahelp() +H_H=[[ +For more detailed info, check http://unanimated.hostfree.pw/ts/scripts-manuals.htm#hydra + +Standard mode: check tags, set values, click on 'Apply'. + +Transform mode 'normal': check tags, set values, set t1/t2/accel if needed, click on 'Transform'. +Transform mode 'add2first': the transforms will be added to the first existing transform in the line. +Transform mode 'add2all': the transforms will be added to all existing transforms in the line. +Transform mode 'all tag blocks': the transforms will be added to all tag blocks, whether they have transforms or not. +Count times from end: This will count transform times from the end of the line, + so "500,200" will mean that the transform will start 500ms and end 200ms before the end of the line. +Shift times/interval: 'normal' tf - shift \t times each line; 'all tag blocks' - shift \t times each tag block. +Relative transform: If you have frz30 and set transform to frz60, you get \t(\frz90), or transform BY 60. + This allows you to keep layers with different frz in sync. (No effect on alpha/colours.) + +Alphas for colours can be done in two ways. One is on the right where it's pretty obvious. The other is using the colour pickers. +These have both the colour and its alpha value, and you can apply either the colour, or the alpha, or both. +By default it uses only the colour. If you want the alpha too, you check Include alphas. +If you want only the alpha and not the colour, you check the only checkbox next to it. + +Additional tags: you can type any extra tags you want to add. + +Tag position: This shows the text of your first line. Type * where you want your tags to go. + +Tag position presets: This places tags in specified positions, proportionally for each selected line. +Custom pattern: This uses an asterisk like the basic mode, but it can use shorter patterns, as opposed to the whole text of the line. + For example you can use the pattern "*and", and tags will be placed before the "and" in any line that contains that pattern, + no matter what the rest of the text is and however many times that word is in the line. +Section: This lets you put tags before a given pattern and then changes the tags back after it. Type only a pattern like "and". + You'll get something like {\bord5}and{\bord}, or back to the value in start tags. Only the first pattern in line is matched. + Custom pattern and Section may not work 100% correctly with inline tags. +Text position: With this option, you type a number in the Tag position field, and tags will be set at that position. + 0 is the start of the line, and it counts visible characters including punctuation and spaces. ("\N" is 2 characters.) + So if you type 12, the tags will go after first 12 characters in all selected lines, whatever the text. + If the line has 12 or fewer characters, nothing happens to it. + To make it more fun, you can enter negative numbers and count from the end, so "-1" will go before last character. + Check the online manual for some more info. + +Add with each line: '2' means that for each line, the value of all checked applicable tags is increased by an extra 2. + +Gradient: Current state is the start. Given tags are the end. Accel works with this. Vertical/horizontal need a clip. + +Centred gradient: 'There and back.' Given state will be in the middle. Last line/character will be the same as the first. + For more on gradients, check the online manual. + +Last: end values for Gradient by Line will be taken from the start tags of the last selected line if available + + +Special functions: select a function, click on 'Special'. + +fscx -> fscy: Applies the value of fscx to fscy, making them the same. + +fscy -> fscx: Same but the other way round. + +move colour tag to first block: If you use hotkeys for colour pickers, the colour gets sometimes applied somewhere in the middle of the line or at the end because that's where the cursor is in the textbox area. This moves a colour tag that isn't at the start of the line to the start. If it finds more, it deletes them all and uses the last one in the line. +This is a lot more useful if hotkeyed, which can now be done. + +convert clip <-> iclip: Changes clips to iclips and vice versa. (Can be hotkeyed.) + +clean up tags: Same as in Script Cleanup. + +sort tags in set order: This sorts tags in each tag block based on a set order. + +back and forth transform: This will transform back and forth +between the current state of the line and tags you select. +So for example, you select \bord 10 and \frz 20 and run the script. It will read the current bord and frz from the line or from style and create transforms based on given interval (Shift times/interval field). A value of 500 means that it will take 500ms to transform to \bord10\frz20, then 500ms to transform back, another 500 forward again, etc. for the whole duration of the line. This way you can create wobbling effects and such. + +select overlaps: This used to be shipped with Aegisub. I don't know if it still is, but somebody wanted that included in HYDRA, so here it is. It selects lines that overlap with other lines. + +convert clip to drawing: Uses coordinates from a clip to create a drawing. + +convert drawing to clip: Same but the other way round. + +size transform from clip: Creates \t(\fscx\fscy) based on a vectorial clip. The distance between the first 2 points of the clip mark the original size; distance between points 3 and 4 mark the final size. This way you can theoretically match linear zoom in the video without Mocha. It may be useful to duplicate the line if you need to see the text while drawing. Pick something in the video, match the size of it with 2 clip points on the first frame and with another 2 points on the last frame. + +convert strikeout to selected: Converts \s1 to the tags you select, and \s0 back to the original state. This allows you to use a quick on/off trigger for multiple tags at the same time. You apply \s to a word or section of text in the Edit Box, and then you can convert it to whatever tags you want. + +chequerboard clip: This creates a checkerboard clip. Not too useful, but you can convert it with the above tool to drawing. This also allows you to resize it with the scaling tool and convert back, so you can get various sizes. + +shad -> xshad+yshad: Changes \shadX to \xshadX\yshadX. (Can be hotkeyed.) + +create shadow from clip: To get correct shadow orientation, make 2 points with a vectorial clip in the direction of the shadow. Distance will be used from current shadow. Shadow is creaqted with \xshad\yshad. (Can be hotkeyed.) + +create 3D effect from shadow: This is one of the more useful things in this menu. The space between the letters and a shadow gets "filled" with the shadow colour. + +split line in 3 parts: Use the fields for 'Transform times' to set duration of line 1 and 3. If you set for example 200 and 300, your line will be triplicated, with the first one being 200ms long, the last one 300ms, and the middle one whatever is left of the original duration. +If you set either of the two to 0, you'll only have 2 lines. +This can be useful for song styling when you want to apply some transforms to the first or last 500ms, for example, because applying the transforms to the lines with the whole duration can be much more laggy, and lines with too many transforms can look too chaotic to work with them. + + +reuse: Unlike Repeat Last, this remembers the (transformable) tags but lets you use a different function and restrictions. +If you simply applied tags before, you can now reuse the tags for other lines, to make transforms or gradients, +or to apply to different layers, etc. For example, you may have 5 layers and want to apply things to layers 3 and 4. +If you don't remember what tags you used last time and whether they're the ones you'd like to reuse, +checking show at the same time will tell you. +"reuse" can be very useful if you do transforms or gradients, you get the tags right, but you mess up the other settings. +You can fix the settings and reuse the tags. + +Apply to: +You can choose to which of the selected lines you want to apply the changes based on the 4 restrictions. +When dealing with multi-layered signs, you may need different tags for different layers, so this can make it easy, without having to change the selection. +The last one with "Text..." in it restricts by any text pattern you type. This is literal and can include tags and comments, so for example you can apply tags only to lines with \frz in them. (The "Text..." one doesn't work.) + +"rem" = remember last settings. + +Repeat Last will run the last used function with the last used settings. + +Save Config saves the current configuration to a file named "hydra4.conf" in your APPDATA folder. +]] +Pr=aegisub.dialog.display({{width=55,height=20,class="textbox",value=H_H}},{"Hai"},{close='Hai'}) +end + +function cuts() +ADD=aegisub.dialog.display +ADP=aegisub.decode_path +ak=aegisub.cancel +ATAG="{[*>]?\\[^}]-}" +STAG="^{>?\\[^}]-}" +COMM="{[^\\}]-}" +end + +function hydra(subs,sel) +if #sel==0 then t_error("No selection",1) end +cuts() +hydrakonfig=ADP("?user").."\\hydra4.conf" +failures={} +success=0 +seln=#sel +app_lay={} +app_sty={"All Styles"} +app_act={"All Actors"} +app_eff={"All Effects"} +for i=1,#subs do + if subs[i].class=="dialogue" then line0=i-1 break end +end +for z,i in ipairs(sel) do + L=subs[i] + layr=L.layer + stl=L.style + akt=L.actor + eph=L.effect + local asdf=0 + for a=1,#app_lay do if layr==app_lay[a] then asdf=1 end end + if asdf==0 then table.insert(app_lay,layr) end + asdf=0 + for a=1,#app_sty do if stl==app_sty[a] then asdf=1 end end + if asdf==0 then table.insert(app_sty,stl) end + asdf=0 + for a=1,#app_act do if akt==app_act[a] then asdf=1 end end + if asdf==0 then table.insert(app_act,akt) end + asdf=0 + for a=1,#app_eff do if eph==app_eff[a] then asdf=1 end end + if asdf==0 then table.insert(app_eff,eph) end + table.sort(app_lay,function(a,b) return a fscy","fscy -> fscx","convert clip <-> iclip","convert clip to drawing","convert drawing to clip","size transform from clip","create shadow from clip","shad -> xshad+yshad","create 3D effect from shadow","convert strikeout to selected","move colour tag to first block","back and forth transform","chequerboard clip","select overlaps","split line in 3 parts"} +hh1={ + {x=0,y=0,class="label",label=" HYDRA "..script_version}, + + {x=0,y=1,class="checkbox",name="k1",label="Primary:"}, + {x=1,y=1,class="coloralpha",name="c1"}, + {x=0,y=2,class="checkbox",name="k3",label="Bor&der:"}, + {x=1,y=2,class="coloralpha",name="c3"}, + {x=0,y=3,class="checkbox",name="k4",label="Shado&w:"}, + {x=1,y=3,class="coloralpha",name="c4"}, + {x=0,y=4,class="checkbox",name="k2",label="useless... (2c):"}, + {x=1,y=4,class="coloralpha",name="c2"}, + {x=0,y=5,class="checkbox",name="alfas",label="Include alphas", + hint="Include alphas from colours pickers.\nRequires Aegisub r7993 or higher."}, + {x=1,y=5,class="checkbox",name="aonly",label="only",hint="Use only alphas, not colours"}, + {x=0,y=6,class="checkbox",name="italix",label="Italics"}, + {x=1,y=6,class="checkbox",name="bolt",label="Bold"}, + {x=0,y=7,class="checkbox",name="under",label="Underline"}, + {x=1,y=7,class="checkbox",name="strike",label="Strike"}, + + {x=2,y=0,class="checkbox",name="bord1",label="\\bord"}, + {x=3,y=0,width=2,class="floatedit",name="bord2",value=0}, + {x=2,y=1,class="checkbox",name="shad1",label="\\shad"}, + {x=3,y=1,width=2,class="floatedit",name="shad2",value=0}, + {x=2,y=2,class="checkbox",name="fs1",label="\\fs"}, + {x=3,y=2,width=2,class="floatedit",name="fs2",value=50}, + {x=2,y=3,class="checkbox",name="spac1",label="\\f&sp"}, + {x=3,y=3,width=2,class="floatedit",name="spac2",value=1}, + {x=2,y=4,class="checkbox",name="blur1",label="\\&blur"}, + {x=3,y=4,width=2,class="floatedit",name="blur2",value=0.5}, + {x=2,y=5,class="checkbox",name="be1",label="\\be"}, + {x=3,y=5,width=2,class="floatedit",name="be2",value=1}, + {x=2,y=6,class="checkbox",name="fscx1",label="\\fscx"}, + {x=3,y=6,width=2,class="floatedit",name="fscx2",value=100}, + {x=2,y=7,class="checkbox",name="fscy1",label="\\fscy"}, + {x=3,y=7,width=2,class="floatedit",name="fscy2",value=100}, + {x=2,y=8,class="checkbox",name="fsc1",label="\\&fsc",hint="both fscx and fscy"}, + {x=3,y=8,width=2,class="floatedit",name="fsc2",value=100}, + + {x=5,y=0,class="checkbox",name="xbord1",label="\\xbord"}, + {x=6,y=0,width=2,class="floatedit",name="xbord2"}, + {x=5,y=1,class="checkbox",name="ybord1",label="\\ybord"}, + {x=6,y=1,width=2,class="floatedit",name="ybord2"}, + {x=5,y=2,class="checkbox",name="xshad1",label="\\&xshad"}, + {x=6,y=2,width=2,class="floatedit",name="xshad2"}, + {x=5,y=3,class="checkbox",name="yshad1",label="\\&yshad"}, + {x=6,y=3,width=2,class="floatedit",name="yshad2"}, + {x=5,y=4,class="checkbox",name="fax1",label="\\fax"}, + {x=6,y=4,width=2,class="floatedit",name="fax2",value=0.05}, + {x=5,y=5,class="checkbox",name="fay1",label="\\fay"}, + {x=6,y=5,width=2,class="floatedit",name="fay2",value=0.05}, + {x=5,y=6,class="checkbox",name="frx1",label="\\frx"}, + {x=6,y=6,width=2,class="floatedit",name="frx2"}, + {x=5,y=7,class="checkbox",name="fry1",label="\\fry"}, + {x=6,y=7,width=2,class="floatedit",name="fry2"}, + {x=5,y=8,class="checkbox",name="frz1",label="\\fr&z"}, + {x=6,y=8,width=2,class="floatedit",name="frz2"}, + + {x=1,y=8,class="checkbox",name="q2",label="\\&q2"}, + {x=0,y=8,class="checkbox",name="glo",label="Global fade",hint="global fade - IN on first line, OUT on last line"}, + {x=0,y=9,class="checkbox",name="fade",label="\\fad (in,out)"}, + {x=1,y=9,width=2,class="floatedit",name="fadin",min=0,hint="fade in"}, + {x=3,y=9,width=2,class="floatedit",name="fadout",min=0,hint="fade out"}, + + {x=0,y=10,class="label",label="Additional tags:"}, + {x=1,y=10,width=5,class="edit",name="moretags",value="\\"}, + {x=6,y=10,class="checkbox",name="show",label="show ",hint="Show the tags being applied.\nNote: only transformable tags."}, + {x=7,y=10,class="checkbox",name="reuse",label="&reuse ",hint="Reuse the (transformable) tags from last run.\nCan be used with a different button\nor 'apply to' restriction."}, + {x=1,y=0,class="checkbox",name="rem",label="rem",hint="Remember last"}, + {x=5,y=9,width=3,class="edit",name="lastags",value=ortags or "",hint="last used tags will appear here"}, +} + +hh2={ + {x=8,y=0,class="label",name="startmode",label="Start mode"}, + {x=9,y=0,class="dropdown",name="smode",items={"1","2","3"},value="1"}, + {x=8,y=1,class="checkbox",name="arfa",label="\\&alpha"}, + {x=9,y=1,class="dropdown",name="alpha",items=alfas,value="00"}, + {x=8,y=2,class="checkbox",name="arf1",label="\\1a"}, + {x=9,y=2,class="dropdown",name="alph1",items=alfas,value="00"}, + {x=8,y=3,class="checkbox",name="arf2",label="\\2a"}, + {x=9,y=3,class="dropdown",name="alph2",items=alfas,value="00"}, + {x=8,y=4,class="checkbox",name="arf3",label="\\3a"}, + {x=9,y=4,class="dropdown",name="alph3",items=alfas,value="00"}, + {x=8,y=5,class="checkbox",name="arf4",label="\\4a"}, + {x=9,y=5,class="dropdown",name="alph4",items=alfas,value="00"}, + {x=8,y=6,class="checkbox",name="an1",label="\\an"}, + {x=9,y=6,class="dropdown",name="an2",items={"1","2","3","4","5","6","7","8","9"},value="5"}, + {x=8,y=7,width=2,class="label",label="Add with each line:"}, + {x=8,y=8,width=2,class="floatedit",name="add",hint="add with each line\nworks with regular tags,\ni.e. the middle two columns"}, +} + +hh2b={ + {x=8,y=0,class="label",name="startmode",label="Start mode"}, + {x=9,y=0,class="dropdown",name="smode",items={"1","2","3"},value="1"}, + {x=8,y=1,class="checkbox",name="arfa",label="\\&alpha"}, + {x=9,y=1,class="dropdown",name="alpha",items=alfas,value="00"}, + {x=8,y=7,width=2,class="label",label="Add with each line:"}, + {x=8,y=8,width=2,class="floatedit",name="add",hint="add with each line\nworks with regular tags,\ni.e. the middle two columns"}, +} + +hh3={ + {x=0,y=11,class="label",label="Tag position*:"}, + {x=1,y=11,width=5,class="edit",name="linetext",value=linetext,hint="Place an asterisk where you want the tags."}, + {x=6,y=11,width=2,class="dropdown",name="tagpres",items=preset_list,value="--- presets ---",hint="presets/options for tag position"}, + + {x=0,y=12,class="label",label="Transform t1,t2:"}, + {x=1,y=12,width=2,class="floatedit",name="trin"}, + {x=3,y=12,width=2,class="floatedit",name="trout"}, + {x=5,y=12,width=3,class="checkbox",name="tend",label="Count times from end"}, + + {x=0,y=13,class="label",label="Transform mode:"}, + {x=1,y=13,width=2,class="dropdown",name="tmode",items={"normal","add2first","add2all","all tag blocks"},value="normal",hint="new \\t | add to first \\t | add to all \\t | add to all {\\tag blocks}"}, + {x=3,y=13,width=2,class="checkbox",name="relative",label="Relative transform", + hint="Example:\ntag: \\frz30\ninput: 60\nresult: \\t(\\frz90)"}, + {x=5,y=13,class="label",label=" Accel:"}, + + {x=0,y=14,class="label",label="Shift times/interval:"}, + {x=1,y=14,width=2,class="floatedit",name="int",value=0,hint="'normal' transform: shift \\t times each line\n'all tag blocks': shift \\t times each block\n'back and forth transform': interval"}, + {x=6,y=13,width=2,class="floatedit",name="accel",value=1,min=0,hint="<1 starts fast, >1 starts slow"}, + + {x=3,y=14,class="label",label="Special functions:"}, + {x=4,y=14,width=4,class="dropdown",name="spec",items=spec_list,value="convert clip <-> iclip"}, + + {x=0,y=15,class="label",label="Gradient:"}, + {x=1,y=15,width=2,class="dropdown",name="gtype",items={"vertical","horizontal","by character","by line"},value="by character", + hint="gradient from current values to selected values\nvertical/horizontal requires clip\nworks with accel"}, + {x=3,y=15,width=2,class="floatedit",name="stripe",value=2,min=0,hint="width of clip stripes for vertical/horizontal gradient"}, + {x=5,y=15,class="label",label="pxl/stripe"}, + {x=6,y=15,class="checkbox",name="short",label="Shorter",hint="rotate in shorter direction;\ngradient hue in shorter direction"}, + {x=7,y=15,class="checkbox",name="last",label="Last",hint="take end values for gradient by line from last line if possible"}, + {x=8,y=15,class="checkbox",name="middle",label="Centred",hint="selected tags will be in the middle;\nend will be same as beginning"}, + {x=9,y=15,class="checkbox",name="hsl",label="HSL"}, + + {x=8,y=9,class="label",label="Apply to:"}, + {x=9,y=9,class="checkbox",name="exc",label="excpt",hint="all except selected\n\n('All' still applies to all)"}, + {x=8,y=10,width=2,class="dropdown",name="applay",items=app_lay,value="All Layers"}, + {x=8,y=11,width=2,class="dropdown",name="applst",items=app_sty,value="All Styles"}, + {x=8,y=12,width=2,class="dropdown",name="applac",items=app_act,value="All Actors"}, + {x=8,y=13,width=2,class="dropdown",name="applef",items=app_eff,value="All Effects"}, + {x=8,y=14,width=2,class="edit",name="appltx",value="Text..."}, +} + B3={"Apply","Transform","Gradient","Repeat Last","Special","Save Config","Help","Cancel"} + B4={"Apply","Transform","Gradient","Repeat Last","Special","Save Config","Switch","Cancel"} + B5={"Apply","Transform","Gradient","clip2size","shad2xyshad","clip2shad","strike2tags","Cancel"} + buttons={{"Apply","Transform","Repeat Last","Load Medium","Load Full","Cancel"}, + {"Apply","Transform","Repeat Last","Load Full","Cancel"},B3,B4} + loadconfig() + local heads=sm*2+1 + hh_gui=hh1 loaded=sm + progress(string.format("Loading Hydra Heads 1-"..heads)) + if sm==2 then for i=1,#hh2 do l=hh2[i] table.insert(hh_gui,l) end loaded=2 end + if sm==3 then for i=1,#hh2 do l=hh2[i] table.insert(hh_gui,l) end for i=1,#hh3 do l=hh3[i] table.insert(hh_gui,l) end end + if sm==4 then for i=1,#hh2b do l=hh2b[i] table.insert(hh_gui,l) end for i=1,#hh3 do l=hh3[i] table.insert(hh_gui,l) end end + hh_buttons=buttons[sm] + P,res=ADD(hh_gui,hh_buttons,{ok='Apply',cancel='Cancel'}) + + if P=="Load Medium" then progress("Loading Heads 4-5") + for key,val in ipairs(hh_gui) do val.value=res[val.name] end + for i=1,#hh2 do l=hh2[i] table.insert(hh_gui,l) end loaded=2 + P,res=ADD(hh_gui,buttons[2],{ok='Apply',cancel='Cancel'}) + end + + if P=="Load Full" then progress("Loading Heads "..(loaded+1)*2 .."-7") + for key,val in ipairs(hh_gui) do val.value=res[val.name] end + if loaded<2 then for i=1,#hh2 do l=hh2[i] table.insert(hh_gui,l) end end + for i=1,#hh3 do l=hh3[i] table.insert(hh_gui,l) end loaded=3 + P,res=ADD(hh_gui,buttons[3],{ok='Apply',cancel='Cancel'}) + end + + if P=="Help" then progress("Loading Head 8") + for key,val in ipairs(hh_gui) do val.value=res[val.name] end + hhh={x=0,y=16,width=10,class="dropdown",name="herp",items={"HELP (scroll/click to read)", + "Standard mode: check tags, set values, click on 'Apply'.", + "Transform mode normal: check tags, set values, set t1/t2/accel if needed, click on 'Transform'.", + "Transform mode add2first: the transforms will be added to the first existing transform in the line.", + "Transform mode add2all: the transforms will be added to all existing transforms in the line.", + "Transform mode 'all tag blocks': the transforms will be added to all tag blocks, whether they have transforms or not.", + "Shift times/interval: 'normal' tf - shift \\t times each line; 'all tag blocks' - shift \\t times each tag block.", + "Relative transform: If you have frz30 and set transform to frz60, you get \\t(\\frz90), or transform BY 60.", + "Relative transform: This allows you to keep layers with different frz in sync. (No effect on alpha/colours.)", + "Additional tags: type any extra tags you want to add.", + "Add with each line: '2' means that for each line, the value of all checked applicable tags is increased by an extra 2.", + "Tag position: This shows the text of your first line. Type * where you want your tags to go.", + "Tag position presets: This places tags in specified positions, proportionally for each selected line.", + "Gradient: Current state is the start. Given tags are the end. Accel works with this. Vertical/horizontal need a clip.", + "Centred gradient: 'There and back.' Given state will be in the middle. Last line/character will be the same as the first.", + "Special functions: select a function, click on 'Special'.", + "Special functions - back and forth transform: select tags, set 'interval'. Missing initial tags are taken from style.", + "Special functions - create 3D effect from shadow: creates a 3D effect using layers. Requires xshad/yshad.", + "Special functions - split line in 3 parts: uses t1 and t2 as time markers.", + },value="HELP (scroll/click to read)"} + table.insert(hh_gui,hhh) + P,res=ADD(hh_gui,{"Apply","Transform","Gradient","Repeat Last","Special","Save Config","More Help","Cancel"},{ok='Apply',cancel='Cancel'}) + end + + if P=="Switch" then + for key,val in ipairs(hh_gui) do val.value=res[val.name] end + P,res=ADD(hh_gui,B5,{ok='Apply',cancel='Cancel'}) + if P=="clip2size" then res.spec="size transform from clip" P="Special" end + if P=="shad2xyshad" then res.spec="shad -> xshad+yshad" P="Special" end + if P=="clip2shad" then res.spec="create shadow from clip" P="Special" end + if P=="strike2tags" then res.spec="convert strikeout to selected" P="Special" end + end + + if res.tmode=="normal" then tmode=1 end + if res.tmode=="add2first" then tmode=2 end + if res.tmode=="add2all" then tmode=3 end + if res.tmode=="all tag blocks" then tmode=4 end + if not tmode then tmode=1 end + res.accel=res.accel or 1 + if res.tagpres=="in the middle" then fak=0.5 end + if loaded>2 and res.tagpres:match("of text") then fa,fb=res.tagpres:match("(%d)/(%d)") fak=fa/fb end + if res.aonly then res.alfas=true end + + if P~="Repeat Last" then hydralast=res Plast=P end + if P=="More Help" then hydrahelp() end + if P=="Apply" then selcheck() trans=0 hh9(subs,sel) end + if P=="Transform" then selcheck() trans=1 hh9(subs,sel) end + if P=="Gradient" then selcheck() hydradient(subs,sel) end + if P=="Special" then sel=special(subs,sel) end + if P=="Save Config" then saveconfig() end + if P=="Repeat Last" then res=hydralast + if not res then t_error("Nothing to repeat",1) end + if Plast=="Gradient" then hydradient(subs,sel) + elseif Plast=="Special" then sel=special(subs,sel) + else hh9(subs,sel) end + end + return sel +end + +function col2first(subs,sel) +res=res or {} +res.spec="move colour tag to first block" +spec_macros(subs,sel) +end + +function sortags(subs,sel) +res=res or {} +res.spec="sort tags in set order" +spec_macros(subs,sel) +end + +function i_clip(subs,sel) +res=res or {} +res.spec="convert clip <-> iclip" +spec_macros(subs,sel) +end + +function shad2xy(subs,sel) +res=res or {} +res.spec="shad -> xshad+yshad" +spec_macros(subs,sel) +end + +function clip2shad(subs,sel) +res=res or {} +res.spec="create shadow from clip" +spec_macros(subs,sel) +end + +function clip2mask(subs,sel) +res=res or {} +res.spec="convert clip to drawing" +spec_macros(subs,sel) +end + +function mask2clip(subs,sel) +res=res or {} +res.spec="convert drawing to clip" +spec_macros(subs,sel) +end + +function spec_macros(subs,sel) +cuts() +loaded=1 +failures={} +success=0 +special(subs,sel) +return sel +end + +function arrowshift(subs,sel) + for x,i in ipairs(sel) do + local l=subs[i] + local t=l.text + local back + if t:match'{switch}$' then back=true else back=false end + t=t:gsub("(\\t)(%b())",function(a,b) return a..b:gsub("\\","/") end):gsub("\\([Nnh])","/%1") + if t:match('>\\') then + if back then t=t:gsub("^([^\\]*)>","%1"):gsub("(\\[^\\]*)>",">%1") + else t=t:gsub(">(\\[^\\]*)","%1>"):gsub(">$","") end + else + if back then t=t:gsub("(\\[^\\]+)$",">%1"):gsub("^>","") + else t=t:gsub("^([^\\]*)","%1>"):gsub(">$","") end + end + t=t:gsub("(\\t)(%b())",function(a,b) return a..b:gsub("/","\\") end):gsub("/([Nnh])","\\%1") + if t~=l.text then + l.text=t + subs[i]=l + end + end +end + +function shapeshifter(subs,sel,sh) + Sh="{"..sh.."}" + for x,i in ipairs(sel) do + local l=subs[i] + local t=l.text + local mark="( *%S+ *)" + if l.effect:match(sh) or l.comment or t:match'{switch}$' then mark="(.)" end + if not t:match '\\p1' then + t=t:gsub("%b{}",function(b) return b:gsub(" ","_SP_") end) + if t:match(Sh) then + t=t:gsub(Sh.."(%b{})","%1"..Sh):gsub(Sh..mark,"%1"..Sh):gsub(Sh.."$","") + else + t,c=t:gsub("^(%b{}"..mark..")","%1"..Sh) + if c==0 then t=t:gsub(mark,"%1"..Sh,1) end + t=t:gsub(Sh.."$","") + end + t=t:gsub("%b{}",function(b) return b:gsub("_SP_"," ") end) + end + if t~=l.text then + l.text=t + subs[i]=l + end + end +end + +function bellshift(subs,sel) + shapeshifter(subs,sel,"•") + return sel +end + +function waveshift(subs,sel) + shapeshifter(subs,sel,"~") + return sel +end + +function comment(subs,sel) + for x,i in ipairs(sel) do + line=subs[i] + line.comment=not line.comment + subs[i]=line + end + return sel +end + +if haveDepCtrl then + depRec:registerMacros({ + {script_name,script_description,hydra}, + {": HELP : / HYDRA","HYDRA",hydrahelp}, + {": Non-GUI macros :/HYDRA: Sort tags in set order","Sort tags in set order",sortags}, + {": Non-GUI macros :/HYDRA: Move colour tag to 1st block","Move colour tag to 1st block",col2first}, + {": Non-GUI macros :/HYDRA: Convert clip <-> iclip","Convert clip <-> iclip",i_clip}, + {": Non-GUI macros :/HYDRA: shad -> xshad+yshad","shad -> xshad+yshad",shad2xy}, + {": Non-GUI macros :/HYDRA: Create shadow from clip","Create shadow from clip",clip2shad}, + {": Non-GUI macros :/HYDRA: Convert clip to drawing","Convert clip to drawing",clip2mask}, + {": Non-GUI macros :/HYDRA: Convert drawing to clip","Convert drawing to clip",mask2clip}, + {": Non-GUI macros :/HYDRA: Bell Shifter","Shifts Bell",bellshift}, + {": Non-GUI macros :/HYDRA: Wave Shifter","Shifts Tilde",waveshift}, + {": Non-GUI macros :/HYDRA: Arrow Shifter","Shifts '>'",arrowshift}, + {": Non-GUI macros :/HYDRA: Comment on-off","Comments/uncomments lines",comment}, + },false) +else + aegisub.register_macro(script_name,script_description,hydra) + aegisub.register_macro(": HELP : / HYDRA","HYDRA",hydrahelp) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Sort tags in set order","Sort tags in set order",sortags) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Move colour tag to 1st block","Move colour tag to 1st block",col2first) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Convert clip <-> iclip","Convert clip <-> iclip",i_clip) + aegisub.register_macro(": Non-GUI macros :/HYDRA: shad -> xshad+yshad","shad -> xshad+yshad",shad2xy) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Create shadow from clip","Create shadow from clip",clip2shad) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Convert clip to drawing","Convert clip to drawing",clip2mask) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Convert drawing to clip","Convert drawing to clip",mask2clip) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Bell Shifter","Shifts Bell",bellshift) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Wave Shifter","Shifts Tilde",waveshift) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Arrow Shifter","Shifts '>'",arrowshift) + aegisub.register_macro(": Non-GUI macros :/HYDRA: Comment on-off","Comments/uncomments lines",comment) +end \ No newline at end of file diff --git a/automation/autoload/ua.ScriptCleanup.lua b/automation/autoload/ua.ScriptCleanup.lua new file mode 100644 index 000000000..a5b590374 --- /dev/null +++ b/automation/autoload/ua.ScriptCleanup.lua @@ -0,0 +1,758 @@ +-- Disclaimer: RTFM! - http://unanimated.hostfree.pw/ts/scripts-manuals.htm#cleanup + +script_name="Script Cleanup" +script_description="Garbage disposal and elimination of incriminating evidence" +script_author="unanimated" +script_version="5.0" +script_namespace="ua.ScriptCleanup" + +local haveDepCtrl,DependencyControl,depRec=pcall(require,"l0.DependencyControl") +if haveDepCtrl then + script_version="5.0.0" + depRec=DependencyControl{feed="https://raw.githubusercontent.com/unanimated/luaegisub/master/DependencyControl.json"} +end + +dont_delete_empty_tags=false -- option to not delete {} + +re=require'aegisub.re' + +function cleanlines(subs,sel) + if res.all then + for k,v in ipairs(GUI) do + if v.x==0 then res[v.name]=true end + end + end + for z,i in ipairs(sel) do + progress("Processing line: "..z.."/"..#sel) + prog=math.floor(z/#sel*100) + aegisub.progress.set(prog) + line=subs[i] + text=line.text + stl=line.style + + if res.nots and not res.nocom then text=text:gsub("{TS[^}]*} *","") end + + if res.nocom then + text=text:gsub("{[^\\}]-}","") + :gsub("{[^\\}]-\\N[^\\}]-}","") + :gsub("^({[^}]-}) *","%1") + :gsub(" *$","") + end + + if res.clear_a then line.actor="" end + if res.clear_e then line.effect="" end + + if res.layers and line.layer<5 then + if stl:match("Defa") or stl:match("Alt") or stl:match("Main") then line.layer=line.layer+5 end + end + + if res.cleantag and text:match("{[*>]?\\") then + txt2=text + text=text:gsub("{\\\\k0}",""):gsub(">\\","\\"):gsub("{(\\[^}]-)} *\\N *{(\\[^}]-)}","\\N{%1%2}") + repeat text,r=text:gsub("{(\\[^}]-)}{(\\[^}]-)}","{%1%2}") until r==0 + text=text:gsub("({\\[^}]-){(\\[^}]-})","%1%2"):gsub("{.-\\r","{\\r"):gsub("^{\\r([\\}])","{%1") + :gsub("\\fad%(0,0%)",""):gsub(ATAG.."$",""):gsub("^({\\[^}]-)\\frx0\\fry0","%1"):gsub("\\%a+%(%)","") + text=text:gsub(ATAG,function(tgs) + tgs2=tgs + :gsub("\\+([\\}])","%1") + :gsub("(\\[^\\})]+)",function(a) if not a:match'clip' and not a:match'\\fn' and not a:match'\\r' then a=a:gsub(' ','') end return a end) + :gsub("(\\%a+)([%d%-]+%.%d+)",function(a,b) if not a:match("\\fn") then b=rnd2dec(b) end return a..b end) + :gsub("(\\%a+)%(([%d%.%-]+),([%d%.%-]+)%)",function(a,b,c) b=rnd2dec(b) c=rnd2dec(c) return a.."("..b..","..c..")" end) + :gsub("(\\%a+)%(([%d%.%-]+),([%d%.%-]+),([%d%.%-]+),([%d%.%-]+)",function(a,b,c,d,e) + return a.."("..rnd2dec(b)..","..rnd2dec(c)..","..rnd2dec(d)..","..rnd2dec(e) end) + tgs2=duplikill(tgs2) + tgs2=extrakill(tgs2) + return tgs2 + end) + if txt2~=text then kleen=kleen+1 end + end + + if res.overlap then + if line.comment==false and stl:match("Defa") then + start=line.start_time + endt=line.end_time + if i<#subs then nextline=subs[i+1] nextart=nextline.start_time end + prevline=subs[i-1] + prevstart=prevline.start_time + prevend=prevline.end_time + dur=line.end_time-line.start_time + ms2fr=aegisub.frame_from_ms + fr2ms=aegisub.ms_from_frame + keyframes=aegisub.keyframes() + startf=ms2fr(start) + endf=ms2fr(endt) + prevendf=ms2fr(prevend) + nextartf=ms2fr(nextart) + + -- start gaps/overlaps + if prevline.class=="dialogue" and prevline.style:match("Defa") and dur>50 then + -- get keyframes + kfst=0 kfprev=0 + for k,kf in ipairs(keyframes) do + if kf==startf then kfst=1 end + if kf==prevendf then kfprev=1 end + end + -- start overlap + if startprevend and start-prevend<=50 then + if kfst==0 and kfprev==1 then nstart=prevend end + end + end + -- end gaps/overlaps + if i<#subs and nextline.style:match("Defa") and dur>50 then + --get keyframes + kfend=0 kfnext=0 + for k,kf in ipairs(keyframes) do + if kf==endf then kfend=1 end + if kf==nextartf then kfnext=1 end + end + -- end overlap + if endt>nextart and endt-nextart<=50 then + if kfnext==1 and kfend==0 then nendt=nextart end + end + -- end gap + if endt4 and v.y>0 and v.name~="onlyt" then res[v.name]=not res[v.name] end + end + end + for z,i in ipairs(sel) do + progress("Processing line: "..z.."/"..#sel) + line=subs[i] + text=line.text + if res.onlyt then res.trans=false + text=text:gsub(ATAG,function(t) return t:gsub("\\","|") end) + :gsub("|t(%b())",function(t) return "\\t"..t:gsub("|","\\") end) + end + tags=text:match(STAG) or "" + inline=text:gsub(STAG,"") + if res.skill and res.ikill then trgt=text tg=3 + elseif res.ikill then trgt=inline tg=2 + else trgt=tags tg=1 end + if res.border then trgt=killtag("[xy]?bord",trgt) end + if res.shadow then trgt=killtag("shad",trgt) end + if res.blur then trgt=killtag("blur",trgt) end + if res.bee then trgt=killtag("be",trgt) end + if res.fsize then trgt=killtag("fs",trgt) end + if res.fspace then trgt=killtag("fsp",trgt) end + if res.scalex then trgt=killtag("fscx",trgt) end + if res.scaley then trgt=killtag("fscy",trgt) end + if res.fade then trgt=trgt:gsub("\\fade?%b()","") end + if res.posi then trgt=trgt:gsub("\\pos%b()","") end + if res.move then trgt=trgt:gsub("\\move%b()","") end + if res.org then trgt=trgt:gsub("\\org%b()","") end + if res.color1 then trgt=killctag("1?c",trgt) end + if res.color2 then trgt=killctag("2c",trgt) end + if res.color3 then trgt=killctag("3c",trgt) end + if res.color4 then trgt=killctag("4c",trgt) end + if res.alfa1 then trgt=killctag("1a",trgt) end + if res.alfa2 then trgt=killctag("2a",trgt) end + if res.alfa3 then trgt=killctag("3a",trgt) end + if res.alfa4 then trgt=killctag("4a",trgt) end + if res.alpha then trgt=killctag("alpha",trgt) end + if res.clip then trgt=trgt:gsub("\\i?clip%b()","") end + if res.fname then trgt=trgt:gsub("\\fn[^\\}]+","") end + if res.frz then trgt=killtag("frz",trgt) end + if res.frx then trgt=killtag("frx",trgt) end + if res.fry then trgt=killtag("fry",trgt) end + if res.fax then trgt=killtag("fax",trgt) end + if res.fay then trgt=killtag("fay",trgt) end + if res.anna then trgt=killtag("an",trgt) end + if res.align then trgt=killtag("a",trgt) end + if res.wrap then trgt=killtag("q",trgt) end + if res["return"] then trgt=trgt:gsub("\\r.+([\\}])","%1") end + if res.kara then trgt=trgt:gsub("\\[Kk][fo]?[%d%.]+([\\}])","%1") end + if res.ital then repeat trgt,r=trgt:gsub("\\i[01]?([\\}])","%1") until r==0 end + if res.bold then repeat trgt,r=trgt:gsub("\\b[01]?([\\}])","%1") until r==0 end + if res.under then repeat trgt,r=trgt:gsub("\\u[01]?([\\}])","%1") until r==0 end + if res.stri then repeat trgt,r=trgt:gsub("\\s[01]?([\\}])","%1") until r==0 end + if res.trans then trgt=trgt:gsub("\\t%b()","") end + trgt=trgt:gsub("\\t%([%d%.,]*%)","") :gsub("{%**}","") + if tg==1 then tags=trgt elseif tg==2 then inline=trgt elseif tg==3 then text=trgt end + if trgt~=text then text=tags..inline end + if res.onlyt then text=text:gsub("{%*?|[^}]-}",function(t) return t:gsub("|","\\") end) end + line.text=text + subs[i]=line + end +end + +function killtag(tag,t) repeat t,r=t:gsub("\\"..tag.."[%d%.%-]-([\\}])","%1") until r==0 return t end +function killctag(tag,t) t=t:gsub("\\"..tag.."&H%x+&","") repeat t,r=t:gsub("\\"..tag.."([\\}])","%1") until r==0 return t end + + +-- hide tags +function hide_tags(subs,sel) + hide=true + if res.inverse then hide=nil end + local numbers="\\i\\b\\u\\s\\q\\a\\be\\blur\\bord\\fs\\fscx\\fscy\\shad\\an\\frz\\fry\\frx\\fsp\\fax\\fay\\" + local alphacol="\\1a\\2a\\3a\\4a\\1c\\2c\\3c\\4c\\alpha\\" + local parent="\\fad\\pos\\move\\org\\clip\\" + local fontret="\\r\\fn\\" + if hide then + hidem={} + for k,v in ipairs(GUI) do + if v.x>4 and v.y>0 and v.name~='onlyt' and v.name~='kara' then + nom=v.label:gsub("c, 1c","1c"):gsub("%(i%)","") + if res[v.name] then table.insert(hidem,nom) end + end + end + end + + for x,i in ipairs(sel) do + line=subs[i] + text=line.text:gsub("\\c&","\\1c&") + startg=text:match("^{\\[^}]-}") or "" + startg=trem(startg) + t2=text:gsub("^{\\[^}]-}","") + + if hide then + for t=1,#hidem do + local tag='\\'..hidem[t] + local htag='//'..hidem[t] + local chk=tag..'\\' + tg=nil + if numbers:match(chk) then + tg=startg:match(tag.."([%d.-]+)") + if tg then t2=t2.."{"..htag..tg.."}" end + startg=startg:gsub(tag.."[%d.-]+","") + if not tg and tag=="\\shad" then + tg1=startg:match("\\xshad([%d.-]+)") + tg2=startg:match("\\yshad([%d.-]+)") + if tg1 then t2=t2.."{//xshad"..tg1.."}" end + if tg2 then t2=t2.."{//yshad"..tg2.."}" end + startg=startg:gsub("\\[xy]shad[%d.-]+","") + end + elseif alphacol:match(chk) then + tg=startg:match(tag.."(&H%x+&)") + if tg then t2=t2.."{"..htag..tg.."}" end + startg=startg:gsub(tag.."&H%x+&","") + elseif parent:match(chk) then + tg=startg:match(tag.."(%b())") + if not tg and tag=="\\clip" then tg=startg:match("\\iclip(%b())") tag='\\iclip' htag='//iclip' end + if not tg and tag=="\\fad" then tg=startg:match("\\fade(%b())") tag='\\fade' htag='//fade' end + if tg then t2=t2.."{"..htag..tg.."}" end + startg=startg:gsub(tag.."%b()","") + elseif fontret:match(chk) then + tg=startg:match(tag.."([^\\}]*)") + if tg then t2=t2.."{"..htag..tg.."}" end + startg=startg:gsub(tag.."[^\\}]*","") + elseif tag=='\\t' then + t2=t2.."{"..trnsfrm:gsub("\\t","//t").."}" + end + end + if res.hidline then + -- hide inline + inT=inline_pos(t2) + t2=t2:gsub(ATAG,"") + for k,v in ipairs(inT) do + t2=t2..v.t:gsub("{%*?\\","{"..v.n.."//") + end + end + else + local vis1,vis2=t2:gsub("%b{}","") + local c=0 + repeat + -- Unhide regular + if res.skill then + for hidden in t2:gmatch("{(//.-)}") do + t2=t2:gsub("{"..esc(hidden).."}","") + hidden=hidden:gsub("//","\\") + startg=startg.."{"..hidden.."}" + end + end + -- unhide inline + if t2:match("{%d+//[^}]+}") and res.ikill then + inT={} + stT='' + for num,tag in t2:gmatch("{(%d+)//([^}]+)}") do + table.insert(inT,{n=num,t=tag}) + end + for tag in t2:gmatch("{//[^}]+}") do stT=stT..tag end + table.sort(inT,function(a,b) return tonumber(a.n) '..vis2) end + end + + if not hide or hide and not res.trans then startg=startg.."{"..trnsfrm.."}" end + text=duplikill(startg:gsub("}{",""):gsub("\\1c","\\c"))..t2 + text=text:gsub("{}",""):gsub("\\1c","\\c") + if line.text~=text then line.text=text subs[i]=line end + end + return sel +end + +-- save inline tags +function inline_pos(t) + inTags={} + tl=t:len() + if tl==0 then return {} end + p=0 + t1='' + repeat + seg=t:match("^(%b{})") -- try to match tags/comments + if seg then + if seg:match("{%*?\\") then table.insert(inTags,{n=p,t=seg}) end + else + seg=t:match("^([^{]+)") -- or match text + if not seg then t_error("Error: There appears to be a problem with the brackets here...\n"..t1..t,1) end + SL=re.find(seg,".") + p=p+#SL -- position of next '{' [or end] + end + t1=t1..seg + t=t:gsub("^"..esc(seg),"") + tl=t:len() + until tl==0 + return inTags +end + +-- rebuild inline tags +function inline_ret2(t,tab) + tl=t:len() + nt='' + kill='_Z#W_' -- this is supposed to never match + for k,v in ipairs(tab) do + N=tonumber(v.n) + if N==0 then nt=nt..v.t + else + m='.' + -- match how many chars at the start + m=m:rep(N) + RS=re.find(t,m) + seg=RS[1].str + seg=re.sub(seg,'^'..kill,'') + nt=nt..seg..'{\\'..v.t..'}' + kill=m -- how many matched in the last round + end + end + -- the rest + seg=re.sub(t,'^'..kill,'') + nt=nt..seg + return nt +end + +-- reanimatools ------- +function esc(str) str=str:gsub("[%%%(%)%[%]%.%-%+%*%?%^%$]","%%%1") return str end +function rnd2dec(num) num=math.floor((num*100)+0.5)/100 return num end +function logg(m) m=m or "nil" aegisub.log("\n "..m) end +function wrap(str) return "{"..str.."}" end +function nobra(t) return t:gsub("%b{}","") end +function nobrea(t) return t:gsub("%b{}",""):gsub("\\[Nh]","") end +function nobrea1(t) return t:gsub("%b{}",""):gsub(" *\\[Nh] *"," ") end +function tagmerge(t) repeat t,r=t:gsub("({\\[^}]-)}{(\\[^}]-})","%1%2") until r==0 return t end +function progress(msg) if aegisub.progress.is_cancelled() then ak() end aegisub.progress.title(msg) end +function t_error(message,cancel) ADD({{class="label",label=message}},{"OK"},{close='OK'}) if cancel then ak() end end + +function duplikill(tagz) + local tags1={"blur","be","bord","shad","xbord","xshad","ybord","yshad","fs","fsp","fscx","fscy","frz","frx","fry","fax","fay"} + local tags2={"c","2c","3c","4c","1a","2a","3a","4a","alpha"} + tagz=tagz:gsub("\\t%b()",function(t) return t:gsub("\\","|") end) + for i=1,#tags1 do + tag=tags1[i] + repeat tagz,c=tagz:gsub("|"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%1%2") until c==0 + repeat tagz,c=tagz:gsub("\\"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%2%1") until c==0 + end + tagz=tagz:gsub("\\1c&","\\c&") + for i=1,#tags2 do + tag=tags2[i] + repeat tagz,c=tagz:gsub("|"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%1%2") until c==0 + repeat tagz,c=tagz:gsub("\\"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%2%1") until c==0 + end + repeat tagz,c=tagz:gsub("\\fn[^\\}]+([^}]-)(\\fn[^\\}]+)","%2%1") until c==0 + repeat tagz,c=tagz:gsub("(\\[ibusq])%d(.-)(%1%d)","%2%3") until c==0 + repeat tagz,c=tagz:gsub("(\\an)%d(.-)(%1%d)","%3%2") until c==0 + tagz=tagz:gsub("(|i?clip%(%A-%))(.-)(\\i?clip%(%A-%))","%2%3") + :gsub("(\\i?clip%b())(.-)(\\i?clip%b())",function(a,b,c) + if a:match("m") and c:match("m") or not a:match("m") and not c:match("m") then return b..c else return a..b..c end end) + tagz=tagz:gsub("|","\\"):gsub("\\t%([^\\%)]-%)","") + return tagz +end + +function extrakill(text,o) + local tags3={"pos","move","org","fad"} + for i=1,#tags3 do + tag=tags3[i] + if o==2 then + repeat text,c=text:gsub("(\\"..tag.."[^\\}]+)([^}]-)(\\"..tag.."[^\\}]+)","%3%2") until c==0 + else + repeat text,c=text:gsub("(\\"..tag.."[^\\}]+)([^}]-)(\\"..tag.."[^\\}]+)","%1%2") until c==0 + end + end + repeat text,c=text:gsub("(\\pos[^\\}]+)([^}]-)(\\move[^\\}]+)","%1%2") until c==0 + repeat text,c=text:gsub("(\\move[^\\}]+)([^}]-)(\\pos[^\\}]+)","%1%2") until c==0 + return text +end + +function trem(tags) + trnsfrm="" + for t in tags:gmatch("\\t%b()") do trnsfrm=trnsfrm..t end + tags=tags:gsub("\\t%b()","") + return tags +end + +function cleantr(tags) + trnsfrm="" + zerotf="" + for t in tags:gmatch("\\t%b()") do + if t:match("\\t%(\\") then + zerotf=zerotf..t:match("\\t%((.*)%)$") + else + trnsfrm=trnsfrm..t + end + end + zerotf="\\t("..zerotf..")" + tags=tags:gsub("\\t%b()",""):gsub("^({[^}]*)}","%1"..zerotf..trnsfrm.."}"):gsub("\\t%(%)","") + return tags +end + +function retextmod(orig,text) + local v1,v2,c,t2 + v1=nobrea(orig) + c=0 + repeat + t2=textmod(orig,text) + v2=nobrea(text) + c=c+1 + until v1==v2 or c==666 + if v1~=v2 then logg("Something went wrong with the text...") logg(v1) logg(v2) end + return t2 +end + +function textmod(orig,text) +if text=="" then return orig end + tk={} + tg={} + text=text:gsub("{\\\\k0}","") + text=tagmerge(text) + vis=nobra(text) + ltrmatches=re.find(vis,".") + if not ltrmatches then logg("text: "..text..'\nvisible: '..vis) + logg("If you're seeing this, something really weird is happening with the re module.\nTry this again or rescan Autoload.") + end + for l=1,#ltrmatches do + table.insert(tk,ltrmatches[l].str) + end + stags=text:match(STAG) or "" + text=text:gsub(STAG,"") :gsub("{[^\\}]-}","") + orig=orig:gsub("{([^\\}]+)}",function(c) return wrap("\\\\"..c.."|||") end) + count=0 + for seq in orig:gmatch("[^{]-{%*?\\[^}]-}") do + chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") + pos=re.find(chars,".") + if pos==nil then ps=0+count else ps=#pos+count end + tgl={p=ps,t=tak,a=as} + table.insert(tg,tgl) + count=ps + end + count=0 + for seq in text:gmatch("[^{]-{%*?\\[^}]-}") do + chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") + pos=re.find(chars,".") + if pos==nil then ps=0+count else ps=#pos+count end + tgl={p=ps,t=tak,a=as} + table.insert(tg,tgl) + count=ps + end + newline="" + for i=1,#tk do + newline=newline..tk[i] + newt="" + for n,t in ipairs(tg) do + if t.p==i then newt=newt..t.a..t.t end + end + if newt~="" then newline=newline.."{"..as..newt.."}" end + end + newtext=stags..newline:gsub("(|||)(\\\\)","%1}{%2"):gsub("({[^}]-)\\\\([^\\}]-)|||","{%2}%1") + text=newtext:gsub("{}","") + return text +end + + +function cleanup(subs,sel,act) +ADD=aegisub.dialog.display +ak=aegisub.cancel +if #sel==0 then t_error("No selection",1) end +ATAG="{[*>]?\\[^}]-}" +STAG="^{>?\\[^}]-}" +if act==0 then act=sel[1] end +chng=0 kleen=0 +GUI={ +{x=0,y=0,class="checkbox",name="nots",label="Remove TS timecodes",hint="Removes timecodes like {TS 12:36}"}, +{x=0,y=1,class="checkbox",name="clear_a",label="Clear Actor field"}, +{x=0,y=2,class="checkbox",name="clear_e",label="Clear Effect field"}, +{x=0,y=3,class="checkbox",name="layers",label="Raise dialogue layer by 5"}, +{x=0,y=4,class="checkbox",name="cleantag",label="Clean up tags",hint="Fixes duplicates, \\\\, \\}, }{, and other garbage"}, +{x=0,y=5,class="checkbox",name="ctrans",label="Clean up transforms"}, +{x=0,y=6,class="checkbox",name="overlap",label="Fix 1-frame gaps/overlaps"}, +{x=0,y=7,class="checkbox",name="nocomline",label="Delete commented lines"}, +{x=0,y=8,class="checkbox",name="noempty",label="Delete empty lines"}, +{x=0,y=9,class="checkbox",name="alphacol",label="Try to fix alpha / colour tags"}, +{x=0,y=10,class="checkbox",name="spaces",label="Fix start/end/double spaces"}, +{x=0,y=12,class="checkbox",name="info",label="Print info"}, +{x=0,y=13,class="checkbox",name="all",label="ALL OF THE ABOVE"}, + +{x=1,y=0,class="label",label=" "}, + +{x=2,y=0,width=2,class="checkbox",name="allcol",label="Remove all colour tags"}, +{x=2,y=1,class="checkbox",name="allphas",label="Remove all alphas"}, +{x=3,y=1,class="checkbox",name="alpha14",label="Only 1a-4a"}, +{x=2,y=2,class="checkbox",name="allrot",label="Remove all rotations",hint="frx, fry, frz"}, +{x=3,y=2,class="checkbox",name="xyrot",label="Only x, y",hint="remove frx, fry"}, +{x=2,y=3,class="checkbox",name="allsize",label="Remove size/scaling",hint="fs, fscx, fscy"}, +{x=3,y=3,class="checkbox",name="scales",label="Only scaling",hint="remove fscx, fscy"}, +{x=2,y=4,class="checkbox",name="allshad",label="Remove all shadows",hint="shad, xshad, yshad"}, +{x=3,y=4,class="checkbox",name="xyshad",label="Only x, y",hint="remove xshad, yshad"}, +{x=2,y=5,class="checkbox",name="parent",label="Remove parentheses",hint="fad(e), (i)clip, pos, move, org\n(but not t)"}, +{x=3,y=5,class="checkbox",name="parent2",label="Except \\pos",hint="fad(e), (i)clip, move, org\n(but not t or pos)"}, +{x=2,y=6,width=2,class="checkbox",name="allpers",label="Remove all perspective",hint="frx, fry, frz, fax, fay, org"}, + +{x=2,y=7,width=2,class="label",label=" ~ Script Cleanup v"..script_version.." ~"}, + +{x=2,y=8,width=2,class="checkbox",name="hspace",label="Remove hard spaces - \\h"}, +{x=2,y=9,class="checkbox",name="nobreak",label="Remove line breaks"}, +{x=3,y=9,class="checkbox",name="nobreak2",label="...no space",hint="Remove line breaks, leave no spaces"}, +{x=2,y=10,class="checkbox",name="nostyle",label="Delete unused styles"}, +{x=3,y=10,class="checkbox",name="nostyle2",label="Except Def.",hint="Delete unused styles except Default"}, +{x=2,y=11,class="checkbox",name="inline",label="Remove inline tags"}, +{x=3,y=11,class="checkbox",name="inline2",label="Except last",hint="Remove inline tags except the last one"}, +{x=2,y=12,width=2,class="checkbox",name="nocom",label="Remove comments from lines",hint="Removes {comments} (not tags)"}, +{x=2,y=13,width=2,class="checkbox",name="notag",label="Remove all {\\tags} from selected lines"}, + +{x=4,y=0,height=14,class="label",label="| \n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|"}, + +{x=5,y=0,class="checkbox",name="skill",label="[start]",value=true}, +{x=6,y=0,class="checkbox",name="ikill",label="[inline]",value=true,hint="only kill, not hide"}, +{x=7,y=0,width=2,class="checkbox",name="inverse",label="[inverse/unhide]",hint="kill all except checked ones\n\n'Unhide' for 'Hide Tags'"}, +{x=7,y=1,width=2,class="checkbox",name="onlyt",label="[from \\t]",hint="remove only from transforms\n\n n/a for 'Hide Tags'"}, + +{x=5,y=1,class="checkbox",name="blur",label="blur"}, +{x=5,y=2,class="checkbox",name="border",label="bord",hint="includes xbord and ybord [not for Hide]"}, +{x=5,y=3,class="checkbox",name="shadow",label="shad",hint="includes xshad and yshad for Hide"}, +{x=5,y=4,class="checkbox",name="fsize",label="fs"}, +{x=5,y=5,class="checkbox",name="fspace",label="fsp"}, +{x=5,y=6,class="checkbox",name="scalex",label="fscx"}, +{x=5,y=7,class="checkbox",name="scaley",label="fscy"}, +{x=5,y=8,class="checkbox",name="fname",label="fn"}, +{x=5,y=9,class="checkbox",name="ital",label="i"}, +{x=5,y=10,class="checkbox",name="bold",label="b"}, +{x=5,y=11,class="checkbox",name="under",label="u"}, +{x=5,y=12,class="checkbox",name="stri",label="s"}, +{x=5,y=13,class="checkbox",name="wrap",label="q"}, + +{x=6,y=1,class="checkbox",name="bee",label="be"}, +{x=6,y=2,class="checkbox",name="color1",label="c, 1c"}, +{x=6,y=3,class="checkbox",name="color2",label="2c"}, +{x=6,y=4,class="checkbox",name="color3",label="3c"}, +{x=6,y=5,class="checkbox",name="color4",label="4c"}, +{x=6,y=6,class="checkbox",name="alpha",label="alpha"}, +{x=6,y=7,class="checkbox",name="alfa1",label="1a"}, +{x=6,y=8,class="checkbox",name="alfa2",label="2a"}, +{x=6,y=9,class="checkbox",name="alfa3",label="3a"}, +{x=6,y=10,class="checkbox",name="alfa4",label="4a"}, +{x=6,y=11,class="checkbox",name="align",label="a"}, +{x=6,y=12,class="checkbox",name="anna",label="an"}, +{x=6,y=13,class="checkbox",name="clip",label="(i)clip"}, + +{x=7,y=2,class="checkbox",name="fade",label="fad"}, +{x=7,y=3,class="checkbox",name="posi",label="pos"}, +{x=7,y=4,class="checkbox",name="move",label="move"}, +{x=7,y=5,class="checkbox",name="org",label="org"}, +{x=7,y=6,class="checkbox",name="frz",label="frz"}, +{x=7,y=7,class="checkbox",name="frx",label="frx"}, +{x=7,y=8,class="checkbox",name="fry",label="fry"}, +{x=7,y=9,class="checkbox",name="fax",label="fax"}, +{x=7,y=10,class="checkbox",name="fay",label="fay"}, +{x=7,y=11,width=2,class="checkbox",name="kara",label="k/kf/ko"}, +{x=7,y=12,class="checkbox",name="return",label="r"}, +{x=7,y=13,class="checkbox",name="trans",label="t"}, + +{x=8,y=12,height=2,class="checkbox",name="hidline",label="hide\ninline",hint='Hide ALL inline tags'}, +} + P,res=ADD(GUI, + {"Run selected","Comments","Tags","Dial 5","Clean Tags","^ Kill Tags","Hide Tags","Cancer"},{ok='Run selected',cancel='Cancer'}) + if P=="Cancer" then ak() end + if P=="^ Kill Tags" then killemall(subs,sel) end + if P=="Hide Tags" then hide_tags(subs,sel) end + if P=="Comments" then res.nocom=true cleanlines(subs,sel) end + if P=="Tags" then res.notag=true cleanlines(subs,sel) end + if P=="Dial 5" then res.layers=true cleanlines(subs,sel) end + if P=="Clean Tags" then res.cleantag=true cleanlines(subs,sel) end + if P=="Run selected" then + C=0 for key,v in ipairs(GUI) do if v.x<=3 and res[v.name] then C=1 end end + if C==0 then t_error("Run Selected: Error - nothing selected",1) end + if res.all then + for key,v in ipairs(GUI) do if v.x>0 and v.name then res[v.name]=false end end + cleanlines(subs,sel) + sel=noemptycom(subs,sel) + else cleanlines(subs,sel) + if res.nocomline and res.noempty then sel=noemptycom(subs,sel) + else + if res.nocomline then sel=nocom_line(subs,sel) end + if res.noempty then sel=noempty(subs,sel) end + end + table.sort(sel) + if res.nostyle or res.nostyle2 then sel=nostyle(subs,sel) end + end + end + if act>#subs then act=#subs end + return sel,act +end + +if haveDepCtrl then depRec:registerMacro(cleanup) else aegisub.register_macro(script_name,script_description,cleanup) end \ No newline at end of file diff --git a/automation/autoload/unkf.lua b/automation/autoload/unkf.lua new file mode 100644 index 000000000..3513f1a2e --- /dev/null +++ b/automation/autoload/unkf.lua @@ -0,0 +1,43 @@ +local tr = aegisub.gettext + +script_name = tr"unkf" +script_description = tr"replace kf/ko tags in selected lines by regular k tags" +script_author = "amoethyst" +script_version = "1.0" + + +function split_line(subs, sel) + local expr_kof = "^(.-{[^}]*\\k)[of](.*)$" + local expr_K = "^(.-{[^}]*\\)K(.*)$" + local before, after + + for _, i in ipairs(sel) do + line = subs[i] + + -- replace ko and kf tags + while true do + before, after = line.text:match(expr_kof) + if before == nil then + break + else + line.text = before .. after + end + end + + -- replace K tags + while true do + before, after = line.text:match(expr_K) + if before == nil then + break + else + line.text = before .. "k" .. after + end + end + + subs[i] = line + end + + aegisub.set_undo_point(script_name) +end + +aegisub.register_macro(script_name, script_description, split_line)