├── README.md ├── image ├── 1.cpp ├── menu.png ├── 删除1.png ├── 删除2.png ├── 歌词1.png ├── 歌词2.png ├── 添加内联特效1.png └── 添加内联特效2.png ├── svg转ass绘图v1.2.zip ├── 【aeg插件】一键导出选中行音频v2.2.zip ├── 一键获取tc评论v1.1.zip ├── 多重边框生成v1.2.lua ├── 小工具集合v3.0.lua ├── 查闪轴1.0.lua ├── 歌词处理小插件2.0.lua ├── 给字幕两侧添加图片.lua ├── 获取up主视频数据v0.9.zip └── 轴校工具v5.3修复bug2.zip /README.md: -------------------------------------------------------------------------------- 1 | # 处理歌词插件 2 |
3 | 4 | ## 1.中日歌词分离 5 | 6 | 用于分离你从网易云或者萌娘百科上直接复制粘贴进aeg里的插件。就省的你之前还要先粘贴进文本文档里删空白行,手动把中日分开之类的很繁琐的操作。 7 | 一般从网易云或者萌娘百科上,直接复制粘贴进aeg的形式会是这样的 8 | 9 |
10 | 处理完之后将中日分开方便操作,会识别空白行分开 11 | 12 |
13 | 14 | 歌词必须是严格的中日一行一行对应,空白行有多少行无所谓 15 | 16 | ## 2.全角空格转半角空格 17 | 全角空格好像会给位置坐标带来问题。顾名思义,替换选中行中的全角空格 18 | 19 | ## 3.删除所有K标签 20 |
21 | 22 |
23 | 24 | ## 4.一键添加内联特效标签 25 | 有时候需要给多行同时添加内联特效,有时候是整行,有时候是添加第一个。可以自行修改,第二个设为空就只设定第一个 26 | 27 |
28 | 29 |
30 | -------------------------------------------------------------------------------- /image/1.cpp: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /image/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/image/menu.png -------------------------------------------------------------------------------- /image/删除1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/image/删除1.png -------------------------------------------------------------------------------- /image/删除2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/image/删除2.png -------------------------------------------------------------------------------- /image/歌词1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/image/歌词1.png -------------------------------------------------------------------------------- /image/歌词2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/image/歌词2.png -------------------------------------------------------------------------------- /image/添加内联特效1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/image/添加内联特效1.png -------------------------------------------------------------------------------- /image/添加内联特效2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/image/添加内联特效2.png -------------------------------------------------------------------------------- /svg转ass绘图v1.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/svg转ass绘图v1.2.zip -------------------------------------------------------------------------------- /【aeg插件】一键导出选中行音频v2.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/【aeg插件】一键导出选中行音频v2.2.zip -------------------------------------------------------------------------------- /一键获取tc评论v1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/一键获取tc评论v1.1.zip -------------------------------------------------------------------------------- /多重边框生成v1.2.lua: -------------------------------------------------------------------------------- 1 | -- 更新: 2 | -- 1.增加了对原有行存在的\frz(即绕z轴旋转标签)的自适应 3 | -- 2.增加了能够将常用边框效果存储为预设的功能,可以从预设打开之前存储的多层样式 4 | -- 3.新增了可以根据选中样式进行应用的模式,针对行数和样式都很多,且只想对某种样式应用,多选比较麻烦的情况下使用,非常方便快捷。(21.01.09 5 | -- 功能: 6 | -- 覆盖原有行边框,生成指定多重边框字幕 7 | -- 注意: 8 | -- 1.多重边框情况下不支持设置透明边框(暂时将原来计划的透明度效果去除) 9 | -- 2.会将原有行注释,生成新行代替原有字幕 10 | -- 11 | local tr = aegisub.gettext 12 | script_name = tr("多重边框") 13 | script_description = tr("多重边框") 14 | script_author = "拉姆0v0" 15 | script_version = "v1.2" 16 | 17 | include("karaskel.lua") 18 | 19 | fp=aegisub.decode_path("?data").."\\border_data.lua" 20 | -- 序列化和反序列化方法 21 | function serialize(obj) 22 | local lua = "" 23 | local t = type(obj) 24 | if t == "number" then 25 | lua = lua .. obj 26 | elseif t == "boolean" then 27 | lua = lua .. tostring(obj) 28 | elseif t == "string" then 29 | lua = lua .. string.format("%q", obj) 30 | elseif t == "table" then 31 | lua = lua .. "{\n" 32 | for k, v in pairs(obj) do 33 | lua = lua .. "[" .. serialize(k) .. "]=" .. serialize(v) .. ",\n" 34 | end 35 | local metatable = getmetatable(obj) 36 | if metatable ~= nil and type(metatable.__index) == "table" then 37 | for k, v in pairs(metatable.__index) do 38 | lua = lua .. "[" .. serialize(k) .. "]=" .. serialize(v) .. ",\n" 39 | end 40 | end 41 | lua = lua .. "}" 42 | elseif t == "nil" then 43 | return nil 44 | else 45 | error("can not serialize a " .. t .. " type.") 46 | end 47 | return lua 48 | end 49 | 50 | function unserialize(lua) 51 | local t = type(lua) 52 | if t == "nil" or lua == "" then 53 | return nil 54 | elseif t == "number" or t == "string" or t == "boolean" then 55 | lua = tostring(lua) 56 | else 57 | error("can not unserialize a " .. t .. " type.") 58 | end 59 | lua = "return " .. lua 60 | local func = loadstring(lua) 61 | if func == nil then 62 | return nil 63 | end 64 | return func() 65 | end 66 | 67 | -- html颜色转# 68 | function coloralpha2assCA(coloralpha) 69 | local length = #coloralpha 70 | -- local A = coloralpha:sub(length-1, length) 71 | -- local B = coloralpha:sub(length-3, length-2) 72 | -- local G = coloralpha:sub(length-5, length-4) 73 | -- local R = coloralpha:sub(length-7, length-6) 74 | -- return "H"..B..G..R, "H"..A 75 | local B = coloralpha:sub(length-1, length) 76 | local G = coloralpha:sub(length-3, length-2) 77 | local R = coloralpha:sub(length-5, length-4) 78 | return "H"..B..G..R, "H00" 79 | end 80 | 81 | -- 获取字幕行的pos 82 | function getPosFromText(text) 83 | local out = text:match("[%-?%d+%.*%d*]+[,]+[%-?%d+%.*%d*]+") 84 | if out ~= nil then 85 | return out 86 | else 87 | return "" 88 | end 89 | end 90 | 91 | -- 获取字幕行的frz 92 | function getFrz(text) 93 | local frz = text:match("\\frz%-?%d+%.*%d*") 94 | if frz ~= nil then 95 | return frz 96 | else 97 | return "" 98 | end 99 | end 100 | 101 | -- 对选中行应用 102 | function apply_to_selected_rows(subs, sel, meta, styles, tags_template, border_data) 103 | -- 对每一行分别应用多层边框 104 | for i = 1, #sel do 105 | local pos_x 106 | local pos_y 107 | -- 分别判断每行是不是自带pos 108 | local l = subs[sel[i]] 109 | local simple_l = subs[sel[i]] 110 | karaskel.preproc_line(subs, meta, styles, l) 111 | local postag_str = getPosFromText(l.text) 112 | local frztag_str = getFrz(l.text) 113 | if postag_str == "" then 114 | 115 | pos_x = l.x 116 | pos_y = l.y 117 | else 118 | pos_x = tonumber(postag_str:sub(1,postag_str:find("[,]+")-1)) 119 | pos_y = tonumber(postag_str:sub(postag_str:find("[,]+")+1,-1)) 120 | end 121 | local bord = 0 122 | -- 对每个边框生成 123 | for j = 1, #border_data do 124 | bord = bord + tonumber(border_data[j].houdu) 125 | local text = tags_template:format(pos_x, pos_y, bord, border_data[j].color, border_data[j].alpha, (frztag_str == "" and "" or frztag_str))..l.text:gsub("{[^}]+}", "") 126 | l.layer = #border_data-j+1 127 | l.text = text 128 | subs.append(l) 129 | end 130 | simple_l.comment = true 131 | subs[sel[i]] = simple_l 132 | end 133 | end 134 | 135 | -- 对选中样式应用 136 | function apply_to_style(subs, sel, meta, styles, tags_template, border_data) 137 | local styles_name_table = {} 138 | for i=1,styles.n do 139 | table.insert(styles_name_table, styles[i].name) 140 | end 141 | local dialog_config4 = { 142 | [1] = { class = "label", x = 0, y = 0, label = "选择要应用的样式:" }, 143 | [2] = { class="dropdown", name="seleted_style", x=1, y=0 , width=5, height=1, items={}, value=""}, 144 | } 145 | dialog_config4[2].items = styles_name_table 146 | dialog_config4[2].value = dialog_config4[2].items[1] 147 | local buttons4, results4 = aegisub.dialog.display(dialog_config4, {"Ok", "Cancel"}) 148 | if buttons4 == "Ok" then 149 | local seleted_style = results4["seleted_style"] 150 | if seleted_style ~= "" then 151 | -- 对每一行分别应用多层边框 152 | for i = 1, #subs do 153 | if subs[i].class == "dialogue" and subs[i].comment == false and subs[i].style == seleted_style then 154 | local pos_x 155 | local pos_y 156 | -- 分别判断每行是不是自带pos 157 | local l = subs[i] 158 | local simple_l = subs[i] 159 | karaskel.preproc_line(subs, meta, styles, l) 160 | local postag_str = getPosFromText(l.text) 161 | local frztag_str = getFrz(l.text) 162 | if postag_str == "" then 163 | 164 | pos_x = l.x 165 | pos_y = l.y 166 | else 167 | pos_x = tonumber(postag_str:sub(1,postag_str:find("[,]+")-1)) 168 | pos_y = tonumber(postag_str:sub(postag_str:find("[,]+")+1,-1)) 169 | end 170 | local bord = 0 171 | -- 对每个边框生成 172 | for j = 1, #border_data do 173 | bord = bord + tonumber(border_data[j].houdu) 174 | local text = tags_template:format(pos_x, pos_y, bord, border_data[j].color, border_data[j].alpha, (frztag_str == "" and "" or frztag_str))..l.text:gsub("{[^}]+}", "") 175 | l.layer = #border_data-j+1 176 | l.text = text 177 | subs.append(l) 178 | end 179 | simple_l.comment = true 180 | subs[i] = simple_l 181 | end 182 | end 183 | else 184 | aegisub.debug.out("你选择要应用的样式为空!") 185 | end 186 | else 187 | aegisub.debug.out("已取消!") 188 | end 189 | end 190 | 191 | -- 保存样式 192 | function save_as(dialog_config3,data_reader_table,border_num,fp) 193 | buttons3, results3 = aegisub.dialog.display(dialog_config3, {"OK", "Cancel"}) 194 | if buttons3 == "OK" then 195 | local save_name = results3["yushename"] 196 | if save_name ~= "" then 197 | local alive = false 198 | for k, v in pairs(data_reader_table) do 199 | if v.name == save_name then 200 | alive = true 201 | break 202 | end 203 | end 204 | if alive == false then 205 | local border_data = {} 206 | for i = 1, border_num do 207 | local color, alpha = coloralpha2assCA(results2["ca_" .. i]) 208 | local houdu = results2["houdu_" .. i] 209 | border_data[i] = { 210 | ["color"] = color, 211 | ["alpha"] = alpha, 212 | ["houdu"] = houdu 213 | } 214 | end 215 | table.insert(data_reader_table, {["name"] = save_name,["data"] = border_data}) 216 | local data_w = io.open(fp, "w+") 217 | data_w:write(serialize(data_reader_table)) 218 | data_w:close() 219 | aegisub.debug.out("已保存!") 220 | else 221 | aegisub.debug.out("已存在同名预设!") 222 | end 223 | else 224 | aegisub.debug.out("预设名不得为空!") 225 | end 226 | else 227 | aegisub.debug.out("已取消保存!") 228 | end 229 | end 230 | 231 | -- 生成边框设置界面config 232 | function get_border_setting_config(border_num_OR_data_table_yushe, conf_template) 233 | local dialog_config2={} 234 | for i = 1, (type(border_num_OR_data_table_yushe) == "table" and #border_num_OR_data_table_yushe or border_num_OR_data_table_yushe) do 235 | dialog_config2[(i-1)*5+1] = unserialize(conf_template[1]:format(i-1, i)) 236 | dialog_config2[(i-1)*5+2] = unserialize(conf_template[2]:format(i-1)) 237 | dialog_config2[(i-1)*5+3] = unserialize(conf_template[3]:format(i, i-1, (type(border_num_OR_data_table_yushe) == "table" and border_num_OR_data_table_yushe[i].color or "HFFFFFF"))) 238 | dialog_config2[(i-1)*5+4] = unserialize(conf_template[4]:format(i-1)) 239 | dialog_config2[(i-1)*5+5] = unserialize(conf_template[5]:format(i, i-1, (type(border_num_OR_data_table_yushe) == "table" and border_num_OR_data_table_yushe[i].houdu or "1"))) 240 | end 241 | return dialog_config2 242 | end 243 | 244 | -- 边框数据转换 245 | function get_borderdata_form_results2(border_num,results2) 246 | local border_data = {} 247 | for i = 1, border_num do 248 | local color, alpha = coloralpha2assCA(results2["ca_" .. i]) 249 | local houdu = results2["houdu_" .. i] 250 | border_data[i] = { 251 | ["color"] = color, 252 | ["alpha"] = alpha, 253 | ["houdu"] = houdu 254 | } 255 | end 256 | return border_data 257 | end 258 | 259 | dialog_config1= 260 | { 261 | [1]={class="label",x=0,y=0,label="新建多重边框层数:"}, 262 | [2]={class="intedit",name="border_num",x=1,y=0,width=1,height=1,value="1",hint="请输入整数"}, 263 | [3]={class="label",x=0,y=1,label="打开选择预设:"}, 264 | [4]={class="dropdown",name="selete",x=1,y=1,width=5,height=1,items={},value=""}, 265 | } 266 | 267 | dialog_config3= 268 | { 269 | [1]={class="label",x=0,y=0,label="给你的预设起个名字吧:"}, 270 | [2]={class="edit",name="yushename",x=1,y=0,width=5,height=1,value="预设1",hint="输入你的名字"}, 271 | } 272 | 273 | local conf_template ={ 274 | [1]='{class="label",x=0,y=%s,label="【第%s层】"}', 275 | [2]='{class="label",x=1,y=%s,label="颜色:"}', 276 | [3]='{class="color",name="ca_%s",x=2,y=%s,width=1,height=1,value="%s"}', 277 | [4]='{class="label",x=3,y=%s,label="厚度:"}', 278 | [5]='{class="edit",name="houdu_%s",x=4,y=%s,width=1,height=1,value="%s"}', 279 | } 280 | 281 | local tags_template = "{\\pos(%s,%s)\\bord%s\\shad0\\3c&%s&\\3a&%s&%s}" 282 | 283 | function border(subs,sel) 284 | -- 读取库数据 285 | local data=io.open(fp,"a+") 286 | local data_str = data:read('*a') 287 | data:close() 288 | if data_str == "" then 289 | local data_w =io.open(fp,"w+") 290 | data_w:write(serialize({})) 291 | data_w:close() 292 | data_str = "{}" 293 | end 294 | -- config设置 295 | local data_reader_table = unserialize(data_str) 296 | local select_table = {} 297 | for k,v in pairs(data_reader_table) do 298 | table.insert(select_table,v.name) 299 | end 300 | dialog_config1[4].items = select_table 301 | dialog_config1[4].value = dialog_config1[4].items[1] 302 | -- 初始化层数 303 | local buttons1 = "Delete preset" 304 | while buttons1=="Delete preset" do 305 | buttons1,results1 = aegisub.dialog.display(dialog_config1,{"New","Open preset","Delete preset","Cancel"}) 306 | if buttons1=="Delete preset" then 307 | local delete_name = results1["selete"] 308 | for k,v in pairs(data_reader_table) do 309 | if v.name == delete_name then 310 | table.remove(data_reader_table,k) 311 | local data_w = io.open(fp, "w+") 312 | data_w:write(serialize(data_reader_table)) 313 | data_w:close() 314 | local after_select_table = {} 315 | for k,v in pairs(data_reader_table) do 316 | table.insert(after_select_table,v.name) 317 | end 318 | dialog_config1[4].items = after_select_table 319 | dialog_config1[4].value = dialog_config1[4].items[1] 320 | end 321 | end 322 | end 323 | end 324 | if buttons1 == "New" then 325 | local border_num = results1["border_num"] 326 | if border_num > 0 then 327 | -- 主体 328 | -- 生成dialog 329 | local dialog_config2=get_border_setting_config(border_num, conf_template) 330 | buttons2,results2 =aegisub.dialog.display(dialog_config2,{"Apply to selected rows","Apply to style", "save as", "Cancel"}) 331 | if buttons2 == "Apply to selected rows" then 332 | local meta, styles = karaskel.collect_head(subs, false) 333 | local border_data = get_borderdata_form_results2(border_num,results2) 334 | apply_to_selected_rows(subs, sel, meta, styles, tags_template, border_data) 335 | elseif buttons2 == "Apply to style" then 336 | local meta, styles = karaskel.collect_head(subs, false) 337 | local border_data = get_borderdata_form_results2(border_num,results2) 338 | apply_to_style(subs, sel, meta, styles, tags_template, border_data) 339 | elseif buttons2 == "save as" then 340 | save_as(dialog_config3,data_reader_table,border_num,fp) 341 | else 342 | aegisub.debug.out("已取消!") 343 | end 344 | else 345 | aegisub.debug.out("层数应大于0!") 346 | end 347 | elseif buttons1 == "Open preset" then 348 | local yushe_name = results1["selete"] 349 | local data_table_yushe = "" 350 | for k,v in pairs(data_reader_table) do 351 | if v.name == yushe_name then 352 | data_table_yushe = v.data 353 | break 354 | else 355 | data_table_yushe = "" 356 | end 357 | end 358 | if data_table_yushe ~= "" then 359 | -- 主体 360 | -- 生成dialog 361 | local dialog_config2 = get_border_setting_config(data_table_yushe, conf_template) 362 | buttons2, results2 = aegisub.dialog.display(dialog_config2, {"Apply to selected rows","Apply to style", "save as", "Cancel"}) 363 | if buttons2 == "Apply to selected rows" then 364 | local meta, styles = karaskel.collect_head(subs, false) 365 | local border_data = get_borderdata_form_results2(#data_table_yushe,results2) 366 | apply_to_selected_rows(subs, sel, meta, styles, tags_template, border_data) 367 | elseif buttons2 == "Apply to style" then 368 | local meta, styles = karaskel.collect_head(subs, false) 369 | local border_data = get_borderdata_form_results2(#data_table_yushe,results2) 370 | apply_to_style(subs, sel, meta, styles, tags_template, border_data) 371 | elseif buttons2 == "save as" then 372 | save_as(dialog_config3,data_reader_table,#data_table_yushe,fp) 373 | else 374 | aegisub.debug.out("已取消!") 375 | end 376 | else 377 | aegisub.debug.out("不可打开空预设!") 378 | end 379 | else 380 | aegisub.debug.out("已取消!") 381 | end 382 | end 383 | 384 | TLL_macros = { 385 | { 386 | script_name = "多重边框v1.2", 387 | script_description = "设置多重边框", 388 | entry = function(subs,sel) border(subs,sel) end, 389 | validation = false 390 | }, 391 | } 392 | 393 | for i = 1, #TLL_macros do 394 | aegisub.register_macro(script_name.." "..script_version.."/"..TLL_macros[i]["script_name"], TLL_macros[i]["script_description"], TLL_macros[i]["entry"], TLL_macros[i]["validation"]) 395 | end -------------------------------------------------------------------------------- /小工具集合v3.0.lua: -------------------------------------------------------------------------------- 1 | local tr = aegisub.gettext 2 | script_name = tr("小工具合集") 3 | script_description = tr("用于处理你不知道从哪里找来的混搭歌词") 4 | script_author = "拉姆0v0" 5 | script_version = "v3.0" 6 | 7 | function text_processing(subs,sel) 8 | local line_storage1={} 9 | local line_storage2={} 10 | local line_storageblank={} 11 | local line_storagetext={} 12 | local num=#sel 13 | for i = 1, num do 14 | if (subs[sel[i]].text=="" or string.match(subs[sel[i]].text,"^[ ]*$") or string.match(subs[sel[i]].text,"^[ ]*$")) then 15 | table.insert(line_storageblank,subs[sel[i]]) 16 | else 17 | table.insert(line_storagetext,subs[sel[i]]) 18 | end 19 | end 20 | for i,v in ipairs(line_storagetext) do 21 | if i%2==1 then 22 | table.insert(line_storage1,v) 23 | else 24 | table.insert(line_storage2,v) 25 | end 26 | end 27 | for i = 1, num do 28 | aegisub.progress.set(i/num*100) 29 | if i<=#line_storagetext then 30 | if i<=(#line_storagetext/2) then 31 | subs[sel[i]]=line_storage1[i] 32 | else 33 | subs[sel[i]]=line_storage2[i-(#line_storagetext/2)] 34 | end 35 | else 36 | subs[sel[i]]=line_storageblank[i-#line_storagetext] 37 | end 38 | end 39 | aegisub.debug.out("已将中日歌词分开,空白行或者空格行位于最下方") 40 | end 41 | 42 | function full_to_half(subs,sel) 43 | for i = 1, #sel do 44 | aegisub.progress.set(i/#sel*100) 45 | local subp = subs[sel[i]] 46 | subp.text = string.gsub(subp.text, " ", " ") 47 | subs[sel[i]] = subp 48 | end 49 | end 50 | 51 | function delete_k(subs,sel) 52 | for i = 1, #sel do 53 | aegisub.progress.set(i/#sel*100) 54 | local subp = subs[sel[i]] 55 | subp.text = string.gsub(subp.text, "{\\k[1-9]%d*}", "") 56 | subs[sel[i]] = subp 57 | end 58 | end 59 | 60 | dialog_config1= 61 | { 62 | 63 | [1]={class="label",x=0,y=1,label="第二个音节可以设置为空,即只在第一个音节出设置内联特效"}, 64 | 65 | [2]={class="label",x=0,y=1,label=""}, 66 | [3]={class="label",x=0,y=2,label="在第一个音节设置内联特效"}, 67 | [4]={class="edit",name="在第一个音节设置内联特效",x=1,y=2,width=1,height=1,items={},value="\\-A"}, 68 | 69 | [5]={class="label",x=0,y=3,label=""}, 70 | [6]={class="label",x=0,y=4,label="在第二个音节设置内联特效"}, 71 | [7]={class="edit",name="在第二个音节设置内联特效",x=1,y=4,width=1,height=1,items={},value="\\-B"}, 72 | 73 | [8]={class="label",x=0,y=5,label=""}, 74 | [9]={class="label",x=0,y=6,label="小提示:按OK做处理后,可以ctrl-z撤销本处理,还有ctrl-y反撤销"}, 75 | 76 | } 77 | 78 | function add_fx(subs,sel) 79 | buttons,results =aegisub.dialog.display(dialog_config1,{"OK","Cancel"}) 80 | if buttons=="OK" then 81 | local fx1 = results["在第一个音节设置内联特效"] 82 | local fx2 = results["在第二个音节设置内联特效"] 83 | for i = 1, #sel do 84 | aegisub.progress.set(i/#sel*100) 85 | local subp = subs[sel[i]] 86 | local t = string.match(subp.text, "^[^}]+") 87 | subp.text = string.gsub(subp.text, "[}]", fx2.."}",2) 88 | subp.text = string.gsub(subp.text, "^[^}]+", t..fx1) 89 | subs[sel[i]] = subp 90 | end 91 | end 92 | end 93 | 94 | function hexstring2number(hexstring, len) 95 | if not len or len > 8 then return end 96 | 97 | local hexbyte = {} 98 | for i = 1, len do 99 | hexbyte[i] = string.byte(hexstring, i) 100 | end 101 | 102 | local detection=0x10 103 | local num_string = {} 104 | for i = 1, len do 105 | if hexbyte[i]>=detection then 106 | num_string[i]=string.format("%x", hexbyte[i]) 107 | else 108 | num_string[i]="0"..string.format("%x", hexbyte[i]) 109 | end 110 | end 111 | 112 | 113 | local num = tonumber("0x"..num_string[1]..num_string[2]..num_string[3]..num_string[4]) 114 | return num 115 | end 116 | 117 | function get_png_size(path) 118 | local png_file = io.open(path, "rb") 119 | local data = png_file:read("*all") 120 | 121 | 122 | -- 保证png至少有37个字节,因为包含文件头等起码就超过这个数字了 123 | if #data < 37 then return end 124 | 125 | -- 文件头的相关信息请百度 126 | local png_header_info = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52} 127 | for i = 1, #png_header_info do 128 | if (string.byte(data, i) ~= png_header_info[i]) then 129 | return 130 | end 131 | end 132 | 133 | -- 这四个字节表示png的宽度 134 | data = string.sub(data, #png_header_info + 1) 135 | local width = hexstring2number(data, 4) 136 | 137 | data = string.sub(data, 5) 138 | local height = hexstring2number(data, 4) 139 | 140 | return width, height 141 | end 142 | 143 | --dialog_config2= 144 | -- { 145 | -- [1]={class="label",x=0,y=0,label="1.使用此功能时,请确认你的aegisub有安装VSFilterMod滤镜。\n2.VSFilterMod只支持24或32位带或不带透明通道的真彩色png格式的图片!\n3.该功能导入的图片无法修改显示大小,请选择大小合适的png格式图片!\nps:你的图片文件名如果过于诡异可能会失败,例如“7J`%TFT)K9]JUL3KF`@26$Y”之类的\n\nby 拉姆0v0"}, 146 | -- } 147 | 148 | function pngmassage_test(subs,sel) 149 | filename = aegisub.dialog.open('请选择一张png图片', '', '','Text files (.png)|*.png', false, true) 150 | if filename==nil then 151 | return 152 | end 153 | local x,y=get_png_size(filename) 154 | local size_x,size_y 155 | for i=1,#subs do 156 | if subs[i].key == "PlayResX" then 157 | size_x=subs[i].value 158 | end 159 | if subs[i].key == "PlayResY" then 160 | size_y=subs[i].value 161 | end 162 | if subs[i].class == "dialogue" then 163 | local l=subs[i] 164 | l.start_time=0 165 | l.end_time=5000 166 | if size_x==nil then 167 | aegisub.debug.out("请打开一个视频或者使用空白视频,以获取ass脚本分辨率信息!!!!!") 168 | return 169 | end 170 | local text1="{\\p1\\an2\\bord0\\shad0\\1img("..filename..")\\pos("..tostring(tonumber(size_x)/2)..","..tostring(tonumber(size_y)-50)..")}m 0 0 l "..tostring(x).." 0 l "..tostring(x).." "..tostring(y).." l 0 "..tostring(y) 171 | l.text=text1 172 | l.effect="" 173 | l.style="Default" 174 | l.comment = false 175 | subs.insert(i,l) 176 | break 177 | end 178 | end 179 | aegisub.debug.out("已完成!!!!!!\n\n文件路径:"..filename.."\n图片分辨率:"..tostring(x).."x"..tostring(y).."\n\n若显示为白色空白:\n1.请检查是否安装了最新版本的VSFilterMod\n2.请检查你所选择的图片名称是否存在非法字符,可以试着修改文件名为比较正常的文件名\n\nby 拉姆0v0") 180 | end 181 | 182 | TLL_macros = { 183 | { 184 | script_name = "中日歌词分离v2.0", 185 | script_description = "用于将中日穿插排版的歌词分离开来", 186 | entry = function(subs,sel) text_processing(subs,sel) end, 187 | validation = false 188 | }, 189 | { 190 | script_name = "全角空格 to 半角空格", 191 | script_description = "用于将歌词中的全角空格转换为半角空格", 192 | entry = function(subs,sel) full_to_half(subs,sel) end, 193 | validation = false 194 | }, 195 | { 196 | script_name = "删除所选行k标签", 197 | script_description = "用于删除所选行k标签", 198 | entry = function(subs,sel) delete_k(subs,sel) end, 199 | validation = false 200 | }, 201 | { 202 | script_name = "一键添加内联特效标签", 203 | script_description = "用于添加内联特效标签", 204 | entry = function(subs,sel) add_fx(subs,sel) end, 205 | validation = false 206 | }, 207 | { 208 | script_name = "一键插入图片v2.0", 209 | script_description = "免去调整图片大小的功能,一键插入图片", 210 | entry = function(subs,sel) pngmassage_test(subs,sel) end, 211 | validation = false 212 | }, 213 | } 214 | 215 | for i = 1, #TLL_macros do 216 | aegisub.register_macro(script_name.." "..script_version.."/"..TLL_macros[i]["script_name"], TLL_macros[i]["script_description"], TLL_macros[i]["entry"], TLL_macros[i]["validation"]) 217 | end 218 | -------------------------------------------------------------------------------- /查闪轴1.0.lua: -------------------------------------------------------------------------------- 1 | local tr = aegisub.gettext 2 | script_name = tr("轴检查") 3 | script_description = tr("脱裤子放屁工具") 4 | script_author = "拉姆0v0" 5 | script_version = "v1.0" 6 | 7 | dialog_config1= 8 | { 9 | 10 | [1]={class="label",x=0,y=0,label="可自由设置检查的时间间隔"}, 11 | [2]={class="label",x=0,y=1,label="设置要检查的前后之间的时间间隔:"}, 12 | [3]={class="intedit",name="时间间隔",x=1,y=1,width=1,height=1,items={},value=300,hint="请输入整数"}, 13 | 14 | } 15 | function text_processing(subs,sel) 16 | buttons,results =aegisub.dialog.display(dialog_config1,{"OK","Cancel"}) 17 | if buttons=="OK" then 18 | local timex = results["时间间隔"] 19 | local stylen={} 20 | local stylenline={} 21 | local shannum={} 22 | for i=1,#subs do 23 | if subs[i].class == "dialogue" and subs[i].comment == false then 24 | if #stylen == 0 then 25 | table.insert(stylen,subs[i].style) 26 | table.insert(shannum,0) 27 | table.insert(stylenline,{i}) 28 | else 29 | local isin=0 30 | for j=1,#stylen do 31 | if subs[i].style == stylen[j] then 32 | table.insert(stylenline[j],i) 33 | isin=1 34 | break 35 | end 36 | end 37 | if isin==0 then 38 | table.insert(stylen,subs[i].style) 39 | table.insert(shannum,0) 40 | table.insert(stylenline,{i}) 41 | end 42 | end 43 | end 44 | end 45 | --查找闪轴部分 46 | aegisub.debug.out("在不同样式中,前后相差小于"..tostring(timex).."毫秒的情况:\n\n") 47 | for i=1,#stylen do 48 | aegisub.debug.out(stylen[i]..":") 49 | for j=1,(#stylenline[i]-1) do 50 | if (subs[stylenline[i][j+1]].start_time-subs[stylenline[i][j]].end_time <= timex) and (subs[stylenline[i][j+1]].start_time-subs[stylenline[i][j]].end_time > 0) then 51 | local subp = subs[stylenline[i][j]] 52 | subp.actor = "(该样式的此行与下一行之间的间隔小于"..tostring(timex).."毫秒,请调整)"..subp.actor 53 | subs[stylenline[i][j]] = subp 54 | 55 | 56 | shannum[i]=shannum[i]+1 57 | end 58 | end 59 | aegisub.debug.out(tostring(shannum[i]).."处\n") 60 | end 61 | aegisub.debug.out("\n具体位置已标记在说话人一栏,请查看。\n\nby 拉姆0v0") 62 | end 63 | end 64 | 65 | 66 | TLL_macros = { 67 | { 68 | script_name = "检查闪轴", 69 | script_description = "检查闪轴", 70 | entry = function(subs,sel) text_processing(subs,sel) end, 71 | validation = false 72 | }, 73 | 74 | } 75 | 76 | for i = 1, #TLL_macros do 77 | aegisub.register_macro(script_name.." "..script_version.."/"..TLL_macros[i]["script_name"], TLL_macros[i]["script_description"], TLL_macros[i]["entry"], TLL_macros[i]["validation"]) 78 | end 79 | -------------------------------------------------------------------------------- /歌词处理小插件2.0.lua: -------------------------------------------------------------------------------- 1 | local tr = aegisub.gettext 2 | script_name = tr("歌词处理") 3 | script_description = tr("用于处理你不知道从哪里找来的混搭歌词") 4 | script_author = "拉姆0v0" 5 | script_version = "2.0" 6 | 7 | function text_processing(subs,sel) 8 | local line_storage1={} 9 | local line_storage2={} 10 | local line_storageblank={} 11 | local line_storagetext={} 12 | local num=#sel 13 | for i = 1, num do 14 | if (subs[sel[i]].text=="" or string.match(subs[sel[i]].text,"^[ ]*$") or string.match(subs[sel[i]].text,"^[ ]*$")) then 15 | table.insert(line_storageblank,subs[sel[i]]) 16 | else 17 | table.insert(line_storagetext,subs[sel[i]]) 18 | end 19 | end 20 | for i,v in ipairs(line_storagetext) do 21 | if i%2==1 then 22 | table.insert(line_storage1,v) 23 | else 24 | table.insert(line_storage2,v) 25 | end 26 | end 27 | for i = 1, num do 28 | aegisub.progress.set(i/num*100) 29 | if i<=#line_storagetext then 30 | if i<=(#line_storagetext/2) then 31 | subs[sel[i]]=line_storage1[i] 32 | else 33 | subs[sel[i]]=line_storage2[i-(#line_storagetext/2)] 34 | end 35 | else 36 | subs[sel[i]]=line_storageblank[i-#line_storagetext] 37 | end 38 | end 39 | aegisub.debug.out("已将中日歌词分开,空白行或者空格行位于最下方") 40 | end 41 | 42 | function full_to_half(subs,sel) 43 | for i = 1, #sel do 44 | aegisub.progress.set(i/#sel*100) 45 | local subp = subs[sel[i]] 46 | subp.text = string.gsub(subp.text, " ", " ") 47 | subs[sel[i]] = subp 48 | end 49 | end 50 | 51 | function delete_k(subs,sel) 52 | for i = 1, #sel do 53 | aegisub.progress.set(i/#sel*100) 54 | local subp = subs[sel[i]] 55 | subp.text = string.gsub(subp.text, "{\\k[1-9]%d*}", "") 56 | subs[sel[i]] = subp 57 | end 58 | end 59 | 60 | dialog_config= 61 | { 62 | 63 | [1]={class="label",x=0,y=1,label="第二个音节可以设置为空,即只在第一个音节出设置内联特效"}, 64 | 65 | [2]={class="label",x=0,y=1,label=""}, 66 | [3]={class="label",x=0,y=2,label="在第一个音节设置内联特效"}, 67 | [4]={class="edit",name="在第一个音节设置内联特效",x=1,y=2,width=1,height=1,items={},value="\\-A"}, 68 | 69 | [5]={class="label",x=0,y=3,label=""}, 70 | [6]={class="label",x=0,y=4,label="在第二个音节设置内联特效"}, 71 | [7]={class="edit",name="在第二个音节设置内联特效",x=1,y=4,width=1,height=1,items={},value="\\-B"}, 72 | 73 | [8]={class="label",x=0,y=5,label=""}, 74 | [9]={class="label",x=0,y=6,label="小提示:按OK做处理后,可以ctrl-z撤销本处理,还有ctrl-y反撤销"}, 75 | 76 | } 77 | 78 | function add_fx(subs,sel) 79 | buttons,results =aegisub.dialog.display(dialog_config,{"OK","Cancel"}) 80 | if buttons=="OK" then 81 | local fx1 = results["在第一个音节设置内联特效"] 82 | local fx2 = results["在第二个音节设置内联特效"] 83 | for i = 1, #sel do 84 | aegisub.progress.set(i/#sel*100) 85 | local subp = subs[sel[i]] 86 | local t = string.match(subp.text, "^[^}]+") 87 | subp.text = string.gsub(subp.text, "[}]", fx2.."}",2) 88 | subp.text = string.gsub(subp.text, "^[^}]+", t..fx1) 89 | subs[sel[i]] = subp 90 | end 91 | end 92 | end 93 | 94 | TLL_macros = { 95 | { 96 | script_name = "中日分离2.0", 97 | script_description = "用于将中日穿插排版的歌词分离开来", 98 | entry = function(subs,sel) text_processing(subs,sel) end, 99 | validation = false 100 | }, 101 | { 102 | script_name = "全角空格 to 半角空格", 103 | script_description = "用于将歌词中的全角空格转换为半角空格", 104 | entry = function(subs,sel) full_to_half(subs,sel) end, 105 | validation = false 106 | }, 107 | { 108 | script_name = "删除所选行k标签", 109 | script_description = "用于删除所选行k标签", 110 | entry = function(subs,sel) delete_k(subs,sel) end, 111 | validation = false 112 | }, 113 | { 114 | script_name = "一键添加内联特效", 115 | script_description = "用于添加内敛特效", 116 | entry = function(subs,sel) add_fx(subs,sel) end, 117 | validation = false 118 | }, 119 | } 120 | 121 | for i = 1, #TLL_macros do 122 | aegisub.register_macro(script_name.." "..script_version.."/"..TLL_macros[i]["script_name"], TLL_macros[i]["script_description"], TLL_macros[i]["entry"], TLL_macros[i]["validation"]) 123 | end 124 | -------------------------------------------------------------------------------- /给字幕两侧添加图片.lua: -------------------------------------------------------------------------------- 1 | local tr = aegisub.gettext 2 | script_name = tr("给选中的字幕两侧添加图片v1.0") 3 | script_description = tr("给选中的字幕两侧添加图片v1.0") 4 | script_author = "拉姆0v0" 5 | script_version = "v1.0" 6 | 7 | function hexstring2number(hexstring, len) 8 | if not len or len > 8 then return end 9 | 10 | local hexbyte = {} 11 | for i = 1, len do 12 | hexbyte[i] = string.byte(hexstring, i) 13 | end 14 | 15 | local detection=0x10 16 | local num_string = {} 17 | for i = 1, len do 18 | if hexbyte[i]>=detection then 19 | num_string[i]=string.format("%x", hexbyte[i]) 20 | else 21 | num_string[i]="0"..string.format("%x", hexbyte[i]) 22 | end 23 | end 24 | 25 | 26 | local num = tonumber("0x"..num_string[1]..num_string[2]..num_string[3]..num_string[4]) 27 | return num 28 | end 29 | 30 | function get_png_size(path) 31 | local png_file = io.open(path, "rb") 32 | local data = png_file:read("*all") 33 | 34 | 35 | -- 保证png至少有37个字节,因为包含文件头等起码就超过这个数字了 36 | if #data < 37 then return end 37 | 38 | -- 文件头的相关信息请百度 39 | local png_header_info = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52} 40 | for i = 1, #png_header_info do 41 | if (string.byte(data, i) ~= png_header_info[i]) then 42 | return 43 | end 44 | end 45 | 46 | -- 这四个字节表示png的宽度 47 | data = string.sub(data, #png_header_info + 1) 48 | local width = hexstring2number(data, 4) 49 | 50 | data = string.sub(data, 5) 51 | local height = hexstring2number(data, 4) 52 | 53 | return width, height 54 | end 55 | 56 | dialog_config2= 57 | { 58 | 59 | [1]={class="label",x=0,y=0,label="请设置图片的出现位置和设定纵向偏移"}, 60 | [2]={class="label",x=0,y=2,label="设置纵向偏移-->>"}, 61 | [3]={class="intedit",name="图片纵向偏移",x=1,y=2,width=1,height=1,items={},value=0,hint="请输入整数"}, 62 | [4]={class="label",x=0,y=3,label="(可忽略,不可为空。正数为下移,负数为上移)"}, 63 | 64 | [5]={class="label",x=0,y=5,label="设置图片出现位置(至少选择一个)"}, 65 | [6]={class="label",x=0,y=6,label="字幕左侧-->>"}, 66 | [7]={class="checkbox",name="字幕左侧",x=1,y=6,width=1,height=1,items={},value=true}, 67 | [8]={class="label",x=0,y=7,label="字幕右侧-->>"}, 68 | [9]={class="checkbox",name="字幕右侧",x=1,y=7,width=1,height=1,items={},value=true}, 69 | 70 | } 71 | 72 | function pngmassage_test(subs,sel) 73 | filename = aegisub.dialog.open('请选择一张png图片', '', '','Text files (.png)|*.png', false, true) 74 | if filename==nil then 75 | return 76 | end 77 | buttons,results =aegisub.dialog.display(dialog_config2,{"OK","Cancel"}) 78 | if buttons=="OK" then 79 | local ymove = results["图片纵向偏移"] 80 | local lc = results["字幕左侧"] 81 | local rc = results["字幕右侧"] 82 | local x,y=get_png_size(filename) 83 | local video_size_x,video_size_y 84 | for i=1,#subs do 85 | if subs[i].key == "PlayResX" then 86 | video_size_x=subs[i].value 87 | end 88 | if subs[i].key == "PlayResY" then 89 | video_size_y=subs[i].value 90 | end 91 | end 92 | 93 | for i=1,#sel do 94 | local l=subs[sel[i]] 95 | local alignx 96 | local posy 97 | for i=1,#subs do 98 | if (subs[i].class == "style" and subs[i].name == l.style) then 99 | style_table = subs[i] 100 | end 101 | end 102 | width, height, descent, ext_lead = aegisub.text_extents(style_table, l.text) 103 | if style_table.align == 2 then 104 | posy=video_size_y-style_table.margin_t 105 | else 106 | posy=style_table.margin_t+height 107 | end 108 | local textl="{\\p1\\an2\\bord0\\shad0\\1img("..filename..")\\pos("..tostring(tonumber(video_size_x)/2-width/2-x/2-10)..","..tostring(posy+ymove)..")}m 0 0 l "..tostring(x).." 0 l "..tostring(x).." "..tostring(y).." l 0 "..tostring(y) 109 | local textr="{\\p1\\an2\\bord0\\shad0\\1img("..filename..")\\pos("..tostring(tonumber(video_size_x)/2+width/2+x/2+10)..","..tostring(posy+ymove)..")}m 0 0 l "..tostring(x).." 0 l "..tostring(x).." "..tostring(y).." l 0 "..tostring(y) 110 | if lc==true then 111 | l.text=textl 112 | subs.append(l) 113 | end 114 | if rc==true then 115 | l.text=textr 116 | subs.append(l) 117 | end 118 | if lc==false and rc==false then 119 | aegisub.debug.out("请至少勾选左右中的一侧添加图片!") 120 | return 121 | end 122 | end 123 | aegisub.debug.out("已完成!!!!!!\n\n文件路径:"..filename.."\n图片分辨率:"..tostring(x).."x"..tostring(y).."\n\n若显示为白色空白:\n1.请检查是否安装了最新版本的VSFilterMod\n2.请检查你所选择的图片名称是否存在非法字符,可以试着修改文件名为比较正常的文件名\n\n提示:若图片生成位置不理想,需要调整纵向偏移,可以ctrl-z撤销本次处理重新设置纵向偏移,还有ctrl-y反撤销\n\nby 拉姆0v0") 124 | end 125 | end 126 | 127 | 128 | aegisub.register_macro(script_name, script_description, pngmassage_test) 129 | -------------------------------------------------------------------------------- /获取up主视频数据v0.9.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/获取up主视频数据v0.9.zip -------------------------------------------------------------------------------- /轴校工具v5.3修复bug2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dumpling233/aegisub_Automation/9aa12b55fca74410584e59d176c488aba1464f81/轴校工具v5.3修复bug2.zip --------------------------------------------------------------------------------