dotfiles/.aegisub/automation/autoload/ua.Significance.lua

3134 lines
124 KiB
Lua

script_name="Significance"
script_description="Import stuff, number stuff, chapter stuff, replace stuff, do a significant amount of other stuff to stuff."
script_author="unanimated"
script_version="3.5"
script_namespace="ua.Significance"
local haveDepCtrl,DependencyControl,depRec=pcall(require,"l0.DependencyControl")
if haveDepCtrl then
script_version="3.5.0"
depRec=DependencyControl{feed="https://raw.githubusercontent.com/unanimated/luaegisub/master/DependencyControl.json"}
end
clipboard=require("aegisub.clipboard")
re=require'aegisub.re'
-- Significance GUI ------------------------------------------------------------------------------------------
function significance(subs,sel,act)
ADD=aegisub.dialog.display
ADP=aegisub.decode_path
ak=aegisub.cancel
ms2fr=aegisub.frame_from_ms
fr2ms=aegisub.ms_from_frame
ATAG="{[*>]?\\[^}]-}"
STAG="^{[*>]?\\[^}]-}"
COMM="{[^\\}]-}"
aegisub.progress.title("Loading...")
datata=datata or ""
sub1=sub1 or ""
sub2=sub2 or ""
sub3=sub3 or 1
for i=1,#subs do if subs[i].class=="dialogue" then line0=i-1 break end end
msg={"If it breaks, it's your fault.","This should be doing something...","Breaking your computer. Please wait.","Unspecified operations in progress.","This may or may not work.","Trying to avoid bugs...","Zero one one zero one zero...","10110101001101101010110101101100001","I'm surprised anyone's using this","If you're seeing this for too long, it's a bad sign.","This might hurt a little.","Please wait... I'm pretending to work.","Close all your programs and run."}
rm=math.random(1,#msg) msge=msg[rm]
if lastimp then dropstuff=lastuff lok=lastlog zerozz=lastzeros fld=lastfield mod0=lastmod0
else dropstuff="replacer" lok=false zerozz="01" fld="effect" mod0="number lines" end
g_impex={"import OP","import ED","import sign","import signs","export sign","import chptrs","update lyrics"}
g_stuff={"save/load","replacer","lua calc","split text to actor/effect","reverse text","reverse words","reverse transforms","fake capitals","format dates","fill columns","split into letters (alpha)","explode","dissolve text","randomised transforms","what is the Matrix?","clone clip","clip2margins","duplicate and shift lines","extrapolate tracking","time by frames","convert framerate","transform \\k to \\t\\alpha","fix kara tags for fbf lines","make style from act. line","make comments visible","switch commented/visible","honorificslaughterhouse"}
LN1=sel[1]-line0
LN2=sel[#sel]-line0
lin=LN1.."-"..LN2
lin=lin:gsub("^(%d+)%-%1$","%1")
if LN2-LN1+1>#sel then lin=lin:gsub("-"," ... #") end
unconfig={
-- Sub --
{x=0,y=16,width=3,class="label",label="Left "},
{x=3,y=16,width=3,class="label",label="Right "},
{x=6,y=16,width=3,class="label",label="Mod "},
{x=0,y=17,width=3,class="edit",name="rep1",value=sub1},
{x=3,y=17,width=3,class="edit",name="rep2",value=sub2},
{x=6,y=17,width=3,class="edit",name="rep3",value=sub3,hint="Numbers: start/repeat[limit]\nreplacer/lua calc: limit"},
-- import
{x=9,y=3,width=2,class="label",label="Import/Export"},
{x=9,y=4,width=2,class="dropdown",name="mega",items=g_impex,value="import signs"},
{x=11,y=4,class="checkbox",name="keep",label="keep line",value=true,},
{x=9,y=5,width=3,class="checkbox",name="restr",label="style restriction (lyrics)",value=false,},
{x=9,y=6,width=3,class="edit",name="rest"},
-- chapters
{x=9,y=7,class="label",label="Chapters"},
{x=10,y=7,width=2,class="checkbox",name="intro",label="autogenerate \"Intro\"",value=true,},
{x=9,y=8,width=2,class="label",label="chapter marker:"},
{x=11,y=8,class="dropdown",name="marker",items={"actor","effect","comment"},value="actor"},
{x=9,y=9,width=2,class="label",label="chapter name:"},
{x=11,y=9,class="dropdown",name="nam",items={"comment","effect"},value="comment"},
{x=9,y=10,width=2,class="label",label="filename from:"},
{x=11,y=10,class="dropdown",name="sav",items={"script","video"},value="script"},
{x=9,y=11,width=2,class="checkbox",name="chmark",label="chapter mark:",value=false,hint="just sets the marker. no xml."},
{x=11,y=11,class="dropdown",name="chap",items={"Intro","OP","Part A","Part B","Part C","ED","Preview"},value="OP"},
{x=9,y=12,width=3,class="edit",name="lang"},
-- numbers
{x=9,y=13,width=2,class="label",label="Numbers"},
{x=9,y=14,width=2,class="dropdown",name="modzero",items={"number lines","number 12321","add to marker","zero fill","random"},value=mod0},
{x=11,y=14,class="dropdown",name="zeros",items={"1","01","001","0001"},value=zerozz},
{x=9,y=15,width=2,class="dropdown",name="field",items={"actor","effect","layer","style","text","left","right","vert","comment"},value=fld},
{x=11,y=15,class="checkbox",name="intxt",label="in text",hint="numbers found in text"},
-- stuff
{x=0,y=15,class="label",label="&Stuff "},
{x=1,y=15,width=2,class="dropdown",name="stuff",items=g_stuff,value=dropstuff}, --dropstuff
{x=3,y=15,class="dropdown",name="regex",items={"lua patterns","perl regexp"},value="perl regexp"},
{x=4,y=15,class="checkbox",name="log",label="log",value=lok,hint="provides some information for many of the functions here"},
{x=8,y=15,class="label",label="Marker:"},
-- textboxes
{x=0,y=0,width=9,height=15,class="textbox",name="dat",value=data},
{x=9,y=1,width=3,class="label",label=" Selected Lines: "..#sel.." [#"..lin.."]"},
-- help
{x=9,y=0,width=3,class="dropdown",name="help",
items={"--- Help menu ---","Import/Export","Update Lyrics","Do Stuff","Numbers","Chapters"},value="--- Help menu ---"},
{x=9,y=17,width=3,class="label",label=" Significance version: "..script_version},
}
loadconfig()
repeat
if P=="&Help" then aegisub.progress.title("Loading Help") aegisub.progress.task("RTFM")
if res.help=="Import/Export" then help=help_i end
if res.help=="Update Lyrics" then help=help_u end
if res.help=="Do Stuff" then help=help_d end
if res.help=="Numbers" then help=help_n end
if res.help=="Chapters" then help=help_c end
if res.help=="--- Help menu ---" then help="Choose something from the menu, dumbass -->" end
for key,val in ipairs(unconfig) do if val.name=="dat" then val.value=help end end
end
if P=="&Info" then aegisub.progress.title("Gathering Info") aegisub.progress.task("...") info(subs,sel,act)
for key,val in ipairs(unconfig) do if val.name=="dat" then val.value=infodump end end
end
P,res=ADD(unconfig,{"Import/Export","Do &Stuff","&Numbers","&Chapters","&Repeat Last","&Info","&Help","Save Config","Cancel"},{ok='Import/Export',cancel='Cancel'})
until P~="&Help" and P~="&Info"
if P=="Cancel" then ak() end
lastimp=true lastuff=res.stuff lastlog=res.log lastzeros=res.zeros lastfield=res.field lastmod0=res.modzero
if P=="&Repeat Last" then if not lastres then ak() end P=lastP res=lastres end
progress("Doing Stuff") aegisub.progress.task(msge)
sub1=res.rep1
sub2=res.rep2
sub3=res.rep3
zer=res.zeros
if P=="Import/Export" then important(subs,sel,act) end
if P=="&Numbers" then numbers(subs,sel) end
if P=="&Chapters" then chopters(subs,sel) end
if P=="Do &Stuff" then
if res.stuff=="convert framerate" then framerate(subs)
elseif res.stuff=="honorificslaughterhouse" then honorifix(subs,sel)
else sel=stuff(subs,sel,act) end
end
lastP=P
lastres=res
if P=="Save Config" then saveconfig() end
return sel
end
-- IMPORT/EXPORT --------------------------------------------------------------------------------------------
function important(subs,sel,act)
aline=subs[act]
atext=aline.text
atags=atext:match("^{(\\[^}]-)}") or ""
atags=atags:gsub("\\move%b()","")
atxt=atext:gsub(STAG,"")
-- create table from user data (lyrics)
sdata={}
if res.mega=="update lyrics" and res.dat=="" then t_error("No lyrics given.",1)
else
res.dat=res.dat.."\n"
for dataline in res.dat:gmatch("(.-)\n") do if dataline~="" then table.insert(sdata,dataline) end end
end
-- user input
sub1=res.rep1
sub2=res.rep2
sub3=res.rep3
zer=res.zeros
rest=res.rest
-- this checks whether the pattern for lines with lyrics was found
songcheck=0
-- paths
scriptpath=ADP("?script")
if script_path=="relative" then path=scriptpath.."\\"..relative_path end
if script_path=="absolute" then path=absolute_path end
-- IMPORT --
if res.mega:match("import") and not res.mega:match("chptrs") then
noshift=false defect=false keeptxt=false deline=false
-- import-single-sign GUI
if res.mega=="import sign" then
press,reslt=ADD({
{x=0,y=0,class="label",label="File name:"},
{x=0,y=1,width=2,class="edit",name="signame"},
{x=1,y=0,width=2,class="dropdown",name="signs",items={"title","eptitle","custom","eyecatch"},value="custom"},
{x=2,y=1,class="label",label=".ass"},
{x=0,y=2,width=3,class="checkbox",name="matchtime",label="keep current line's times",value=true,},
{x=0,y=3,width=3,class="checkbox",name="keeptext",label="keep current line's text",value=false,},
{x=0,y=4,width=3,class="checkbox",name="keeptags",label="combine tags (current overrides) ",value=false,},
{x=0,y=5,width=3,class="checkbox",name="addtags",label="combine tags (imported overrides)",value=false,},
{x=0,y=6,width=3,class="checkbox",name="noshift",label="don't shift times (import as is)",value=false,},
{x=0,y=7,width=3,class="checkbox",name="deline",label="delete original line",value=false,},
},{"OK","Cancel"},{ok='OK',close='Cancel'})
if press=="Cancel" then ak() end
if reslt.signs=="custom" then signame=reslt.signame else signame=reslt.signs end
noshift=reslt.noshift keeptxt=reslt.keeptext deline=reslt.deline
keeptags=reslt.keeptags addtags=reslt.addtags
end
-- read signs.ass
if res.mega=="import signs" then
file=io.open(path.."signs.ass")
if file==nil then ADD({{class="label",label=path.."signs.ass\nNo such file."}},{"ok"},{cancel='ok'}) ak() end
signs=file:read("*all")
io.close(file)
end
-- sort out if using OP, ED, signs, or whatever .ass and read the file
songtype=res.mega:match("import (%a+)")
if songtype=="sign" then songtype=signame end
file=io.open(path..songtype..".ass")
if file==nil then t_error(path..songtype..".ass\nNo such file.",1) end
song=file:read("*all")
io.close(file)
-- cleanup useless stuff
song=song:gsub("^.-(Dialogue:)","%1")
song=song.."\n"
song=song:gsub("\n\n$","\n")
song=song:gsub("%[[^%]]-%]\n","\n")
-- make table out of lines
slines={}
for sline in song:gmatch("(.-)\n") do
if sline~="" then table.insert(slines,sline) end
end
-- save (some) current line properties
btext=atext
basetime=aline.start_time
basend=aline.end_time
basestyle=aline.style
baselayer=aline.layer
-- import-signs list and GUI
if res.mega=="import signs" then
-- make a table of signs in signs.ass
signlist={}
signlistxt=""
for x=1,#slines do
efct=slines[x]:match("%a+: %d+,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-(,[^,]-,).*")
esfct=esc(efct)
if not signlistxt:match(esfct) then signlistxt=signlistxt..efct end
end
for sn in signlistxt:gmatch(",([^,]-),") do table.insert(signlist,sn) end
-- import-signs GUI
button,reslt=ADD({
{x=0,y=0,class="label",label="Choose a sign to import:"},
{x=0,y=1,class="dropdown",name="impsign",items=signlist,value=signlist[1]},
{x=0,y=2,class="checkbox",name="matchtime",label="keep current line's times",value=true,},
{x=0,y=3,class="checkbox",name="keeptext",label="keep current line's text",value=false,},
{x=0,y=4,class="checkbox",name="keeptags",label="combine tags (current overrides) ",value=false,},
{x=0,y=5,class="checkbox",name="addtags",label="combine tags (imported overrides)",value=false,},
{x=0,y=6,class="checkbox",name="noshift",label="don't shift times (import as is)",value=false,},
{x=0,y=7,class="checkbox",name="defect",label="delete 'effect'",value=false,},
{x=0,y=8,class="checkbox",name="deline",label="delete original line",value=false,},
},{"OK","Cancel"},{ok='OK',close='Cancel'})
if button=="Cancel" then ak() end
if button=="OK" then whatsign=reslt.impsign end
noshift=reslt.noshift defect=reslt.defect keeptxt=reslt.keeptext deline=reslt.deline
keeptags=reslt.keeptags addtags=reslt.addtags
-- nuke lines for the other signs
for x=#slines,1,-1 do
efct=slines[x]:match("%a+: %d+,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-,([^,]-),.*")
if efct~=whatsign then table.remove(slines,x) end
end
end
-- check start time of the first line (for overall shifting)
starttime=slines[1]:match("%a+: %d+,([^,]+)")
shiftime=string2time(starttime)
if res.mega:match("sign") and noshift then shiftime=0 end
-- importing lines from whatever .ass
for x=#slines,1,-1 do
local ltype,layer,s_time,e_time,style,actor,margl,margr,margv,eff,txt=slines[x]:match("(%a+): (%d+),([^,]-),([^,]-),([^,]-),([^,]-),([^,]-),([^,]-),([^,]-),([^,]-),(.*)")
l2=aline
if ltype=="Comment" then l2.comment=true else l2.comment=false end
l2.layer=layer
-- timing/shifting depending on settings
if res.mega:match("import sign") and reslt.matchtime then l2.start_time=basetime l2.end_time=basend else
s_time=string2time(s_time)
e_time=string2time(e_time)
if not noshift then s_time=s_time+basetime e_time=e_time+basetime end
l2.start_time=s_time-shiftime
l2.end_time=e_time-shiftime
end
l2.style=style
l2.actor=actor
l2.margin_l=margl
l2.margin_r=margr
l2.margin_t=margv
l2.effect=eff
if defect then l2.effect="" end
l2.text=txt
atext=txt
if keeptxt and actor~="x" then
btext2=btext:gsub("{\\[^}]-}","")
l2.text=l2.text:gsub("^({\\[^}]-}).*","%1"..btext2) atext=btext2
end
if keeptags and actor~="x" then
l2.text=addtag(atags,l2.text)
l2.text=l2.text:gsub("({%*?\\[^}]-})",function(tg) return duplikill(tg) end)
:gsub("({%*?\\[^}]-})",function(tg) return extrakill(tg,2) end)
end
if addtags and actor~="x" then
l2.text="{"..atags.."}"..l2.text
l2.text=l2.text:gsub("{(\\[^}]-)}{(\\[^}]-)}","{%1%2}")
:gsub("({%*?\\[^}]-})",function(tg) return duplikill(tg) end)
:gsub("({%*?\\[^}]-})",function(tg) return extrakill(tg,2) end)
end
subs.insert(act+1,l2)
end
-- delete line if not keeping
if deline then res.keep=false end
if not res.keep then subs.delete(act) else
-- keep line, restore initial state + comment out
atext=btext aline.comment=true aline.start_time=basetime aline.end_time=basend aline.style=basestyle aline.actor="" aline.effect=""
aline.layer=baselayer aline.text=atext subs[act]=aline
end
end
-- EXPORT --
if res.mega=="export sign" then
exportsign=""
for z,i in ipairs(sel) do
line=subs[i]
text=line.text
if z==1 then snam=line.effect end
exportsign=exportsign..line.raw.."\n"
end
press,reslt=ADD({
{x=0,y=0,class="label",label="Target:",},
{x=0,y=1,class="label",label="Name:",},
{x=1,y=0,width=2,class="dropdown",name="addsign",
items={"Add to signs.ass","Save to new file:"},value="Add to signs.ass"},
{x=1,y=1,width=2,class="edit",name="newsign",value=snam},
},{"OK","Cancel"},{ok='OK',close='Cancel'})
if press=="Cancel" then ak() end
if press=="OK" then
if reslt.newsign=="" then t_error("No name supplied.",1) end
newsgn=reslt.newsign:gsub("%.ass$","")
if reslt.addsign=="Add to signs.ass" then
file=io.open(path.."signs.ass")
if not file then file=io.open(path.."signs.ass","w") end
sign=file:read("*all") or ""
file:close()
exportsign=exportsign:gsub("(%u%a+: %d+,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-,[^,]-),[^,]-,(.-)\n","%1,"..reslt.newsign..",%2\n")
sign=sign:gsub("%u%a+: [^\n]+,"..esc(reslt.newsign)..",.-\n","") :gsub("^\n*","")
sign=sign.."\n"..exportsign
file=io.open(path.."signs.ass","w")
file:write(sign)
end
if reslt.addsign=="Save to new file:" then
file=io.open(path..newsgn..".ass","w")
file:write(exportsign)
end
file:close()
end
end
-- IMPORT CHAPTERS
if res.mega=="import chptrs" then
xml=aegisub.dialog.open("Chapters file (xml)","",scriptpath.."\\","*.xml",false,true)
if xml==nil then ak() end
file=io.open(xml)
xmlc=file:read("*all")
io.close(file)
chc=0
for ch in xmlc:gmatch("<ChapterAtom>(.-)</ChapterAtom>") do
chnam=ch:match("<ChapterString>(.-)</ChapterString>")
chtim=ch:match("<ChapterTimeStart>(.-)</ChapterTimeStart>")
chtim=chtim:gsub("(%d%d):(%d%d):(%d%d)%.(%d%d%d?)(.*)",function(a,b,c,d,e) if d:len()==2 then d=d.."0" end return d+c*1000+b*60000+a*3600000 end)
l2=aline
if fr2ms(1)==nil then chs=chtim else chs=fr2ms(ms2fr(chtim)) end
l2.start_time=chs
l2.end_time=chs+1
l2.actor="chptr"
l2.text="{"..chnam.."}"
subs.insert(act+chc,l2)
chc=chc+1
end
end
-- Update Lyrics
if res.mega=="update lyrics" then
sup1=esc(sub1) sup2=esc(sub2)
for z,i in ipairs(sel) do
progress("Updating Lyrics... "..round(z/#sel)*100 .."%")
line=subs[i]
text=line.text
songlyr=sdata
if line.style:match(rest) then stylecheck=1 else stylecheck=0 end
if res.restr and stylecheck==0 then pass=0 else pass=1 end
if res.field=="actor" then marker=line.actor
elseif res.field=="effect" then marker=line.effect end
denumber=marker:gsub("%d","")
-- marked lines
if marker:match(sup1.."%d+"..sup2) and denumber==sub1..sub2 and pass==1 then
index=tonumber(marker:match(sup1.."(%d+)"..sup2))
puretext=text:gsub("{%*?\\[^}]-}","")
lastag=text:match("({\\[^}]-}).$")
if songlyr[index]~=nil and songlyr[index]~=puretext then
text=text:gsub("^({\\[^}]-}).*","%1"..songlyr[index])
if not text:match("^{\\[^}]-}") then text=songlyr[index] end
end
songcheck=1
if songlyr[index]~=puretext then
if lastag~=nil then text=text:gsub("(.)$",lastag.."%1") end
change=" (Changed)"
else change=""
end
aegisub.log("\nupdate: "..puretext.." --> "..songlyr[index]..change)
end
line.text=text
subs[i]=line
end
end
if res.mega=="update lyrics" and songcheck==0 then press,reslt=ADD({{x=0,y=0,class="label",label="The "..res.field.." field of selected lines doesn't match given pattern \""..sub1.."#"..sub2.."\".\n(Or style pattern wasn't matched if restriction enabled.)\n#=number sequence"}},{"ok"},{cancel='ok'}) end
noshift=nil defect=nil keeptxt=nil deline=nil keeptags=nil addtags=nil
end
-- NUMBERS -------------------------------------------------------------------------------------------
function numbers(subs,sel)
zl=zer:len()
if sub3:match("[,/;]") then startn,int=sub3:match("(%d+)[,/;](%d+)") int=tonumber(int) else startn=sub3:gsub("%[.-%]","") int=1 end
if sub3:match("%[") then numcycle=tonumber(sub3:match("%[(%d+)%]")) else numcycle=0 end
if sub3=="" then startn=1 end
startn=tonumber(startn)
mark=res.field:gsub("left","margin_l"):gsub("right","margin_r"):gsub("vert","margin_t")
if res.modzero=="number lines" and res.intxt then res.field='nope' end
if res.modzero=="number 12321" then
NB={}
if numcycle==0 then t_error("You must set a counting limit 'Y': X[Y]",1) end
for q=startn,numcycle do
qq=0
repeat
table.insert(NB,q)
qq=qq+1
until qq>=int
end
for q=numcycle-1,startn+1,-1 do
qq=0
repeat
table.insert(NB,q)
qq=qq+1
until qq>=int
end
end
if res.modzero=="random" then
if not tonumber(sub1) or not tonumber(sub2) then t_error("No valid input. \nUse Left and Right fields to set limits \nfor random number generation.",1) end
if not tonumber(sub3) or tonumber(sub3)<1 then t_error("Error. Mod must be 1 or higher.",1) end
if string.match("layer style text",res.field) then t_error("Invalid marker. \nOnly actor, effect, comment, and margins.",1) end
ranTab={}
local R=math.ceil(#sel/sub3)
for i=1,R do
local rndm=round(math.random(sub1*1000,sub2*1000)/1000,zl-1)
table.insert(ranTab,rndm)
end
end
for z=1,#sel do
i=sel[z]
line=subs[i]
text=line.text
if res.modzero:match"number" then
progress("Numbering... "..round(z/#sel)*100 .."%")
if startn==nil or numcycle>0 and startn>numcycle then t_error("Wrong parameters. Syntax: start/repeat[limit]\nExamples:\n5 (5 6 7 8...)\n5/3 (5 5 5 6 6 6 7 7 7...)\n5/3[6] (5 5 5 6 6 6 5 5 5 6 6 6...)\n5[6] (5 6 5 6 5 6...)",1) end
local Z=z
if res.modzero=="number lines" then
-- regular numbering
count=math.ceil(Z/int)+(startn-1)
if numcycle>0 and count>numcycle then
repeat count=count-(numcycle-startn+1) until count<=numcycle
end
else -- 1 2 3 4 5 4 3 2 1
if Z>#NB then repeat Z=Z-#NB until Z<=#NB end
count=NB[Z]
end
count=tostring(count)
if zl>count:len() then repeat count="0"..count until zl==count:len() end
if not mark:match'margin' and mark~='layer' then number=sub1..count..sub2 else number=count end
if res.intxt then text=text:gsub("%d+",number)
elseif mark=="comment" then text=text..wrap(number)
elseif mark=="text" then text=number
else line[mark]=number
end
end
if res.modzero=="add to marker" then
progress("Adding... "..round(z/#sel)*100 .."%")
if res.field=="actor" then line.actor=sub1..line.actor..sub2
elseif res.field=="effect" then line.effect=sub1..line.effect..sub2
elseif res.field=="text" then text=sub1..text..sub2
end
end
if res.modzero=="zero fill" then
progress("Filling... "..round(z/#sel)*100 .."%")
form="%0"..zl.."d"
mark=res.field
aet="actoreffect"
if aet:match(mark) then
target=line[mark]
target=target:gsub("(%d+)",function(d) return string.format(form,d) end)
line[mark]=target
end
if mark=='text' then
nt=''
repeat
seg,t2=text:match("^(%b{})(.*)")
if not seg then seg,t2=text:match("^([^{]+)(.*)")
if not seg then break end
seg=seg:gsub("(%-?[%d.]+)",function(d)
if tonumber(d)>0 and not d:match("%.%d") then return string.format(form,d) else return d end
end)
end
nt=nt..seg
text=t2
until text==''
text=nt
end
end
if res.modzero=="random" then
li=math.ceil(z/sub3)
local num=0
for t=1,#ranTab do
if li==t then num=ranTab[t] break end
end
if mark:match "margin" and num<0 then num=0-num end
if mark=="comment" then
text=text..wrap("random: "..num)
else
line[mark]=num
end
end
line.text=text
subs[i]=line
end
end
-- CHAPTERS ------------------------------------------------------------------------------------------------
function chopters(subs,sel)
if res.marker=="effect" and res.nam=="effect" then t_error("Error. Both marker and name cannot be 'effect'.",1) end
if res.chmark then
if res.lang~="" then kap=res.lang else kap=res.chap end
for z,i in ipairs(sel) do
line=subs[i]
text=line.text
if res.marker=="actor" then line.actor="chptr" end
if res.marker=="effect" then line.effect="chptr" end
if res.marker=="comment" then text=text.."{chptr}" end
if res.nam=="effect" then line.effect=kap end
if res.nam=="comment" then text=nobra(text) text=wrap(kap)..text end
line.text=text
subs[i]=line
end
else
euid=2013
chptrs={}
subchptrs={}
if res.lang=="" then clang="eng" else clang=res.lang end
for i=1,#subs do
if subs[i].class=="info" then
if subs[i].key=="Video File" then videoname=subs[i].value videoname=videoname:gsub("%.mkv","") end
end
if subs[i].class=="dialogue" then
line=subs[i]
text=line.text
actor=line.actor
effect=line.effect
start=line.start_time
if text:match("{[Cc]hapter}") or text:match("{[Cc]hptr}") or text:match("{[Cc]hap}") then comment="chapter" else comment="" end
if res.marker=="actor" then marker=actor:lower() end
if res.marker=="effect" then marker=effect:lower() end
if res.marker=="comment" then marker=comment:lower() end
if marker=="chapter" or marker=="chptr" or marker=="chap" then
if res.nam=="comment" then
name=text:match("^{([^}]*)}")
name=name:gsub(" [Ff]irst [Ff]rame",""):gsub(" [Ss]tart",""):gsub("part a","Part A"):gsub("part b","Part B"):gsub("preview","Preview")
else
name=effect
end
if name:match("::") then main,subname=name:match("(.+)::(.+)") sub=1
else sub=0
end
lineid=start+2013+i
timecode=math.floor(start/1000)
tc1=math.floor(timecode/60)
tc2=timecode%60
tc3=start%1000
tc4="00"
if tc2==60 then tc2=0 tc1=tc1+1 end
if tc1>119 then tc1=tc1-120 tc4="02" end
if tc1>59 then tc1=tc1-60 tc4="01" end
if tc1<10 then tc1="0"..tc1 end
if tc2<10 then tc2="0"..tc2 end
if tc3<100 then tc3="0"..tc3 end
linetime=tc4..":"..tc1..":"..tc2.."."..tc3
if linetime=="00:00:00.00" then linetime="00:00:00.033" end
if sub==0 then
cur_chptr={id=lineid,name=name,tim=linetime}
table.insert(chptrs,cur_chptr)
else
cur_chptr={id=lineid,subname=subname,tim=linetime,main=main}
table.insert(subchptrs,cur_chptr)
end
end
if line.style=="Default" then euid=euid+text:len() end
end
end
-- subchapters
subchapters={}
for c=1,#subchptrs do
local ch=subchptrs[c]
ch_main=ch.main
ch_uid=ch.id
ch_name=ch.subname
ch_time=ch.tim
schapter=" <ChapterAtom>\n <ChapterDisplay>\n <ChapterString>"..ch_name.."</ChapterString>\n <ChapterLanguage>"..clang.."</ChapterLanguage>\n </ChapterDisplay>\n <ChapterUID>"..ch_uid.."</ChapterUID>\n <ChapterTimeStart>"..ch_time.."</ChapterTimeStart>\n <ChapterFlagHidden>0</ChapterFlagHidden>\n <ChapterFlagEnabled>1</ChapterFlagEnabled>\n </ChapterAtom>\n"
subchapter={main=ch_main,chap=schapter}
table.insert(subchapters,subchapter)
end
-- chapters
insert_chapters=""
if res.intro then
insert_chapters=" <ChapterAtom>\n <ChapterUID>"..#subs.."</ChapterUID>\n <ChapterFlagHidden>0</ChapterFlagHidden>\n <ChapterFlagEnabled>1</ChapterFlagEnabled>\n <ChapterDisplay>\n <ChapterString>Intro</ChapterString>\n <ChapterLanguage>"..clang.."</ChapterLanguage>\n </ChapterDisplay>\n <ChapterTimeStart>00:00:00.033</ChapterTimeStart>\n </ChapterAtom>\n"
end
table.sort(chptrs,function(a,b) return a.tim<b.tim or (a.tim==b.tim and a.id<b.id) end)
for c=1,#chptrs do
local ch=chptrs[c]
ch_uid=ch.id
ch_name=ch.name
ch_time=ch.tim
local subchaps=""
for c=1,#subchapters do
local subc=subchapters[c]
if subc.main==ch_name then subchaps=subchaps..subc.chap end
end
chapter=" <ChapterAtom>\n <ChapterUID>"..ch_uid.."</ChapterUID>\n <ChapterFlagHidden>0</ChapterFlagHidden>\n <ChapterFlagEnabled>1</ChapterFlagEnabled>\n <ChapterDisplay>\n <ChapterString>"..ch_name.."</ChapterString>\n <ChapterLanguage>"..clang.."</ChapterLanguage>\n </ChapterDisplay>\n"..subchaps.." <ChapterTimeStart>"..ch_time.."</ChapterTimeStart>\n </ChapterAtom>\n"
insert_chapters=insert_chapters..chapter
end
chapters="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<Chapters>\n <EditionEntry>\n <EditionFlagHidden>0</EditionFlagHidden>\n <EditionFlagDefault>0</EditionFlagDefault>\n <EditionUID>"..euid.."</EditionUID>\n"..insert_chapters.." </EditionEntry>\n</Chapters>"
scriptpath=ADP("?script")
scriptname=aegisub.file_name()
scriptname=scriptname:gsub("%.ass","")
if ch_script_path=="relative" then path=scriptpath.."\\"..ch_relative_path end
if ch_script_path=="absolute" then path=ch_absolute_path end
path=path:gsub("([^\\])$","%1/"):gsub("\\$","/")
repeat path,r=path:gsub("\\[^\\]+\\%.%.[\\/]","/") until r==0
if not videoname then videoname=aegisub.project_properties().video_file:gsub("^.*\\",""):gsub("%.mkv","") end
if res.sav=="script" then filename=scriptname else filename=videoname end
chdialog={
{x=0,y=0,width=35,class="label",label="Text to export (You can edit it before saving/copying):"},
{x=0,y=1,width=35,height=20,class="textbox",name="copytext",value=chapters},
{x=0,y=21,width=35,class="label",label='File "'..filename..'.xml" will be saved in "'..path..'"\nIf you want to change the path, use Save Config.'},
{x=35,y=0,width=12,class="label",label="You can also edit this && refresh"}, -- #4
}
q=1
for chn in chapters:gmatch("<ChapterString>(.-)</ChapterString>") do
table.insert(chdialog,{x=35,y=q,width=12,name='ch_'..q,class="edit",value=chn})
q=q+1
end
repeat
if pressed=="Refresh" then
q=0
reslt.copytext=reslt.copytext:gsub("(<ChapterString>).-(</ChapterString>)",function(a,b) q=q+1 return a..reslt["ch_"..q]..b end)
for k,v in ipairs(chdialog) do
if v.class=='edit' then v.value=reslt[v.name] end
if v.name=='copytext' then v.value=reslt.copytext end
end
end
pressed,reslt=ADD(chdialog,{"Save xml file","mp4-compatible chapters","Cancel","Copy to clipboard","Refresh"},{cancel='Cancel'})
until pressed~="Refresh"
chapters=reslt.copytext
if pressed=="Copy to clipboard" then clipboard.set(chapters) end
if pressed=="Save xml file" then
local file=io.open(path..filename..".xml","w")
if file==nil then os.execute("mkdir \""..path.."\"") file=io.open(path..filename..".xml","w") end
if file==nil then t_error("File could not be saved. Probably path doesn't exist:\n"..path,1) end
file:write(chapters)
file:close()
end
if pressed=="mp4-compatible chapters" then
mp4chap=""
m4c=1
for ch in chapters:gmatch("<ChapterAtom>(.-)</ChapterAtom>") do
chnam=ch:match("<ChapterString>(.-)</ChapterString>")
chtim=ch:match("<ChapterTimeStart>(.-)</ChapterTimeStart>")
num=tostring(m4c)
if num:len()==1 then num="0"..num end
chnum="CHAPTER"..num
mp4chap=mp4chap..chnum.."="..chtim.."\n"..chnum.."NAME="..chnam.."\n\n"
m4c=m4c+1
end
chapters=mp4chap:gsub("\n\n$","")
chdialog[2].value=chapters
chdialog[3].label=chdialog[3].label:gsub('%.xml','_chapters.txt')
for c=#chdialog,4,-1 do table.remove(chdialog,c) end
pressed,reslt=ADD(chdialog,{"Save txt file","Cancel","Copy to clipboard"},{cancel='Cancel'})
chapters=reslt.copytext
if pressed=="Copy to clipboard" then clipboard.set(chapters) end
if pressed=="Save txt file" then
local file=io.open(path..filename.."_chapters.txt","w")
if file==nil then os.execute("mkdir \""..path.."\"") file=io.open(path..filename.."_chapters.txt","w") end
file:write(chapters)
file:close()
end
end
end
end
-- STUFF ---------------------------------------------------------------------------------------------------
function stuff(subs,sel,act)
STAG="^{\\[^}]-}"
repl=0
data={} raw=res.dat.."\n"
for dataline in raw:gmatch("(.-)\n") do table.insert(data,dataline) end
nsel={} for z,i in ipairs(sel) do table.insert(nsel,i) end
orig_sel=#sel
nope=nil
if res.stuff=="make style from act. line" then
line=subs[act]
text=line.text
sr=stylechk(subs,line.style)
nontra=text:gsub("\\t%b()","")
tags=nontra:match("^{\\[^}]-}") or ""
a1=tags:match("\\1a&H(%x%x)&") or sr.color1:match("&H(%x%x)")
a2=tags:match("\\2a&H(%x%x)&") or sr.color2:match("&H(%x%x)")
a3=tags:match("\\3a&H(%x%x)&") or sr.color3:match("&H(%x%x)")
a4=tags:match("\\4a&H(%x%x)&") or sr.color4:match("&H(%x%x)")
color1=tags:match("\\1?c&H(%x%x%x%x%x%x)&") or sr.color1:match("&H%x%x(%x%x%x%x%x%x)&")
color2=tags:match("\\2c&H(%x%x%x%x%x%x)&") or sr.color2:match("&H%x%x(%x%x%x%x%x%x)&")
color3=tags:match("\\3c&H(%x%x%x%x%x%x)&") or sr.color3:match("&H%x%x(%x%x%x%x%x%x)&")
color4=tags:match("\\4c&H(%x%x%x%x%x%x)&") or sr.color4:match("&H%x%x(%x%x%x%x%x%x)&")
sr.color1="&H"..a1..color1.."&"
sr.color2="&H"..a2..color2.."&"
sr.color3="&H"..a3..color3.."&"
sr.color4="&H"..a4..color4.."&"
sr.bold=tags:match("\\b([01])") or sr.bold
sr.italic=tags:match("\\i([01])") or sr.italic
sr.underline=tags:match("\\u([01])") or sr.underline
sr.strikeout=tags:match("\\s([01])") or sr.strikeout
sr.fontname=tags:match("\\fn([^\\}]+)") or sr.fontname
sr.fontsize=tags:match("\\fs(%d+)") or sr.fontsize
sr.scale_x=tags:match("\\fscx([^\\}]+)") or sr.scale_x
sr.scale_y=tags:match("\\fscx([^\\}]+)") or sr.scale_y
sr.spacing=tags:match("\\fsp([^\\}]+)") or sr.spacing
sr.angle=tags:match("\\frz([^\\}]+)") or sr.angle
sr.outline=tags:match("\\bord([^\\}]+)") or sr.outline
sr.shadow=tags:match("\\shad([^\\}]+)") or sr.shadow
sr.align=tags:match("\\an(%d)") or sr.align
sr.margin_l=line.margin_l or sr.margin_l
sr.margin_r=line.margin_r or sr.margin_r
sr.margin_t=line.margin_t or sr.margin_t
stylename={{class="label",label="Style Name"},{y=1,class="edit",name="snam",value=""},
{y=2,class="checkbox",name="switch",label="switch to new style",value=true},
{y=3,class="checkbox",name="del",label="delete style tags from line",value=true},
}
pres,rez=ADD(stylename,{"OK","Cancel"},{ok='OK',close='Cancel'})
if pres=="Cancel" then ak() end
sr.name=rez.snam:gsub(",",";")
if rez.del then
tags=text:match(STAG)
tags=tags
:gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end)
:gsub("\\%d?[ac]%b&&","")
:gsub("\\f[ns][^\\}]+","")
:gsub("\\frz[^\\}]+","")
:gsub("\\[ibus][01]","")
:gsub("\\...d[^\\}]+","")
:gsub("\\an%d","")
:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end)
:gsub("{}","")
line.text=text:gsub(STAG,tags)
line.margin_l=0
line.margin_r=0
line.margin_t=0
end
if rez.switch then line.style=sr.name end
subs[act]=line
for i=1,#subs do
if subs[i].class=="style" then
st=subs[i]
if st.name==sr.name then t_error("Style with that name already exists",1) end
end
if subs[i].class=="dialogue" then subs.insert(i,sr)
for z,s in ipairs(sel) do sel[z]=sel[z]+1 end
break end
end
end
-- DATES GUI --
if res.stuff=="format dates" then
dategui=
{{x=0,y=0,class="dropdown",name="date",value="January 1st",items={"January 1","January 1st","1st of January","1st January"}},
{x=1,y=0,class="checkbox",name="log",label="log",value=false,}}
pres,rez=ADD(dategui,{"OK","Cancel"},{ok='OK',close='Cancel'})
if pres=="Cancel" then ak() end
datelog=""
end
-- EXPLODE GUI --
if res.stuff=="explode" then
-- remember values
if exploded then
exx=expl_dist_x exy=expl_dist_y htype=ex_hor vtype=ex_ver expropx=xprop expropy=yprop exf=exfad cfad=cfade
exinvx=xinv exinvy=yinv implo=implode exquence=exseq exkom=excom seqinv=invseq seqpercent=seqpc rmmbr=exremember
else
exx=0 exy=0 htype="all" vtype="all" expropx=false expropy=false exf=0 cfad=0 exinvx=false exinvy=false
implo=false exquence=false exkom=false seqinv=false seqpercent=100 rmmbr=false
end
explodegui={
{x=0,y=0,class="label",label="Horizontal distance: "},
{x=1,y=0,class="floatedit",name="edistx",value=exx,hint="Maximum horizontal distance for move"},
{x=0,y=1,class="label",label="Vertical distance: "},
{x=1,y=1,class="floatedit",name="edisty",value=exy,hint="Maximum vertical distance for move"},
{x=2,y=0,class="label",label="direction: "},
{x=3,y=0,class="dropdown",name="hortype",value=htype,items={"only left","mostly left","all","mostly right","only right"}},
{x=4,y=0,class="checkbox",name="xprop",label="proportional",value=expropx,hint="Uniform move rather than random"},
{x=5,y=0,class="checkbox",name="xinv",label="inverse",value=exinvx,hint="Uniform move in the other direction"},
{x=2,y=1,class="label",label="direction: "},
{x=3,y=1,class="dropdown",name="vertype",value=vtype,items={"only up","mostly up","all","mostly down","only down"}},
{x=4,y=1,class="checkbox",name="yprop",label="proportional",value=expropy,hint="Uniform move rather than random"},
{x=5,y=1,class="checkbox",name="yinv",label="inverse",value=exinvy,hint="Uniform move in the other direction"},
{x=0,y=2,class="checkbox",name="ecfo",label="Custom fade:",hint="Default is line length",value=cfad},
{x=1,y=2,class="floatedit",name="exfad",value=exf},
{x=2,y=2,class="checkbox",name="exseq",label="sequence",value=exquence,hint="move in a sequence instead of all at the same time"},
{x=3,y=2,class="floatedit",name="seqpc",value=seqpercent,step=nil,min=1,max=100,hint="how much time should the sequence take up"},
{x=4,y=2,class="label",label="% of move"},
{x=5,y=2,class="checkbox",name="invseq",label="inverse",value=seqinv,hint="inverse sequence"},
{x=0,y=3,class="checkbox",name="impl",label="Implode",value=implo},
{x=1,y=3,class="checkbox",name="rem",label="Same for all lines",value=rmmbr,hint="use only for layered lines with the same text"},
{x=3,y=3,class="checkbox",name="excom",label="Leave original line commented out",value=exkom,width=3},
}
pres,rez=ADD(explodegui,{"OK","Cancel"},{ok='OK',close='Cancel'})
if pres=="Cancel" then ak() end
expl_dist_x=rez.edistx expl_dist_y=rez.edisty
exfad=rez.exfad cfade=rez.ecfo
ex_hor=rez.hortype ex_ver=rez.vertype
xprop=rez.xprop yprop=rez.yprop
xinv=rez.xinv yinv=rez.yinv
implode=rez.impl exseq=rez.exseq
seqpc=round(rez.seqpc) invseq=rez.invseq
exremember=rez.rem excom=rez.excom
exploded=true
end
-- Randomised Transforms GUI --
if res.stuff=="randomised transforms" then
rine=subs[sel[1]]
durone=rine.end_time-rine.start_time
rtgui={
{x=0,y=0,class="checkbox",name="rtfad",label="Random Fade",width=2,value=true},
{x=0,y=1,class="checkbox",name="rtdur",label="Random Duration",width=2,value=true},
{x=2,y=0,class="label",label="Min: "},
{x=2,y=1,class="label",label="Max: "},
{x=3,y=0,class="floatedit",name="minfad",value=math.floor(durone/5),min=0},
{x=3,y=1,class="floatedit",name="maxfad",value=durone},
{x=4,y=0,class="checkbox",name="rtin",label="Fade In",width=3,value=false,hint="In instead of Out"},
{x=4,y=1,class="checkbox",name="maxisdur",label="Max = Current Duration",width=4,value=true,hint="The maximum will be the duration of each selected line"},
{x=0,y=2,class="checkbox",name="movet",label="t1+t2 in \\move",width=2,hint="use given timecodes in \\move"},
{x=2,y=2,class="label",label="\\t 1"},
{x=4,y=2,class="label",label="\\t 2"},
{x=3,y=2,class="floatedit",name="t1",value=0},
{x=5,y=2,class="floatedit",name="t2",value=0,width=3},
{x=0,y=3,class="label",label="Transform"},
{x=1,y=3,class="dropdown",name="rttag",value="blur",
items={"blur","bord","shad","fs","fsp","fscx","fscy","fax","fay","frz","frx","fry","xbord","ybord","xshad","yshad"}},
{x=2,y=3,class="label",label="Min: "},
{x=4,y=3,class="label",label=" Max: "},
{x=3,y=3,class="floatedit",name="mintfn",value=0,hint="Minimum value for a given tag"},
{x=5,y=3,class="floatedit",name="maxtfn",width=3,value=0,hint="Maximum value for a given tag"},
{x=0,y=4,class="label",width=2,label="Colour Transform"},
{x=2,y=4,class="label",label="Max: "},
{x=3,y=4,class="floatedit",name="rtmaxc",value=100,min=1,max=100,hint="Maximum % of colour change.\nColour tag must be present. Otherwise set to 100%."},
{x=4,y=4,class="label",label="%"},
{x=5,y=4,class="checkbox",name="rtc1",label="\\c ",value=true},
{x=6,y=4,class="checkbox",name="rtc3",label="\\3c ",value=false},
{x=7,y=4,class="checkbox",name="rtc4",label="\\4c ",value=false},
{x=0,y=5,class="checkbox",name="rtacc",label="Use Acceleration",width=2,value=false},
{x=2,y=5,class="label",label="Min: "},
{x=3,y=5,class="floatedit",name="minacc",value=1,min=0},
{x=4,y=5,class="label",label=" Max:"},
{x=5,y=5,class="floatedit",name="maxacc",width=3,value=1,min=0},
{x=0,y=6,class="checkbox",name="rtmx",label="Random Move X",width=2,value=false},
{x=2,y=6,class="label",label="Min: "},
{x=3,y=6,class="floatedit",name="minmx",value=0},
{x=4,y=6,class="label",label=" Max:"},
{x=5,y=6,class="floatedit",name="maxmx",width=3,value=0},
{x=0,y=7,class="checkbox",name="rtmy",label="Random Move Y",width=2,value=false},
{x=2,y=7,class="label",label="Min: "},
{x=3,y=7,class="floatedit",name="minmy",value=0},
{x=4,y=7,class="label",label=" Max:"},
{x=5,y=7,class="floatedit",name="maxmy",width=3,value=0},
}
if rtremember then
for key,val in ipairs(rtgui) do
if val.class~="label" then val.value=rez[val.name] end
end
end
rtchoice={"Fade/Duration","Number Transform","Colour Transform","Help","Cancel"}
rthlp={"Fade/Duration","Number Transform","Colour Transform","Cancel"}
pres,rez=ADD(rtgui,rtchoice,{ok='Fade/Duration',close='Cancel'})
if pres=="Help" then
rthelp={x=0,y=8,width=8,height=6,class="textbox",value="This is supposed to be used after 'split into letters (alpha)' or with gradients.\n\nFade/Duration Example: Min: 500, Max: 2000.\nA random number between those is generated for each line, let's say 850.\nThis line's duration will be 850ms, and it will have a 850ms fade out.\n\nNumber Transform Example: Blur, Min: 0.6, Max: 2.5\nRandom number generated: 1.7. Line will have: \\t(\\blur1.7)\n\nRandom Colour Transform creates transforms to random colours. \nMax % transform limits how much the colour can change.\n\nAccel works with either transform function.\n\nRandom Move works as an additional option with any function.\nIt can be used on its own if you uncheck other stuff. Works with Fade In."}
table.insert(rtgui,rthelp)
pres,rez=ADD(rtgui,rthlp,{ok='Fade/Duration',close='Cancel'})
end
if pres=="Cancel" then ak() end
if pres=="Fade/Duration" then RTM="FD" end
if pres=="Number Transform" then RTM="NT" end
if pres=="Colour Transform" then RTM="CT" end
rtremember=true
RTF=rez.rtfad RTD=rez.rtdur
MnF=rez.minfad MxF=rez.maxfad
RTin=rez.rtin RTMax=rez.maxisdur
RTT=rez.rttag MnT=rez.mintfn MxT=rez.maxtfn
RTA=rez.rtacc MnA=rez.minacc MxA=rez.maxacc
MnX=rez.minmx MxX=rez.maxmx MnY=rez.minmy MxY=rez.maxmy
MxC=round(rez.rtmaxc*255/100)
rtcol={}
if rez.rtc1 then table.insert(rtcol,1) end
if rez.rtc3 then table.insert(rtcol,3) end
if rez.rtc4 then table.insert(rtcol,4) end
t_times=""
end
-- Clone Clip GUI --
if res.stuff=="clone clip" then
if clone_h then cchc=clone_h else cchc=2 end
if clone_v then ccvc=clone_v else ccvc=2 end
if dist_h then cchd=dist_h else cchd=20 end
if dist_v then ccvd=dist_v else ccvd=20 end
if ccshift then ccsh=ccshift else ccsh=0 end
ccgui={
{x=0,y=0,class="label",label="Horizontal distance: "},
{x=1,y=0,class="intedit",name="hdist",value=cchd,min=1},
{x=0,y=1,class="label",label="Horizontal clones: "},
{x=1,y=1,class="intedit",name="hclone",value=cchc,min=1},
{x=0,y=2,class="label",label="Vertical distance: "},
{x=1,y=2,class="intedit",name="vdist",value=ccvd,min=1},
{x=0,y=3,class="label",label="Vertical clones: "},
{x=1,y=3,class="intedit",name="vclone",value=ccvc,min=1},
{x=0,y=4,class="label",label="Shift even rows by:"},
{x=1,y=4,class="intedit",name="ccshift",value=ccsh,min=0},
}
pres,rez=ADD(ccgui,{"OK","Cancel"},{ok='OK',close='Cancel'})
if pres=="Cancel" then ak() end
clone_h=rez.hclone dist_h=rez.hdist
clone_v=rez.vclone dist_v=rez.vdist
ccshift=rez.ccshift
end
-- DISSOLVE GUI ---------------------------------------------------------------------------------------------
if res.stuff=="dissolve text" then
if dlast then ddistance=v_dist ddlines=dlines dshape=shape dalter=alternate dissin=disin otherd=otherdis v2direction=v2d
else ddistance=10 ddlines=10 dshape="square" dalter=true dissin=false otherd=false v2direction="randomly"
end
dissgui={
{x=0,y=0,class="label",label="Distance between points: "},
{x=1,y=0,class="floatedit",name="ddist",value=ddistance,min=4,step=2},
{x=0,y=1,class="label",label="Shape of clips:"},
{x=1,y=1,class="dropdown",name="shape",items={"square","square 2","diamond","triangle 1","triangle 2","hexagon","wave/hexagram","vertical lines","horizontal lines"},value=dshape},
{x=0,y=2,class="checkbox",name="alt",label="Shift even rows (all except vertical/horizontal lines)",value=dalter,width=2},
{x=0,y=3,class="checkbox",name="disin",label="Reverse effect (fade in rather than out)",value=dissin,width=2},
{x=0,y=4,class="checkbox",name="otherdiss",label="Dissolve v2. ... Lines:",value=otherd,hint="only square, diamond, vertical lines"},
{x=1,y=4,class="floatedit",name="modlines",value=ddlines,min=6,step=2},
{x=0,y=5,class="label",label=" Dissolve v2: Dissolve"},
{x=1,y=5,class="dropdown",name="v2dir",items={"randomly","from top","from bottom","from left","from right"},value=v2direction},
}
pres,rez=ADD(dissgui,{"OK","What Is This","Cancel"},{ok='OK',close='Cancel'})
if pres=="What Is This" then
dishelp={x=0,y=6,width=10,height=8,class="textbox",value="The script can either automatically draw a clip around the text,\nor you can make your own clip.\nThe automation only considers position, alignment, and scaling,\nso for anything more complex, make your own.\nYou can just try it without a clip,\nand if the result isn't right, draw a clip first. (Only 4 points!)\n\n'Distance between points' will be the distance between the\nstarting points of all the little iclips.\nLess Distance = more clips = more lag,\nso use the lowest values only for smaller text.\nYou can run this on one line or fbf lines.\nThe ideal 'fade' is as many frames as the given Distance.\nThat way the clips grow by 1 pixel per frame.\nAny other way doesn't look too good,\nbut you can apply Distance 10 over 20 lines\nand have each 2 consecutive lines identical.\nMore Distance than lines doesn't look so bad, and the effect is 'faster'.\nIf you apply this to 1 line, the line will be split to have the effect applied to as many frames as the Distance is. (This is preferred.)\nFor hexagon, the actual distance is twice the input. (It grows faster.)\n\nThe shapes should be self-explanatory, so just experiment.\n\n'Shift even rows' means that even rows will have an offset\nfrom odd rows by half of the given Distance.\nNot checking this will have a slightly different and less regular effect,\nthough it also depends on the shape. Again, experiment.\n\nIf you need to apply this to several layers, you have to do it one by one. The GUI remembers last values. But more layers = more lag.\n\nAll kinds of things can make this lag, so use carefully.\nLines are less laggy than other shapes.\nHorizontal lines are the least laggy. (Unless you have vertical text.)\n\nFor longer fades, use more Distance.\nThis works great with vertical lines but is pretty useless with horizontal.\n\n'Reverse effect' is like fade in while the default is fade out.\nWith one line selected, it applies to the first frames.\n\n'Dissolve v2' is a different kind of dissolve\nand only works with square, diamond, and vertical lines.\nLine count for this is independent on distance between points.\nIt's the only effect that allows Distance 4.\n'Shift even rows' has no effect here.\n\nYou can set a direction of Dissolve v2.\nObviously top and bottom is nonsense for vertical lines.\n'Reverse effect' reverses the direction too, so choose the opposite.\n\nThere may be weird results with some combinations of settings.\nThere may be some malfunctions, as the script is pretty complex.\nSome of them -might- be fixed by reloading automation scripts.\nMakes no sense with \\move. Nukes \\fad.\n\nThere are some fun side effects.\nFor example with 'square 2' and 'Shift even rows',\nyou get a brick wall on the last frame."}
table.insert(dissgui,dishelp)
pres,rez=ADD(dissgui,{"OK","Cancel"},{ok='OK',close='Cancel'})
end
if pres=="Cancel" then ak() end
dlast=true
v_dist=rez.ddist
shape=rez.shape
alternate=rez.alt
disin=rez.disin
otherdis=rez.otherdiss
dlines=rez.modlines
v2d=rez.v2dir
dis2=false
if v2d=="randomly" then dir=5 end
if v2d=="from top" then dir=8 end
if v2d=="from bottom" then dir=2 end
if v2d=="from left" then dir=4 end
if v2d=="from right" then dir=6 end
if not otherdis and v_dist==4 then
t_error("Distance 4 is only allowed for square mod. Changing to 6.")
v_dist=6
end
if otherdis then
if shape=="square" or shape=="diamond" or shape=="vertical lines" then dis2=true else dis2=false end
if shape=="square" then alternate=false end
if shape=="diamond" then alternate=true end
end
if dis2 and #sel==1 then linez=dlines
elseif dis2 and #sel>1 then linez=#sel
else linez=v_dist end
-- DISSOLVE create lines if only one selected ------------------------
if #sel==1 then
rine=subs[sel[1]]
rine.text=rine.text:gsub("\\fad%(.-%)","")
start=rine.start_time endt=rine.end_time
startf=ms2fr(start) endf=ms2fr(endt)
lframes=ms2fr(endt-start)
if lframes<linez then t_error("Line must be at least "..linez.." frames long.",1) end
if disin then
for l=1,linez do
rine.start_time=fr2ms(startf+l-1)
rine.end_time=fr2ms(startf+l)
subs.insert(sel[1],rine)
sel[1]=sel[1]+1
end
for s=1,linez do table.insert(sel,sel[1]-s) end
table.sort(sel)
rine.start_time=fr2ms(startf+linez)
rine.end_time=endt
subs[sel[#sel]]=rine
table.remove(sel,#sel)
else
for l=1,linez do
rine.start_time=fr2ms(endf-l)
rine.end_time=fr2ms(endf-l+1)
subs.insert(sel[1]+1,rine)
end
for s=1,linez do table.insert(sel,sel[1]+s) end
rine.start_time=start
rine.end_time=fr2ms(endf-linez)
subs[sel[1]]=rine
table.remove(sel,1)
end
end
if disin then table.sort(sel,function(a,b) return a>b end) end
-- DISSOLVE Initial Calculations -----------------------------------------------------------------
line=subs[sel[1]]
text=line.text
text=text:gsub("\\clip%(([%d.-]+),([%d.-]+),([%d.-]+),([%d.-]+)%)",function(a,b,c,d)
a=math.floor(a) b=math.floor(b) c=math.ceil(c) d=math.ceil(d)
return string.format("\\clip(m %d %d l %d %d %d %d %d %d)",a,b,c,b,c,d,a,d) end)
-- draw clip when no clip present
if not text:match("\\clip") then
styleref=stylechk(subs,line.style)
vis=text:gsub("%b{}","")
width,height,descent,ext_lead=aegisub.text_extents(styleref,vis)
bord=text:match("\\bord([%d%.]+)") if bord==nil then bord=styleref.outline end
bord=math.ceil(bord)
scx=text:match("\\fscx([%d%.]+)") if scx==nil then scx=styleref.scale_x end scx=scx/100
scy=text:match("\\fscy([%d%.]+)") if scy==nil then scy=styleref.scale_y end scy=scy/100
wi=round(width)
he=round(height)
text2=getpos(subs,text)
if not text:match("\\pos") then text=text2 end
xx,yy=text:match("\\pos%(([%d.-]+),([%d.-]+)%)")
if h_al=="left" then cx1=xx cx2=xx+wi*scx end
if h_al=="right" then cx1=xx-wi*scx cx2=xx end
if h_al=="mid" then cx1=xx-wi/2*scx cx2=xx+wi/2*scx end
if v_al=="top" then cy1=yy cy2=yy+he*scy end
if v_al=="bottom" then cy1=yy-he*scy cy2=yy end
if v_al=="mid" then cy1=yy-he/2*scy cy2=yy+he/2*scy end
cx1=math.floor(cx1-bord) cx2=math.ceil(cx2+bord)
cy1=math.floor(cy1-bord) cy2=math.ceil(cy2+bord)
text=addtag("\\clip(m "..cx1.." "..cy1.." l "..cx2.." "..cy1.." "..cx2.." "..cy2.." "..cx1.." "..cy2..")",text)
end
-- get outermost clip points even if it's irregular (though it should be fucking regular)
exes={} wais={}
klip=text:match("\\clip%(m [%d%-]+ [%d%-]+ l [%d%-]+ [%d%-]+ [%d%-]+ [%d%-]+ [%d%-]+ [%d%-]+")
for ex,wai in klip:gmatch("([%d%-]+) ([%d%-]+)") do
table.insert(exes,tonumber(ex))
table.insert(wais,tonumber(wai))
end
table.sort(exes) table.sort(wais)
x1=exes[1]-2 x2=exes[4]+2 y1=wais[1]-2 y2=wais[4]+2
width=x2-x1 height=y2-y1
h_dist=2*v_dist
if shape=="hexagon" then h_dist=math.floor(h_dist*2) end
rows=math.ceil(height/v_dist)
rows2=math.ceil(height/v_dist/2)
points=math.ceil(width/h_dist)+1
if shape=="horizontal lines" then vert=2*v_dist rows=math.ceil(rows/2)+1 else vert=v_dist end
if shape:match("triangle") or shape=="wave/hexagram" then rows=rows+1 end
xpoints={}
for w=1,points do point=x1+h_dist*(w-1) table.insert(xpoints,point) end
ypoints={}
for w=1,rows do point=y1+vert*(w-1) table.insert(ypoints,point) end
ypoints2={}
for w=1,rows2 do point=y1+2*v_dist*(w-1) table.insert(ypoints2,point) end
-- this is all centers of individual iclip shapes
allpoints1={}
for w=1,#ypoints do
for z=1,#xpoints do
u=0
if alternate and w%2==0 then u=h_dist/2 else u=0 end -- every even row is shifted by half of h_dist from odd rows
rnum=math.random(2000,6000)
if dir==5 then rindex=rnum end
if dir==8 then rindex=rnum*ypoints[w]^2 end
if dir==4 then rindex=rnum*xpoints[z]^2 end
if dir==2 then rindex=0-rnum*ypoints[w]^2 end
if dir==6 then rindex=0-rnum*xpoints[z]^2 end
point={xpoints[z]+u,ypoints[w],rindex}
table.insert(allpoints1,point)
end
end
allpoints2={}
for w=1,#ypoints2 do
for z=1,#xpoints do
u=0
if alternate and w%2==0 then u=h_dist/2 else u=0 end -- every even row is shifted by half of h_dist from odd rows
rnum=math.random(2000,6000)
if dir==5 then rindex=rnum end
if dir==8 then rindex=rnum*ypoints2[w]^2 end
if dir==4 then rindex=rnum*xpoints[z]^2 end
if dir==2 then rindex=0-rnum*ypoints2[w]^2 end
if dir==6 then rindex=0-rnum*xpoints[z]^2 end
point={xpoints[z]+u,ypoints2[w],rindex}
table.insert(allpoints2,point)
end
end
if dis2 and shape=="square" or shape=="square 2" or shape=="hexagon" then allpoints=allpoints2 else allpoints=allpoints1 end
if dis2 and shape=="vertical lines" then allpoints={}
for w=1,#xpoints do
rnum=math.random(2000,6000)
if dir==4 then rindex=rnum*xpoints[w]^2
elseif dir==6 then rindex=0-rnum*xpoints[w]^2
else rindex=rnum end
table.insert(allpoints,{xpoints[w],0,rindex})
end
end
if dis2 then table.sort(allpoints,function(a,b) return a[3]<b[3] end) end
-- DISSOLVE v2 Calculations ------------------------------------------
if dis2 then d2c=0 fullclip="" dis2tab={} rnd=1 ppl=#allpoints/linez
for w=1,#allpoints do
pt=allpoints[w]
vd=v_dist
if shape=="square" then
krip="m "..pt[1]-vd.." "..pt[2]-vd.." l "..pt[1]+vd.." "..pt[2]-vd.." "..pt[1]+vd.." "..pt[2]+vd.." "..pt[1]-vd.." "..pt[2]+vd.." "
end
if shape=="diamond" then
krip="m "..pt[1].." "..pt[2]-vd.." l "..pt[1]+vd.." "..pt[2].." "..pt[1].." "..pt[2]+vd.." "..pt[1]-vd.." "..pt[2].." "
end
if shape=="vertical lines" then
krip="m "..pt[1]-vd.." "..y1.." l "..pt[1]+vd.." "..y1.." "..pt[1]+vd.." "..y2.." "..pt[1]-vd.." "..y2.." "
end
fullclip=fullclip..krip
d2c=d2c+1
if d2c>=math.floor(ppl) and w>=ppl*rnd then d2c=0 rnd=rnd+1 table.insert(dis2tab,fullclip) end
end
end
-- DISSOLVE END --------------------------------------------------------------------
end
-- What Is the Matrix --
if res.stuff=="what is the Matrix?" then
matrixgui={
{x=0,y=0,class="label",label="Max. transformations per letter: "},
{x=1,y=0,class="intedit",name="tpl",value=4,min=2},
{x=0,y=1,class="label",label="Frames to stay the same: "},
{x=1,y=1,class="intedit",name="fts",value=2,min=1},
{x=0,y=2,class="label",label="Character set: "},
{x=1,y=2,class="dropdown",name="charset",items={"UPPERCASE","lowercase","Both","More","Everything"},value="Both"},
{x=0,y=3,class="label",label="Chance to keep letter (0-10):"},
{x=1,y=3,class="intedit",name="mkeep",value=5,min=0,max=10},
{x=0,y=4,width=2,class="checkbox",name="showall",label="Show all letters from the start",value=true,},
{x=0,y=5,width=2,class="label",label="Monospace fonts are optimal. For others, use left alignment."},
}
if matrixres then
for key,val in ipairs(matrixgui) do
if val.class=="checkbox" or val.class=="dropdown" or val.class=="intedit" then val.value=rez[val.name] end
end
end
pres,rez=ADD(matrixgui,{"What Is the Matrix?","Cancel"},{ok='What Is the Matrix?',close='Cancel'})
if pres=="Cancel" then ak() end
AB="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
ab="abcdefghijklmnopqrstuvwxyz"
Ab="!?()$&+-="
aB="@#%^*/[]';:,.|"
if rez.charset=="UPPERCASE" then chset=AB.." "
elseif rez.charset=="lowercase" then chset=ab.." "
elseif rez.charset=="Both" then chset=AB..ab.." "
elseif rez.charset=="More" then chset=AB..ab..Ab.." "
else chset=AB..ab..Ab..aB.." " end
ABC=chset:len()
mframes=rez.tpl
fpl=rez.fts
matrixres=rez
end
if res.stuff=="time by frames" then frs=res.rep1:match("%-?%d+") or 0 fre=res.rep2:match("%-?%d+") or 0
rine=subs[sel[1]]
fstart=ms2fr(rine.start_time)
fendt=ms2fr(rine.end_time)
rine.start_time=fr2ms(fstart)
rine.end_time=fr2ms(fendt)
subs[sel[1]]=rine
if frs==0 and fre==0 then t_error("Use Left/Right to input \nnumber of frames \nto shift by for start/end.",1) end
end
if res.stuff=="duplicate and shift lines" then
FB=tonumber(res.rep1:match("^%d+")) or 0
FA=tonumber(res.rep2:match("^%d+")) or 0
if FA+FB==0 then t_error("Use the Left and Right fields to set how many times you want to duplicate the line.",1) end
FALL=FA+FB+1
FB1=FB+1
end
if res.stuff=="split text to actor/effect" then
sep1=esc(res.rep1)
sep2=esc(res.rep2)
target1=res.field
if target1~="actor" and target1~="effect" then t_error("Marker must be Actor or Effect.",1) end
if sep1=="" then t_error("No separator given. (Use Left field.)",1) end
if sep2~="" then
if target1=="actor" then target2="effect" else target2="actor" end
end
end
if res.stuff=="clip2margins" then
resx,resy=nil,nil
for i=1,#subs do
if subs[i].class=="info" then
local k=subs[i].key
local v=subs[i].value
if k=="PlayResX" then resx=v end
if k=="PlayResY" then resy=v end
end
if resx and resy then break end
end
local G={
{x=0,y=0,width=2,class="checkbox",name="l",label="Set left margin"},
{x=0,y=1,width=2,class="checkbox",name="r",label="Set right margin"},
{x=0,y=2,width=2,class="checkbox",name="v",label="Set vertical margin"},
{x=0,y=3,class="label",label="Vertical:"},
{x=1,y=3,class="dropdown",name="tb",items={"from top","from bottom"},value='from top'},
{x=0,y=4,width=2,class="checkbox",name="a",label="Set all"},
}
P2,rez=ADD(G,{"+","-"},{ok='+',close='-'})
if rez.a then rez.l=true rez.r=true rez.v=true end
end
if res.stuff=="extrapolate tracking" then
l01=subs[sel[1]]
l02=subs[sel[#sel]]
e01=l01.effect:lower()
e02=l02.effect:lower()
if e01~='x' and e02~='x' or e01=='x' and e02=='x' then t_error("The first OR last line of the selection must be marked with 'x' in Effect.",1) end
if e01=='x' then fade='in' else fade='out' end
if fade=='in' then table.sort(sel,function(a,b) return a>b end) end
for z,i in ipairs(sel) do
l=subs[i]
t=l.text
if l.effect=='x' then break end
tags=t:match(STAG) or ''
tags=tags:gsub('\\t%b()','')
posx,posy=tags:match('\\pos%((.-),(.-)%)')
fscx=tags:match('\\fscx([%d.]+)') or '100'
fscy=tags:match('\\fscy([%d.]+)') or '100'
if z==1 then r1tags={posx,posy,fscx,fscy} end
r2tags={posx,posy,fscx,fscy}
refl=z-1
end
-- loggtab(r1tags)
-- loggtab(r2tags)
PX=(r2tags[1]-r1tags[1])/refl
PY=(r2tags[2]-r1tags[2])/refl
SX=(r2tags[3]-r1tags[3])/refl
SY=(r2tags[4]-r1tags[4])/refl
-- logg(PX)
-- logg(PY)
-- logg(SX)
-- logg(SY)
c=0
for z,i in ipairs(sel) do
l=subs[i]
t=l.text
if l.effect=='x' then
c=c+1
CPX=round(r2tags[1]+PX*c,2)
CPY=round(r2tags[2]+PY*c,2)
t=t:gsub('\\pos%(.-,.-%)','\\pos('..CPX..','..CPY..')')
if SX~=0 or SY~=0 then
t=t:gsub('\\fscx([%d.]+)',function(a) return '\\fscx'..round(a+SX*c,2) end)
t=t:gsub('\\fscy([%d.]+)',function(a) return '\\fscy'..round(a+SY*c,2) end)
end
end
l.text=t
subs[i]=l
end
end
KO1=subs[sel[1]].start_time
if res.stuff:match("replacer") or res.stuff=="fix kara tags for fbf lines" or res.stuff=="fill columns" then table.sort(sel,function(a,b) return a>b end) end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- LINES START HERE ---------------------------------------------------------------------------------------------------------------------------------------------------------
for z=#sel,1,-1 do
i=sel[z]
progress("Processing line #"..i-line0.." ["..#sel+1-z.."/"..#sel.."]")
line=subs[i]
text=line.text
orig=text
style=line.style
-- What Is the Matrix --
if res.stuff=="what is the Matrix?" then
start=line.start_time endt=line.end_time
startf=ms2fr(start)
tags=text:match("^{\\[^}]-}") or ""
visible=text:gsub("%b{}","")
ltrs={}
ltrs2={}
matches=re.find(visible,".")
for l=1,#matches do
table.insert(ltrs,matches[l].str)
r=math.random(1,ABC)
table.insert(ltrs2,chset:sub(r,r))
end
base=""
lines={}
for l=1,#ltrs do
for f=1,mframes do
if f<mframes then
x=math.random(1,ABC)
letter=chset:sub(x,x)
z=math.random(0,9)
if z<rez.mkeep and f>1 then letter=lastletter end
if l==1 and letter==" " then letter="e" end
if l==#ltrs and letter==" " then letter="s" end
txt=base..letter
lastletter=letter
else letter=ltrs[l] txt=base..letter base=txt
end
if not rez.showall then txt=txt.."{\\alpha&HFF&}" end
for n=l+1,#ltrs do
if rez.showall then
y=math.random(1,ABC)
letter=chset:sub(y,y)
z=math.random(0,9)
if z<rez.mkeep and n>l then letter=ltrs2[n] end
ltrs2[n]=letter
if n==#ltrs and letter==" " then letter="k" end
txt=txt..letter
else
txt=txt..ltrs[n]
end
end
txt=tags..txt
fact=l*mframes+f-mframes-1
startfr=startf+fact*fpl
st=fr2ms(startfr)
et=fr2ms(startfr+fpl)
l2={txt,st,et}
table.insert(lines,l2)
end
lastletter=nil
end
for ln=#lines,1,-1 do
lin=lines[ln]
line.text=lin[1]
line.start_time=lin[2]
line.end_time=lin[3]
if ln==#lines and endt>lin[3] then line.end_time=endt end
subs.insert(i+1,line)
end
line.comment=true
end
if res.stuff=="save/load" and z==1 then
if savedata==nil then savedata="" end
if res.dat~="" then
savedata=savedata.."\n\n"..res.dat
savedata=savedata
:gsub("^\n\n","")
:gsub("\n\n\n","\n\n")
ADD({{class="label",label="Data saved.",x=0,y=0,width=20,height=2}},{"OK"},{close='OK'})
else
ADD({{x=0,y=0,width=50,height=18,class="textbox",name="savetxt",value=savedata},},{"OK"},{close='OK'})
end
end
if res.stuff=="replacer" then
lim=sub3:match("^%d+")
if lim==nil then limit=1 else limit=tonumber(lim) end
replicant1=sub1
replicant2=sub2
tk=text
count=0
if res.regex=="lua patterns" then
repeat
text=text:gsub(replicant1,replicant2) count=count+1
until count==limit
if text~=tk then repl=repl+1
if res.log then
r1=replicant1:gsub("%%%(","_L_"):gsub("%%%)","_R_"):gsub("%(",""):gsub("%)",""):gsub("_L_","%%%("):gsub("_R_","%%%)")
for l1 in tk:gmatch(r1) do
aegisub.log("\nOrig: "..l1)
l2=l1:gsub(replicant1,replicant2)
aegisub.log("\nMod: "..l2)
end
end
end
else
if replicant1=='' then t_error('Replacing an empty string is not allowed with regexp.',1) end
repeat
text=re.sub(text,replicant1,replicant2) count=count+1
until count==limit
if text~=tk then repl=repl+1
if res.log then
for r1 in re.gfind(tk,replicant1) do
aegisub.log("\nOrig: "..r1)
r2=re.sub(r1,replicant1,replicant2)
aegisub.log("\nMod: "..r2)
end
end
end
end
end
if res.stuff=="lua calc" then
lim=sub3:match("^%d+")
if lim==nil then limit=1 else limit=tonumber(lim) end
replicant1=sub1:gsub("\\","\\")
replicant2=sub2:gsub("\\","\\")
replicant1=sub1:gsub("\\\\","\\")
replicant2=sub2:gsub("\\\\","\\")
replicant2="||"..replicant2.."||"
replicant2=replicant2:gsub("%.%.","||")
tk=text
count=0
repeat
text=text:gsub(replicant1,function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)
tab1={"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p"}
tab2={a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}
r2=replicant2
asd=1
repeat
for r=1,16 do
r2=r2
:gsub(tab1[r].."%*([%d%.]+)",function(num) return tab2[r]*tonumber(num) end)
:gsub(tab1[r].."%/([%d%.]+)",function(num) return tab2[r]/tonumber(num) end)
:gsub(tab1[r].."%+([%d%.]+)",function(num) return tab2[r]+tonumber(num) end)
:gsub(tab1[r].."%-([%d%.]+)",function(num) return tab2[r]-tonumber(num) end)
:gsub("([%d%.]+)%*([%d%.]+)",function(n1,n2) return tonumber(n1)*tonumber(n2) end)
:gsub("([%d%.]+)%/([%d%.]+)",function(n1,n2) return tonumber(n1)/tonumber(n2) end)
:gsub("([%d%.]+)%+([%d%.]+)",function(n1,n2) return tonumber(n1)+tonumber(n2) end)
:gsub("([%d%.]+)%-([%d%.]+)",function(n1,n2) return tonumber(n1)-tonumber(n2) end)
end
for r=1,16 do
if tab2[r]~=nil then
r2=r2:gsub("([|%*%/%+%-])"..tab1[r].."|","%1"..tab2[r].."|")
r2=r2:gsub("%("..tab1[r].."%)","("..tab2[r]..")")
end
end
r2=r2:gsub("round%(([^%)]+)%)",function(num) return math.floor(tonumber(num)+0.5) end)
asd=asd+1
until not r2:match("[%*%/%+%-]") or asd==12
r2=r2:gsub("||","")
return r2 end) count=count+1
until count==limit
if text~=tk then
repl=repl+1
if res.log then logg("1. "..tk.."\n 2. "..text.."\n") end
end
end
if res.stuff=="make comments visible" then text=text:gsub("\\N","/N"):gsub(" *{ *([^\\}]-)}"," %1"):gsub("/N","\\N")
if text~=orig then repl=repl+1 else nope=1 end
end
if res.stuff=="switch commented/visible" then
text=text
:gsub("\\N","_br_")
:gsub("{([^\\}]-)}","}%1{")
:gsub("^([^{]+)","{%1")
:gsub("([^}]+)$","%1}")
:gsub("([^}])({\\[^}]-})([^{])","%1}%2{%3")
:gsub("^({\\[^}]-})([^{])","%1{%2")
:gsub("([^}])({\\[^}]-})$","%1}%2")
:gsub("{}","")
:gsub("_br_","\\N")
if text~=orig then repl=repl+1 else nope=1 end
end
if res.stuff=="reverse text" then
tags=text:match(STAG) or ""
if not tags:match('\\p1') then
text=text:gsub(STAG,""):gsub("(\\[Nh])","{%1}")
inTags=inline_pos(text)
text=text:gsub("%b{}","")
local er
local count=0
repeat
t2=""
er=0
for L in re.gfind(text,".") do
t2=L..t2
-- errors
if L=="" or re.find(L,"..") then er=er+1 end
end
count=count+1
until er==0 or count==30
t2=inline_ret(t2,inTags)
t2=t2:gsub("{(\\[Nh])}","%1")
if count==30 and er>0 then
logg("Repeated re module failure on line #"..i-line0.." (30 times).\n Some characters keep disappearing or being added.\n Skipping this line. Please redo it separately.")
else
text=tags..t2
end
end
if text~=orig then repl=repl+1 else nope=1 end
end
if res.stuff=="reverse words" then
tags=text:match(STAG) or ""
visible=text:gsub("%b{}",""):gsub("\\N","")
if visible:match(" ") then
text=text:gsub("%b{}",""):gsub(" *\\N *"," \\N")
breaks={}
local k=1
for w in text:gmatch("(%S+%s*)") do
if w:match('\\N') then table.insert(breaks,k) end
k=k+1
end
text=text:gsub("\\N","")
nt=""
for l in text:gmatch("%S+") do nt=" "..l..nt end
nt2=''
k=1
for w in nt:gmatch("(%S+%s*)") do
for i,n in ipairs(breaks) do
if n==k then w='\\N'..w end
end
nt2=nt2..w
k=k+1
end
text=tags..nt2
end
if text~=orig then repl=repl+1 else nope=1 end
end
if res.stuff=="fill columns" and #sel>1 then
local G={
{x=0,y=0,class="checkbox",name="layer",label="Layer"},
{x=0,y=1,class="checkbox",name="start_time",label="Start Time"},
{x=0,y=2,class="checkbox",name="end_time",label="End Time"},
{x=0,y=3,class="checkbox",name="margin_l",label="Left Margin"},
{x=0,y=4,class="checkbox",name="margin_r",label="Right Margin"},
{x=0,y=5,class="checkbox",name="margin_t",label="Vertical Margin"},
{x=0,y=6,class="checkbox",name="style",label="Style"},
{x=0,y=7,class="checkbox",name="actor",label="Actor"},
{x=0,y=8,class="checkbox",name="effect",label="Effect"},
{x=0,y=9,class="checkbox",name="text",label="Text"},
}
if z==#sel then
local buttons={'Fill','All/None','Fail'}
fcr=fcr or {}
repeat
if FCP=='All/None' then
local Q=0
for k,v in ipairs(G) do
if fcr[v.name]==false then Q=1 end
end
for k,v in ipairs(G) do
if Q==1 then v.value=true else v.value=false end
end
end
FCP,fcr=ADD(G,buttons,{ok='Fill',close='Fail'})
if FCP=='Fail' then ak() end
until FCP~='All/None'
rrine=line
else
for k,v in ipairs(G) do
if (line[v.name]=='' or line[v.name]==0) and fcr[v.name] then line[v.name]=rrine[v.name] end
end
text=line.text
rrine=line
end
end
-- REVERSE TRANSFORMS ------------------
if res.stuff=="reverse transforms" then
styleref=stylechk(subs,line.style)
text=text:gsub("\\1c","\\c")
tags=text:match(STAG) or ""
text=text:gsub(STAG,"")
tags=cleantr(tags)
tags=duplikill(tags)
tags=tags:gsub("\\fs(%d)","\\fsize%1")
notrans=tags:gsub("\\t%b()","")
for tr in tags:gmatch("\\t%b()") do
tr=tr:gsub("\\i?clip%([^%)]+%)","") :gsub("\\t%(","") :gsub("%)$","")
for tag in tr:gmatch("\\[1234]?%a+") do
if not notrans:match(tag) then
tags=fill_in(tags,tag)
end
tags=tags:gsub(tag.."([^\\}]+)([^}]-)"..tag.."([^\\}%)]+)",tag.."%3%2"..tag.."%1")
end
end
tags=tags:gsub("(i?clip%([^%)]+%))([^}]-\\t[^}]-)(i?clip%([^%)]+%))","%3%2%1") :gsub("\\fsize","\\fs")
text=tags..text
if text~=orig then repl=repl+1 else nope=1 end
end
if res.stuff=="fake capitals" then
tags=text:match(STAG) or ""
text=text:gsub(STAG,"")
text=re.sub(text,"(\\u)","{\\\\fs"..sub1.."}\\1{\\\\fs}")
text=text:gsub("{\\fs}(%-?){\\fs"..sub1.."}","%1")
text=tags..text
text=text:gsub("{(\\[^}]-)}{(\\[^}]-)}","{%1%2}")
if text~=orig then repl=repl+1 else nope=1 end
end
if res.stuff=="format dates" then
text2=text:gsub("%b{}","")
if rez.date=="January 1" then
text=re.sub(text,"(January|February|March|April|May|June|July|August|September|October|November|December) (\\d+)(st|nd|th)","\\1 \\2")
text=re.sub(text,"(\\d+)(st|nd|th|st of|nd of|th of) (January|February|March|April|May|June|July|August|September|October|November|December)","\\3 \\1")
end
if rez.date=="January 1st" then
text=re.sub(text,"(January|February|March|April|May|June|July|August|September|October|November|December) (\\d+)","\\1 \\2th")
text=re.sub(text,"(\\d+)(st|nd|th|st of|nd of|th of) (January|February|March|April|May|June|July|August|September|October|November|December)","\\3 \\1th")
text=text:gsub("(%d)thth","%1th") :gsub("1thst","1st") :gsub("2thnd","2nd") :gsub("1th","1st") :gsub("2th","2nd")
end
if rez.date=="1st of January" then
text=re.sub(text,"(January|February|March|April|May|June|July|August|September|October|November|December) (\\d+)(st|nd|th)?","\\2\\3 of \\1")
text=re.sub(text,"(\\d+) of (January|February|March|April|May|June|July|August|September|October|November|December)","\\1th of \\2")
text=text:gsub("(%d)thth","%1th") :gsub("1thst","1st") :gsub("2thnd","2nd") :gsub("1th","1st") :gsub("2th","2nd")
text=re.sub(text,"(\\d+)(st|nd|th) (January|February|March|April|May|June|July|August|September|October|November|December)","\\1\\2 of \\3")
end
if rez.date=="1st January" then
text=re.sub(text,"(January|February|March|April|May|June|July|August|September|October|November|December) (\\d+)(st|nd|th)?","\\2\\3 \\1")
text=re.sub(text,"(\\d+) (January|February|March|April|May|June|July|August|September|October|November|December)","\\1th \\2")
text=text:gsub("(%d)thth","%1th") :gsub("1thst","1st") :gsub("2thnd","2nd") :gsub("1th","1st") :gsub("2th","2nd")
text=re.sub(text,"(\\d+)(st|nd|th) of (January|February|March|April|May|June|July|August|September|October|November|December)","\\1\\2 \\3")
end
textn=text:gsub("%b{}","")
if text2~=textn then datelog=text2.." -> "..textn.."\n"..datelog end
if text~=orig then repl=repl+1 else nope=1 end
end
if res.stuff=="transform \\k to \\t\\alpha" then
repeat text=text:gsub("{(\\[^}]-)}{(\\[^}]-)}","{%1%2}") until not text:match("{(\\[^}]-)}{(\\[^}]-)}")
if text:match("^{[^}]-\\alpha") then alf=text:match("^{[^}]-\\alpha&H(%x%x)&") else alf="00" end
text=text:gsub("\\alpha&H%x%x&","")
tab={}
for kpart in text:gmatch("{[^}]-\\k[fo][%d%.]+[^}]-}[^{]*") do
table.insert(tab,kpart)
end
lastim=0
text=""
for k=1,#tab do
part=tab[k]
tim=tonumber(part:match("\\k[fo]([%d%.]+)"))*10
part=part:gsub("\\k[fo][%d%.]+","\\alpha&HFF&\\t("..lastim..","..lastim+tim..",\\alpha&H"..alf.."&)")
tab[k]=part
lastim=lastim+tim
text=text..tab[k]
end
if text~=orig then repl=repl+1 else nope=1 end
end
-- SPLIT and EXPLODE -------------------------------------------
if res.stuff=="split into letters (alpha)" or res.stuff=="explode" then
l2=line
tags=text:match(STAG) or ""
vis=text:gsub("%b{}","")
af="{\\alpha&HFF&}"
a0="{\\alpha&H00&}"
letters={}
ss=0
ltrmatches=re.find(vis,".")
for l=1,#ltrmatches do
table.insert(letters,ltrmatches[l].str)
end
if savetab==nil then savetab={} end
-- create texts for all resulting lines
for l=#letters,1,-1 do
tx=af
ltr=a0..letters[l]..af
for t=1,#letters do
ltr2=letters[t]
if t==l then ltr2=ltr end
tx=tx..ltr2
end
tx=textmod(text,tx)
txt2=tags..tx
if not txt2:match("\\pos") then txt2=getpos(subs,txt2) end
txt2=txt2:gsub("{\\alpha&HFF&}$","")
txt2=tagmerge(txt2)
txt2=txt2:gsub(ATAG,function(tg) return duplikill(tg) end)
-- Explode
if res.stuff=="explode" then
dur=line.end_time-line.start_time
if cfade then FO=exfad else FO=dur end
if implode then expfad="\\fad("..FO..",0)" else expfad="\\fad(0,"..FO..")" end
if FO==0 then EFO="" else EFO=expfad end
if ex_hor=="all" then ex1a=0-expl_dist_x ex1b=expl_dist_x end
if ex_hor=="only left" then ex1a=0-expl_dist_x ex1b=0 end
if ex_hor=="only right" then ex1a=0 ex1b=expl_dist_x end
if ex_hor=="mostly left" then ex1a=0-expl_dist_x ex1b=expl_dist_x/3 end
if ex_hor=="mostly right" then ex1a=0-expl_dist_x/3 ex1b=expl_dist_x end
if ex_ver=="all" then ex2a=0-expl_dist_y ex2b=expl_dist_y end
if ex_ver=="only up" then ex2a=0-expl_dist_y ex2b=0 end
if ex_ver=="only down" then ex2a=0 ex2b=expl_dist_y end
if ex_ver=="mostly up" then ex2a=0-expl_dist_y ex2b=expl_dist_y/3 end
if ex_ver=="mostly down" then ex2a=0-expl_dist_y/3 ex2b=expl_dist_y end
rvrs=#letters-l+1
if xinv then xind=rvrs else xind=l end
if yinv then yind=rvrs else yind=l end
if invseq then seqt=rvrs else seqt=l end
if implode then seqt=#letters-seqt+1 end
if exremember and z<#sel then
tab=savetab[rvrs]
ex1=tab.x1 ex2=tab.x2
else
if xprop then
xhdist=(ex1b-ex1a)/#letters
ex1=round(ex1a+xhdist*xind)
else
ex1=math.ceil(math.random(ex1a,ex1b))
end
if yprop then
xvdist=(ex2b-ex2a)/#letters
ex2=round(ex2a+xvdist*yind)
else
ex2=math.ceil(math.random(ex2a,ex2b))
end
end
if exremember and z==#sel then table.insert(savetab,{x1=ex1,x2=ex2}) end
-- move sequence
if exseq then
tfrag=round(dur/#letters/(100/seqpc))
xt1=tfrag*seqt-tfrag
else
xt1=0
end
xt2=dur
if implode and exseq then xt2=dur-xt1 xt1=0 end
txt2=txt2:gsub("\\move%(([%d.-]+),([%d.-]+).-%)","\\pos(%1,%2)")
txt2=txt2:gsub("\\fad%(.-%)","")
if implode then
txt2=txt2:gsub("\\pos%(([%d.-]+),([%d.-]+)%)",
function(a,b) return EFO.."\\move("..a+ex1..","..b+ex2..","..a..","..b..","..xt1..","..xt2..")" end)
else
txt2=txt2:gsub("\\pos%(([%d.-]+),([%d.-]+)%)",
function(a,b) return EFO.."\\move("..a..","..b..","..a+ex1..","..b+ex2..","..xt1..","..xt2..")" end)
end
txt2=txt2:gsub("{\\[^}]-}$","")
end
l2.text=txt2
-- I hope I don't ever have to touch this shit again
if letters[l]~=" " then subs.insert(i+1,l2)
ss=ss+1
shift=orig_sel-z
if shift>0 then
for s=#sel,1,-1 do
if s>z+ss-1 then sel[s]=sel[s]+1 end
end
end
table.insert(sel,sel[z]+ss)
table.sort(sel)
end
end
line.comment=true
end
-- Clone Clip
if res.stuff=="clone clip" and text:match("\\clip%((.-)%)") then
text=text:gsub("\\clip%(([%d.-]+),([%d.-]+),([%d.-]+),([%d.-]+)%)",function(a,b,c,d)
a=math.floor(a) b=math.floor(b) c=math.ceil(c) d=math.ceil(d)
return string.format("\\clip(m %d %d l %d %d %d %d %d %d)",a,b,c,b,c,d,a,d) end)
clip=text:match("\\clip%((.-)%)")
clip=clip.." "
h_clip=clip
for h=1,clone_h-1 do
hc=clip:gsub("([%d%-]+) ([%d%-]+)",function(a,b) return a+dist_h*h.." "..b end)
h_clip=h_clip..hc
end
fullclip=h_clip
for v=1,clone_v-1 do
if v%2==1 then offset=ccshift else offset=0 end
vc=h_clip:gsub("([%d%-]+) ([%d%-]+)",function(a,b) return a+offset.." "..b+dist_v*v end)
fullclip=fullclip..vc
end
fullclip=fullclip:gsub(" $","")
text=text:gsub("\\clip%((.-)%)","\\clip("..fullclip..")")
if text~=orig then repl=repl+1 else nope=1 end
end
if res.stuff=="clip2margins" and text:match("\\clip%(([%d.,-]-)%)") then
local l,t,r,b=text:match("\\clip%(([%d.-]+),([%d.-]+),([%d.-]+),([%d.-]+)%)")
if rez.l then line.margin_l=round(l) end
if rez.r then line.margin_r=round(resx-r) end
if rez.v and rez.tb=="from top" then line.margin_t=round(t) end
if rez.v and rez.tb=="from bottom" then line.margin_t=round(resy-b) end
end
-- DISSOLVE Individual Lines --------------------------------------------------------------------------------------
if res.stuff=="dissolve text" then
fullklip=""
-- radius of clips based on # of sel. lines and shapes
r=math.ceil(z*linez/#sel-1)
if shape=="diamond" and not alternate then r=math.floor(r*1.5) end
if shape:match("triangle") and alternate then r=math.floor(r*1.4) end
if shape:match("triangle") and not alternate then r=math.floor(r*1.5) end
if shape=="wave/hexagram" then r=math.floor(r*1.55) end
xpt=0 sw=0 osq=0
if not dis2 and not shape:match("lines") and r>0 then
for w=1,#allpoints do
pt=allpoints[w]
if shape=="square" or shape=="square 2" then
krip="m "..pt[1]-r.." "..pt[2]-r.." l "..pt[1]+r.." "..pt[2]-r.." "..pt[1]+r.." "..pt[2]+r.." "..pt[1]-r.." "..pt[2]+r.." "
end
if shape=="diamond" then
krip="m "..pt[1].." "..pt[2]-r.." l "..pt[1]+r.." "..pt[2].." "..pt[1].." "..pt[2]+r.." "..pt[1]-r.." "..pt[2].." "
end
if shape=="triangle 1" then
krip="m "..pt[1].." "..pt[2]-r.." l "..pt[1]+r.." "..pt[2]+r.." "..pt[1]-r.." "..pt[2]+r.." "
end
if shape=="triangle 2" then
krip="m "..pt[1]-r.." "..pt[2]-r.." l "..pt[1]+r.." "..pt[2]-r.." "..pt[1].." "..pt[2]+r.." "
end
if shape=="hexagon" then
krip="m "..pt[1].." "..pt[2]-2*r.." l "..pt[1]+2*r.." "..pt[2]-r.." "..pt[1]+2*r.." "..pt[2]+r.." "..pt[1].." "..pt[2]+2*r.." "..pt[1]-2*r.." "..pt[2]+r.." "..pt[1]-2*r.." "..pt[2]-r.." "
end
if shape=="wave/hexagram" then
if sw==0 then
krip="m "..pt[1].." "..pt[2]-r+1 .." l "..pt[1]+r.." "..pt[2]+r+1 .." "..pt[1]-r.." "..pt[2]+r+1 .." "
else
krip="m "..pt[1]-r.." "..pt[2]-r.." l "..pt[1]+r.." "..pt[2]-r.." "..pt[1].." "..pt[2]+r.." "
end
xpt=xpt+1
if xpt==#xpoints then xpt=0 sw=1-sw end
end
fullklip=fullklip..krip
end
end
if not dis2 and shape=="vertical lines" and r>0 then
for w=1,#xpoints do
pt=xpoints[w]
krip="m "..pt-r.." "..y1.." l "..pt+r.." "..y1.." "..pt+r.." "..y2.." "..pt-r.." "..y2.." "
fullklip=fullklip..krip
end
end
if not dis2 and shape=="horizontal lines" and r>0 then
for w=1,#ypoints do
pt=ypoints[w]
krip="m "..x1-vert.." "..pt-r.." l "..x2+vert.." "..pt-r.." "..x2+vert.." "..pt+r.." "..x1-vert.." "..pt+r.." "
fullklip=fullklip..krip
end
end
if dis2 and r>0 then fullklip=dis2tab[r] end
fullklip=fullklip:gsub(" $","")
text=text:gsub("\\clip%(.-%)","")
if r>0 then text=addtag("\\iclip("..fullklip..")",text) end
end
-- DISSOLVE END 2 ----------------------------------------------
-- RANDOMISED TRANSFORMS ---------------------------------------
if res.stuff=="randomised transforms" then
dur=line.end_time-line.start_time
t_1=rez.t1 t_2=rez.t2
if rez.t1<0 then t_1=dur+rez.t1 end
if rez.t2<0 then t_2=dur+rez.t2 end
if t_1~=0 or t_2~=0 then
t_times=round(t_1)..","..round(t_2)..","
end
if rez.movet then
m_times=t_times:gsub("(.*),",",%1"):gsub(",0$",","..dur):gsub("%-%d+","0")
else m_times=""
end
if RTMax then MxF=dur end
-- Fade/Duration
if RTM=="FD" then
FD=math.random(MnF,MxF)
if RTD and not RTin then line.end_time=line.start_time+FD end
if RTD and RTin then line.start_time=line.end_time-FD end
if RTF then text=text:gsub("\\fad%b()","") end
if RTF and not RTin then text="{\\fad(0,"..FD..")}"..text text=text:gsub(FD.."%)}{",FD..")") end
if RTF and RTin then text="{\\fad("..FD..",0)}"..text text=text:gsub(",0%)}{",",0)") end
end
-- Number Transform
if RTM=="NT" then
NT=math.random(MnT*10,MxT*10)/10
if RTA then NTA=math.random(MnA*10,MxA*10)/10 axel=NTA.."," else axel="" end
text=addtag("\\t("..t_times..axel.."\\"..RTT..NT..")",text)
end
-- Colour Transform
if RTM=="CT" then
CTfull=""
for c=1,#rtcol do
ctype="\\"..rtcol[c].."c"
ctype=ctype:gsub("\\1c","\\c")
Bluu,Grin,Rett=text:match("^{[^}]-"..ctype.."&H(%x%x)(%x%x)(%x%x)&")
if Bluu~=nil then
R=tonumber(Rett,16)
G=tonumber(Grin,16)
B=tonumber(Bluu,16)
Red=math.random(R-MxC,R+MxC)
Green=math.random(G-MxC,G+MxC)
Blue=math.random(B-MxC,B+MxC)
else
Red=math.random(0,255)
Green=math.random(0,255)
Blue=math.random(0,255)
end
CT=ctype.."&H"..tohex(Blue)..tohex(Green)..tohex(Red).."&"
CTfull=CTfull..CT
end
if RTA then NTA=math.random(MnA*10,MxA*10)/10 axel=NTA.."," else axel="" end
if CTfull~="" then text=addtag("\\t("..t_times..axel..CTfull..")",text) end
end
-- Move X
if rez.rtmx then
MMX=math.random(MnX,MxX)
text=text:gsub("\\move%(([%d.-]+),([%d.-]+),([%d.-]+),([%d.-]+)",
function(a,b,c,d) if RTin then a=a+MMX else c=c+MMX end
return "\\move("..a..","..b..","..c..","..d..m_times end)
text=text:gsub("\\pos%(([%d.-]+),([%d.-]+)",
function(a,b) a2=a if RTin then a=a+MMX else a2=a2+MMX end
return "\\move("..a..","..b..","..a2..","..b..m_times end)
end
-- Move Y
if rez.rtmy then
MMY=math.random(MnY,MxY)
text=text:gsub("\\move%(([%d.-]+),([%d.-]+),([%d.-]+),([%d.-]+)",
function(a,b,c,d) if RTin then b=b+MMY else d=d+MMY end
return "\\move("..a..","..b..","..c..","..d..m_times end)
text=text:gsub("\\pos%(([%d.-]+),([%d.-]+)",
function(a,b) b2=b if RTin then b=b+MMY else b2=b2+MMY end
return "\\move("..a..","..b..","..a..","..b2..m_times end)
end
end
if res.stuff=="time by frames" and z>1 then
line.start_time=fr2ms(fstart+(z-1)*frs)
line.end_time=fr2ms(fendt+(z-1)*fre)
end
if res.stuff=="split text to actor/effect" then
local t1,txt=text:match("^(..-)"..sep1.."(.*)$")
local t2,txt2
if t1 and sub3=="0" then t1=t1..sep1 end
if sep2~="" then
txt=txt or text
t2,txt2=txt:match("^(..-)"..sep2.."(.*)$")
if t2 and sub3=="0" then t2=t2..sep2 end
if t2 and sub3=="2" then t2=sep2..t2 end
t2=t2 or ""
if target1=="actor" then line.effect=line.effect..t2 else line.actor=line.actor..t2 end
end
if txt and sub3=="2" then txt=sep1..txt end
if txt2 and sub3=="2" then txt2=sep2..txt2 end
if target1=="actor" then line.actor=line.actor..t1 or "" else line.effect=line.effect..t1 or "" end
text=txt2 or txt or text
if text~=orig then repl=repl+1 else nope=1 end
end
-- DUPLICATE AND SHIFT LINES
if res.stuff=="duplicate and shift lines" then
SF=ms2fr(line.start_time)
EF=ms2fr(line.end_time)
l2=line
effect=line.effect
for x=FALL,1,-1 do
F=x-FB1
-- Main line
if F==0 then
l2.start_time=fr2ms(SF)
l2.end_time=fr2ms(EF)
l2.effect=effect.."[0]"
l2.text=text
end
-- Before
if F<0 then
l2.start_time=fr2ms(SF+F)
l2.end_time=fr2ms(SF+F+1)
l2.effect=effect.."["..F.."]"
l2.text=text
:gsub("\\move%(([%d.-]+),([%d.-]+),([%d.-]+),([%d.-]+).-%)","\\pos(%1,%2)")
:gsub("\\t%b()","")
:gsub("\\fad%b()","")
end
-- After
if F>0 then
l2.start_time=fr2ms(EF+F-1)
l2.end_time=fr2ms(EF+F)
l2.effect=effect.."["..F.."]"
l2.text=text
:gsub("\\move%(([%d.-]+),([%d.-]+),([%d.-]+),([%d.-]+).-%)","\\pos(%3,%4)")
:gsub("\\t%b()",function(t) return t:gsub("\\t%([^\\]*",""):gsub("%)$","") end)
:gsub("\\fad%b()","")
l2.text=l2.text:gsub("({%*?\\[^}]-})",function(tg) return duplikill(tg) end)
end
if res.rep3=="0" then l2.effect=effect end
subs.insert(sel[#sel]+1,l2)
end
end
if res.stuff=="fix kara tags for fbf lines" then
KOD=line.start_time-KO1
if KOD>0 then
LINE={}
for k in text:gmatch("%b{}[^{]*") do table.insert(LINE,k) end
for K=1,#LINE do
seg=LINE[K]
seg=seg:gsub("^(.-)(\\k[of]?)([%d%.]+)(.-)$",function(s,t,k,e)
k=k*10
KOD=KOD-k
if KOD>=0 then return s..e end
if KOD<0 and k+KOD>0 then k=(k+KOD)/10 return s..t..k..e end
if KOD<0 and k+KOD<0 then k=k/10 return s..t..k..e end
end)
LINE[K]=seg:gsub("{}","")
end
nt=""
for K=1,#LINE do nt=nt..LINE[K] end
text=nt
end
if text~=orig then repl=repl+1 else nope=1 end
end
line.text=text
subs[i]=line
if res.stuff=="what is the Matrix?" then subs.delete(i) end
end
progress("Operation complete.")
-- END of LINES
if res.stuff:match"replacer" or res.stuff=="lua calc" then progress("All stuff has been finished.")
if repl==1 then rp=" modified line" else rp=" modified lines" end
press,reslt=ADD({},{repl..rp},{cancel=repl..rp})
end
if res.stuff=="duplicate and shift lines" then
SEL=#sel
for z=#sel,1,-1 do
subs.delete(sel[z])
end
for x=1,(FA+FB)*SEL do
table.insert(sel,sel[SEL]+x)
end
end
if res.stuff=="split into letters (alpha)" or res.stuff=="explode" and not rez.excom then
for i=#sel,1,-1 do
line=subs[sel[i]]
if line.comment then subs.delete(sel[i]) table.remove(sel,i)
for s=i,#sel do sel[s]=sel[s]-1 end
end
end
end
if res.stuff=="format dates" and rez.log then aegisub.log(datelog) end
if noclip then t_error("Some lines weren't processed - missing clip.") noclip=nil end
if res.log then
if repl>1 or nope then t_error(repl.." out of "..#sel.." lines have been modified.") end
end
savetab=nil
return sel
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 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 shiftsel2(sel,i,mode)
if i<sel[#sel] then
for s=1,#sel do
if sel[s]>i 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 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
-- Honorificslaughterhouse --
function honorifix(subs,sel)
for i=#subs,1,-1 do
if subs[i].class=="dialogue" then
line=subs[i]
line.text=line.text
:gsub("%-san","{-san}")
:gsub("%-tan","{-tan}")
:gsub("%-chan","{-chan}")
:gsub("%-kun","{-kun}")
:gsub("%-sama","{-sama}")
:gsub("%-niisan","{-niisan}")
:gsub("%-oniisan","{-oniisan}")
:gsub("%-oniichan","{-oniichan}")
:gsub("%-oneesan","{-oneesan}")
:gsub("%-oneechan","{-oneechan}")
:gsub("%-neesama","{-neesama}")
:gsub("%-sensei","{-sensei}")
:gsub("%-se[mn]pai","{-senpai}")
:gsub("%-dono","{-dono}")
:gsub("Onii{%-chan}","Brother{Onii-chan}")
:gsub("Onii{%-san}","Brother{Onii-san}")
:gsub("Onee{%-chan}","Sister{Onee-chan}")
:gsub("Onee{%-san}","Sister{Onee-san}")
:gsub("Onee{%-sama}","Sister{Onee-sama}")
:gsub("onii{%-chan}","brother{onii-chan}")
:gsub("onii{%-san}","brother{onii-san}")
:gsub("onee{%-chan}","sister{onee-chan}")
:gsub("onee{%-san}","sister{onee-san}")
:gsub("onee{%-sama}","sister{onee-sama}")
:gsub("{{","{")
:gsub("}}","}")
:gsub("({[^{}]-){(%-%a-)}([^{}]-})","%1%2%3")
subs[i]=line
end
end
end
-- framerate --
function framerate(subs)
f1=res.rep1
f2=res.rep2
if not tonumber(f1) or not tonumber(f2) then
local GUI={
{x=0,y=0,width=2,class="label",label="No framerates supplied.\nTry these. (From -> to)"},
{x=0,y=1,class="dropdown",name="f1",items={23.976,24,25,29.970,30},value=23.976},
{x=1,y=1,class="dropdown",name="f2",items={23.976,24,25,29.970,30},value=25},
}
fP,fres=ADD(GUI,{"OK","Cancel"},{ok='OK',close='Cancel'})
if fP=="Cancel" then ak() end
f1=fres.f1 f2=fres.f2
end
for i=1,#subs do
if subs[i].class=="dialogue" then
local line=subs[i]
line.start_time=line.start_time/f2*f1
line.end_time=line.end_time/f2*f1
subs[i]=line
end
end
end
-- reanimatools ---------------------------------------------------
function esc(str) str=str:gsub("[%%%(%)%[%]%.%-%+%*%?%^%$]","%%%1") return str 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 addtag(tag,text) text=text:gsub("^({\\[^}]-)}","%1"..tag.."}") return text end
function round(n,dec) dec=dec or 0 n=math.floor(n*10^dec+0.5)/10^dec return 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 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 string2line(str)
local ltype,layer,s_time,e_time,style,actor,margl,margr,margv,eff,txt=str:match("(%a+): (%d+),([^,]-),([^,]-),([^,]-),([^,]-),([^,]-),([^,]-),([^,]-),([^,]-),(.*)")
l2={}
l2.class="dialogue"
if ltype=="Comment" then l2.comment=true else l2.comment=false end
l2.layer=layer
l2.start_time=string2time(s_time)
l2.end_time=string2time(e_time)
l2.style=style
l2.actor=actor
l2.margin_l=margl
l2.margin_r=margr
l2.margin_t=margv
l2.effect=eff
l2.text=txt
return l2
end
function string2time(timecode)
timecode=timecode:gsub("(%d):(%d%d):(%d%d)%.(%d%d)",function(a,b,c,d) return d*10+c*1000+b*60000+a*3600000 end)
return timecode
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 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 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 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 t_error(message,cancel)
ADD({{class="label",label=message}},{"OK"},{close='OK'})
if cancel then ak() end
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
function getpos(subs,text) -- modified version
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 h_al="left"
elseif alignright:match(acalign) then horz=resx-acright h_al="right"
elseif alignmid:match(acalign) then horz=resx/2 h_al="mid" end
if aligntop:match(acalign) then vert=acvert v_al="top"
elseif alignbot:match(acalign) then vert=resy-acvert v_al="bottom"
elseif aligncent:match(acalign) then vert=resy/2 v_al="mid" 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
-- 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)
if not RS then logg(">>> Fatal error. Try rescanning Autoload dir. <<<") end
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
-- Config Stuff --
function saveconfig()
unconf="Unimportant Configuration\n\n"
for key,val in ipairs(unconfig) do
if val.class=="floatedit" or val.class=="dropdown" then
unconf=unconf..val.name..":"..res[val.name].."\n"
end
if val.class=="checkbox" and val.name~="save" then
unconf=unconf..val.name..":"..tf(res[val.name]).."\n"
end
end
unimpkonfig=ADP("?user").."\\unimportant.conf"
file=io.open(unimpkonfig)
if file~=nil then
konf=file:read("*all")
io.close(file)
imp1=konf:match("imp1:(.-)\n")
imp2=konf:match("imp2:(.-)\n")
imp3=konf:match("imp3:(.-)\n")
chap1=konf:match("chap1:(.-)\n")
chap2=konf:match("chap2:(.-)\n")
chap3=konf:match("chap3:(.-)\n")
end
if imp1==nil then imp1="relative" end
if imp2==nil then imp2="" end
if imp3==nil then imp3="D:\\typesetting\\" end
if chap1==nil then chap1="relative" end
if chap2==nil then chap2="" end
if chap3==nil then chap3="D:\\typesetting\\" end
savestuff={
{x=0,y=0,width=3,class="label",label="This will save the values of dropdown menus and checkboxes, plus the following:"},
{x=0,y=1,class="label",label="Import script path:"},
{x=0,y=2,class="label",label="Import relative path:"},
{x=0,y=3,class="label",label="Import absolute path:"},
{x=0,y=4,class="label",label="Chapters save path:"},
{x=0,y=5,class="label",label="Chapters relative path:"},
{x=0,y=6,class="label",label="Chapters absolute path:"},
{x=1,y=1,class="dropdown",name="imp1",items={"relative","absolute"},value=imp1,hint="relative = script folder"},
{x=1,y=2,class="edit",width=2,name="imp2",value=imp2,hint="path from script folder"},
{x=1,y=3,class="edit",width=2,name="imp3",value=imp3},
{x=1,y=4,class="dropdown",name="chap1",items={"relative","absolute"},value=chap1,hint="relative = script folder"},
{x=1,y=5,class="edit",width=2,name="chap2",value=chap2,hint="path from script folder"},
{x=1,y=6,class="edit",width=2,name="chap3",value=chap3},
{x=0,y=7,width=3,class="label",label="Default ('relative' + '') is the script path. 'ABC' in 'relative path' will use subfolder 'ABC'.\n'..' in 'relative path' will go one folder higher."},
}
click,rez=ADD(savestuff,{"Save","Cancel"},{ok='Save',close='Cancel'})
if click=="Cancel" then ak() end
rez.imp3=rez.imp3:gsub("[^\\]$","%1\\")
rez.chap3=rez.chap3:gsub("[^\\]$","%1\\")
for key,val in ipairs(savestuff) do
if val.x==1 then unconf=unconf..val.name..":"..rez[val.name].."\n" end
end
file=io.open(unimpkonfig,"w")
file:write(unconf)
file:close()
ADD({{class="label",label="Config saved to:\n"..unimpkonfig}},{"OK"},{close='OK'})
end
function loadconfig()
unimpkonfig=ADP("?user").."\\unimportant.conf"
file=io.open(unimpkonfig)
script_path="relative"
relative_path=""
absolute_path="D:\\typesetting\\"
ch_script_path="relative"
ch_relative_path=""
ch_absolute_path="D:\\typesetting\\"
if file~=nil then
konf=file:read("*all")
file:close()
if konf:match("^%-%-") then konf="" t_error("Your config file is outdated.\nUse the 'Save Config' button to save a new one.")
else
for key,v in ipairs(unconfig) do
if v.class=="floatedit" 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 lastimp and v.name=="stuff" then v.value=lastuff end
if lastimp and v.name=="log" then v.value=lastlog end
if lastimp and v.name=="zeros" then v.value=lastzeros end
if lastimp and v.name=="field" then v.value=lastfield end
if lastimp and v.name=="modzero" then v.value=lastmod0 end
end
end
end
script_path=konf:match("imp1:(.-)\n") or "relative"
relative_path=konf:match("imp2:(.-)\n") or ""
absolute_path=konf:match("imp3:(.-)\n") or "D:\\typesetting\\"
ch_script_path=konf:match("chap1:(.-)\n") or "relative"
ch_relative_path=konf:match("chap2:(.-)\n") or ""
ch_absolute_path=konf:match("chap3:(.-)\n") or "D:\\typesetting\\"
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 analyze(l)
text=l.text
dur=l.end_time-l.start_time
dura=dur/1000
txt=text:gsub("%b{}","") :gsub("\\N","")
visible=text:gsub("{\\alpha&HFF&}[^{}]-%b{}",""):gsub("{\\alpha&HFF&}[^{}]*$",""):gsub("%b{}",""):gsub("\\N","\n"):gsub(" *\n+ *"," "):gsub("^ *(.-) *$","%1")
wrd=0 for word in txt:gmatch("([%a\']+)") do wrd=wrd+1 end
chars=visible:gsub(" ",""):gsub("[.,\"]","")
char=chars:len()
cps=math.ceil(char/dura)
if dur==0 then cps=0 end
end
function info(subs,sel,act)
styletab={}
dc=0
sdur=0
S=subs[sel[1]].start_time
E=subs[sel[#sel]].end_time
video=nil stitle=nil colorspace=nil resx=nil resy=nil
prop=aegisub.project_properties()
for x,i in ipairs(sel) do
line=subs[i]
dur=line.end_time-line.start_time
if line.start_time<S then S=line.start_time end
if line.end_time>E then E=line.end_time end
sdur=sdur+dur
end
seldur=sdur/1000
for i=1,#subs do
if subs[i].class=="info" then
local k=subs[i].key
local v=subs[i].value
if k=="Title" then stitle=v end
if k=="Video File" then video=v end
if k=="YCbCr Matrix" then colorspace=v end
if k=="PlayResX" then resx=v end
if k=="PlayResY" then resy=v end
end
if video==nil then video=prop.video_file:gsub("^.*\\","") end
if stitle==nil then sct="" else sct="Script title: "..stitle.."\n" end
if video==nil then vf="" else vf="Video file: "..video.."\n" end
if resy==nil then reso="" else reso="Script resolution: "..resx.."x"..resy.."\n" end
if colorspace==nil then cols="" else cols="Colorspace: "..colorspace.."\n" end
nfo=sct..vf..reso..cols
if subs[i].class=="style" then
local s=subs[i]
table.insert(styletab,s)
end
if subs[i].class=="dialogue" then
dc=dc+1
local l=subs[i]
if i==act then
ano=dc
analyze(l)
for s=1,#styletab do st=styletab[s]
if st.name==l.style then
acfont=st.fontname
acsize=st.fontsize
acalign=st.align
acleft=st.margin_l
acright=st.margin_r
acvert=st.margin_t
acbord=st.outline
acshad=st.shadow
if st.bold then actbold="Bold" else actbold="Regular" end
end
end
ast=l.start_time
aet=l.end_time
if ms2fr(1) then
afr=" ("..ms2fr(aet)-ms2fr(ast).." frames)"
fps=round(aegisub.frame_from_ms(99999999)/(99999999/1000)*1000)/1000
frate="\nFramerate: "..fps.." fps"
else afr='' frate="\nFramerate: unknown"
end
actime=(l.end_time-ast)/1000 ..'s'..afr
actime=actime:gsub("(%(1 frame)s","%1")
if stfr~=nil then else fps=0 end
aligntop="789" alignbot="123" aligncent="456"
alignleft="147" alignright="369" alignmid="258"
if aligntop:match(acalign) then vert=acvert
elseif alignbot:match(acalign) then vert=resy-acvert
elseif aligncent:match(acalign) then vert=resy/2 end
if alignleft:match(acalign) then horz=acleft
elseif alignright:match(acalign) then horz=resx-acright
elseif alignmid:match(acalign) then horz=resx/2 end
aktif="Active line: #"..ano.."\nStyle used: "..l.style.."\nFont used: "..acfont.."\nWeight: "..actbold.."\nFont size: "..acsize.."\nBorder: "..acbord.."\nShadow: "..acshad.."\nDuration: "..actime.."\nCharacters: "..char.."\nCharacters per second: "..cps.."\nDefault position: "..horz..","..vert.."\n\nVisible text:\n"..visible
end
end
end
if ms2fr(1) then selfr=" ("..ms2fr(E)-ms2fr(S).." frames)" else selfr='' end
selfr=selfr:gsub("(%(1 frame)s","%1")
infodump=nfo.."Styles used: "..#styletab.."\nDialogue lines: "..dc..", Selected: "..#sel.."\nCombined length of selected lines: "..seldur.."s\nSelection duration: "..(E-S)/1000 .."s"..selfr..frate.."\n\n"..aktif
end
help_i=[[
- IMPORT/EXPORT -
This allows you to import OP/ED or signs (or whatever) from an external .ass file.
OP/ED must be saved as OP.ass and ED.ass; a sign can have any name.
The .ass file may contain headers, or it can be just the dialogue lines.
The imported stuff will be shifted to your currently selected line (or the first one in your selection).
The first line of the saved file works as a reference point, so use a "First frame of OP" line etc.
(You can save your OP/ED shifted to 0 or you can just leave it as is;
the times will be recalculated to start at the current line.)
"keep line" will keep your current line and comment it. Otherwise the line gets deleted.
IMPORT SIGN / IMPORT SIGNS - works like OP/ED, but you have to input the sign's name.
The difference between the two is:
SIGN - each sign must be saved in its own .ass file.
In the GUI, input the sign's/file's name, for example "eptitle"[.ass].
SIGNS - all signs must be saved in signs.ass.
They are distinguished by what's in the "effect" field - that's the sign's name.
For SIGN, make something like eptitle.ass, eyecatch.ass;
for SIGNS, put "eptitle" or "eyecatch" in the effect field, and put all the signs in signs.ass.
(You can have blank lines between signs for clarity. The script can deal with those.)
The GUI will then show you a list of signs that it gets from the effect fields.
I recommend using SIGNS, as it's imo more efficient (but SIGN was written first and I didn't nuke it).
Options:
With nothing checked, stuff is shifted to the first frame of your active line (like OP/ED).
(SIGN) File name: "custom" will use what you type below. The other ones are presets.
"keep current line's times" - all imported lines will have the start/end time of your active line
"keep current line's text" - all imported lines will have their text (not tags) replaced with your active line's text
- If you want to replace only some lines and keep others, like masks, put 'x' in actor field of the mask.
"combine tags (current overrides)" - tags from current + imported line get combined (current overrides imported)
"combine tags (imported overrides)" - same as above, but imported overrides current
- Both of these will also be ignored for imported lines that have "x" in actor field.
"don't shift times" - times of imported lines will be kept as they were saved
"delete original line" - this overrides the "keep line" option in the main menu.
(I thought it would be convenient to have it here.)
EXPORT SIGN - Saves the selected sign(s) either to 'signs.ass' or to a new file.
Effect field must contain the signs' names.
You can use relative or absolute paths. (Check the settings below.)
Default is the script's folder. If you want the default to be one folder up, use "..\".
You can use an absolute path, have one huge signs.ass there,
and have all the signs marked "show_name-sign_name" in the effect field.
IMPORT CHPTRS - Imports chapters from xml files - creates lines with "chptr" in actor and {ch. name} as text.
The path for importing things can be set in 'Save Config'. Default is script path.]]
help_u=[[
UPDATE LYRICS
This is probably the most complicated part, but if your songs have some massive styling with layers + tracking,
this will make updating lyrics, which would otherwise be a pain in the ass, really easy.
The only styling that will prevent this from working is inline tags - gradient by character etc.
The prerequisite here is that your OP/ED MUST have NUMBERED lines!
(See NUMBERS section - might be good to read that first.)
The numbers must correspond to the verses, not to lines in the script.
If line 1 of the SONG is mocha-tracked over 200 frames, all of those frames must be numbered 01.
It is thus most convenient to number the lines before you start styling, when it's still simple.
How this works:
Paste your updated lyrics into the large, top-left area of the GUI.
Use the Left and Right fields to set the markers to detect the right lines.
Without markers it will just look for numbers.
If your OP lines are numbered with "OP01eng", you must set "OP" under Left and "eng" under Right.
For now, everything is case-sensitive (I might change that later if it gets really annoying and pointless).
You must also correctly set the actor/effect choice in the bottom-right part of the GUI.
If you pasted lyrics, selected "update lyrics", and set markers and actor/effect.
Then hit Import, and lyrics will be updated.
How it works - example:
The lyrics you pasted in the data box get their lines assigned with numbers from 1 to whatever.
Let's say your markers are "OP01eng" and you're using the effect field.
The script looks for lines with that pattern in the effect field.
When it finds one, it reads the number (for example "01" from "OP01eng")
and replaces the line's text (skipping tags) with line 1 from the pasted lyrics.
For every line marked "OP##eng" it replaces the current lyrics with line ## from your pasted updated lyrics.
To make sure this doesn't fuck up tremendously, it shows you a log with all replacements at the end.
That's pretty much all you really need to know for updating lyrics, but there are a few more things.
If the script doesn't find any lines that match the markers, it gives you a message like this:
"The effect field of selected lines doesn't match given pattern..."
This means the lines either don't exist in your selection, or you probably forgot to set the markers.
"style restriction" is an extra option that lets you limit the replacing to lines whose style contains given pattern.
Let's give some examples:
You check the restriction and type "OP" in the field below.
You can now select the whole script instead of selecting only the OP lines,
and only lines with "OP" in style will be updated.
You may have the ED numbered the same way, but the "OP" restriction will ignore it.
This can be also useful if you have lines numbered just 01, 02 etc.,
and you have english and romaji, all mixed together.
If your styles are OP-jap and OP-eng, you can type "jap" in the restriction field if you're updating romaji
to make sure the script doesn't update the english lines as well (replacing them with romaji).
It is, however, recommended to just use different markers, like j01 / e01.]]
help_c=[[
- CHAPTERS -
This will generate chapters from the .ass file. Use 'Save Config' to set path for saving the .xml.
MARKER: For a line to be used for chapters, it has to be marked with "chapter"/"chptr"/"chap"
in actor/effect field (depending on settings) or the same 3 options as a separate comment, ie. {chapter} etc.
CHAPTER NAME: What will be used as chapter name.
It's either the content of the effect field, or the line's FIRST comment.
If the comment is {OP first frame} or {ED start}, the script will remove " first frame" or " start",
so you can keep those.
If you use default settings, just put "chapter" in actor field and make comments like {OP} or {Part A}.
Subchapters: You can make subchapters like this {Part A::Scene 5}.
This will be a subchapter of "Part A" called "Scene 5".
If you want a different LANGUAGE than 'eng', set it in the textbox below "chapter mark"
CHAPTER MARK: Sets the selected chapter for selected line(s). Uses marker and name. (Doesn't create xml.)
If you want a custom chapter name, type it in the textbox below this.
mp4-compatible chapters: switches to this format:
CHAPTER01=00:00:00.033
CHAPTER01NAME=Intro]]
help_n=[[
- NUMBERS -
This is a tool to number lines and add various markers to actor/effect fields.
The dropdown with "01" lets you choose how many leading zeros you want.
The Left and Right fields will add stuff to the numbers. If Left is "x" and Right is "yz",
the first marker will be "x01yz".
What makes this function much more versatile is the "Mod" field.
If you put in one number, then that's the number from which the numbering will start, so "5" -> 5, 6, 7, etc.
You can, however, use a comma or slash to modify the numbering some more.
"8,3" or "8/3" will start numbering from 8, repeating each number 3 times, so 8, 8, 8, 9, 9, 9, 10, 10, 10, etc.
This allows you to easily number lines that are typeset in layers etc.
Additionally, you can set a limit in [], for example 1/3[2], which will start from 1, use each number 3 times,
and only go up to 2 and then start again, so: 1 1 1 2 2 2 1 1 1 2 2 2
2/3[4] would give you 2 2 2 3 3 3 4 4 4 2 2 2 3 3 3 4 4 4 ...
"number 12321" works the same, but...
1. requires the limit in [] because...
2. numbers up and back, so 1[5] gives 1 2 3 4 5 4 3 2 1 2 3 4 5 4 3 2 ...
"add to marker" uses the Left and Right fields to add stuff to the current content of actor/effect/text.
If you number lines for the OP, you can set "OP-" in Left and "-eng" in Right to get "OP-01-eng".
(Mod does nothing when adding markers.)
"zero fill" finds numbers and fills them with zeroes based on the dropdown menu. Works only for actor, effect, and text.
For text, it skips tags and comments, as well as negative numbers and decimals.
"in text" works with "number lines".
The Marker field is ignored, and numbering is applied to numbers found in text.
In every line, wherever a number is detected in text, it's replaced by the number for that line.
Mostly useful for non-subbing purposes. Make 20 lines with: <img src="name01.jpg"><br>
Run the function and the number of the jpg will change for each line. (Copypaste back to html.)
"random" generates random numbers.
Use Left and Right fields to set the limits, e.g. from 1 to 100 or from -20 to 20.
Marker is where the numbers will appear. You can use effect, actor, comment (it'll be {random: #}),
or margins, though they can't have negatives and decimals, so the functionality is limited.
Rounding is set by the dropdown menu on the right: 1 is whole numbers, 01 can be 0.1, 001 -> 0.001, etc.
These numbers can then be used by other functions to modify various things (Relocator's Randomise).
Mod 1 is one result per line, Mod 2 will give the same result for each 2 lines, etc. This can be used to keep the same results for all layers on the same frame.]]
help_d=[[
- DO STUFF -
- Save/Load -
You can use this to save for example bits of text you need to paste frequently (like a multi-clipboard).
Paste text in the data area to save it. If the data area is empty, the function will load your saved texts.
- Replacer + Lua Patterns -
Use "Left" and "Right" for a lua regexp replace function.
- Replacer + Perl Regexp -
Use "Left" and "Right" for a perl regexp replace function.
- Lua Calc -
Use "Left" and "Right" with lua regexp to perform calculations on captured numbers.
Captures will be named a, b, c... up to p (16 captures max).
Functions are +, -, *, /, and round(a), which rounds the number captured in a.
> Example: (%d)(%d)(%d) -> a+1b*2c-3
This will match 3-digit patterns, add 1 to first digit, multiply the second by 2, and subtract 3 from the 3rd.
If you want to leave one of the captures as is, use .. to separate it from other letters: a+1b..c-3
> Example: pos%(([%d%.]+),([%d%.]+) -> pos(a+50,b-100
This will shift position right by 50 and up by 100.
- Split Text to Actor/Effect
This allows you to split off patterns of text and move them to Effect/Actor.
If text is "Abc/def/xyz", you put / in Left, and you set Marker to Effect, you'll get "Abc" in Effect and "def/xyz" in Text.
If you set Mod to "0", the separator will be included, so with } in Left, you can move start tags to Effect.
If you set Mod to "2", the separator will be left in Text.
If there's another separator in Right, there's a second split and the chunk goes to Actor. ("Abc" in Effect, "def" in Actor, "xyz" in Text.)
If you select Actor in Marker, it will be the primary target and Effect the secondary.
Obviously no other fields than Eff/Act are really useful for this, so they don't work.
If you repeat the function, new chunks will be added to Actor/Effect, so you can for example separate by \ and move stuff tag by tag.
(Text thus split can be put back together with MultiCopy's Attach function.)
- Reverse Text -
Reverses text (character by character). Nukes comments and inline tags.
- Reverse Words -
Reverses text (word by word). Nukes comments and inline tags.
- Fill Columns -
For selected columns, each line with an empty entry for that column (empty string or zero) will inherit the value from the previous selected line.
- Reverse Transforms -
\blur1\t(\blur3) becomes \blur3\t(\blur1). Only for initial tags. Only one transform for each tag.
- Fake Capitals -
Creates fake capitals by increasing font size for first letters.
With all caps, for first letters of words. With mixed text, for uppercase letters.
Set the \fs for the capitals in the Left field.
Looks like this: {\fs60}F{\fs}AKE {\fs60}C{\fs}APITALS
- Format Dates -
Formats dates to one of 4 options. Has its own GUI. Only converts from the other 3 options in the GUI.
- Split into Letters (alpha) -
Makes a line for each letter, making the other letters invisible with alpha.
This lets you do things with each letter separately.
- Explode -
This splits the line into letters and makes each of them move in a different direction and fade out.
- Dissolve Text -
Various modes of dissolving text. Has its own Help.
- Randomised Transforms -
Various modes of randomly transforming text. Has its own Help.
- Clone Clip -
Clones/replicates a clip you draw.
Set how many rows/columns and distances between them, and you can make large patterns.
- Clip2Margins -
Sets margins for the line from a rectangular clip if present. You can choose which margins to set.
Margins are set around the clip.
- Duplicate and Shift Lines -
Duplicates selected lines as many times you want before and/or after the current line.
Use Left/Right fields to set how many frames should be duplicated before/after the line.
\move and \t --> lines before get \pos with start coordinates and state before transforms;
lines after get end coordinates and state after transforms.
Lines are automatically numbered in Effect field. You can disable that by typing 0 in Mod field.
- Extrapolate Tracking -
Extrapolates position and scaling for beginning/end of a mocha-tracked line that didn't track,
usually because of a fade. Only works for linear movement/zoom.
If mocha doesn't track the first/last few frames, apply the data without those frames.
Mark the untracked frames with 'x' in Effect. Select those plus a few frames before or after, and use this.
How many frames to select will depend on how exactly "linear" the movement is.
With perfectly linear, you can select all the tracked lines to get a more accurate per-frame average.
If there seems to be a little bit of an acceleration, use only about 4-6 reference frames.
Requirements:
- All selected lines must be 1 frame long! (Use line2fbf if they aren't.)
- Lines to apply extrapolation to must be marked with 'x' in effect. The rest are reference lines.
- Selection must be consecutive, lines sorted by time, etc. Not fool-proofed for stupid shit.
- If you have several signs and/or layers, each must be done separately.
(Since layers will have the same values, you can use MultiCopy to copy from one to another.)
- Time by Frames -
Left = frames to shift start time by, each line (2 = each new line starts 2 frames later than previous)
Right = frames to shift end time by, each line (4 = each new line ends 4 frames later than previous)
- Convert Framerate -
Converts framerate from a to b where a is the input from "Left" and b is input from "Right".
- Fix Kara Tags for fbf Lines -
If you need to split a line with kara tags, this adjusts the tags so that the text appears continuously as it should.
Selection must include the first line for reference. Applies to all karaoke tags indiscriminately - \k, \kf, \ko.
- Make Style from Act. Line -
Creates a new style from the values in start tags in active line combined with the current style.
- Make Comments Visible -
Nukes { } from comments, thus making them part of the text visible on screen.
- Switch Commented/Visible -
Comments out what's visible and makes visible what's commented. Allows switching between two texts.
- Honorificslaughterhouse -
Comments out honorifics.]]
function switch(subs,sel)
res={}
res.dat=''
res.stuff='switch commented/visible'
stuff(subs,sel)
end
function rvrstxt(subs,sel)
res={}
res.dat=''
res.stuff='reverse text'
stuff(subs,sel)
end
function rvrswrds(subs,sel)
res={}
res.dat=''
res.stuff='reverse words'
stuff(subs,sel)
end
if haveDepCtrl then
depRec:registerMacros({
{script_name,script_description,significance},
{": Non-GUI macros :/Significance: Switch commented && visible text","Switch commented & visible text",switch},
{": Non-GUI macros :/Significance: Reverse text","Reverse text",rvrstxt},
{": Non-GUI macros :/Significance: Reverse words","Reverse words",rvrswrds},
},false)
else
aegisub.register_macro(script_name,script_description,significance)
aegisub.register_macro(": Non-GUI macros :/Significance: Switch commented && visible text","Switch commented & visible text",switch)
aegisub.register_macro(": Non-GUI macros :/Significance: Reverse text","Reverse text",rvrstxt)
aegisub.register_macro(": Non-GUI macros :/Significance: Reverse words","Reverse words",rvrswrds)
end