diff --git a/lib/CharsetDetect.ahk b/lib/CharsetDetect.ahk new file mode 100644 index 0000000..5cb2469 --- /dev/null +++ b/lib/CharsetDetect.ahk @@ -0,0 +1,150 @@ +class TextEncodingDetect { + UTF8Bom := [0xEF, 0xBB, 0xBF] + UTF16LeBom := [0xFF, 0xFE] + UTF16BeBom := [0xFE, 0xFF] + UTF32LeBom := [0xFF, 0xFE, 0x00, 0x00] + + DetectEncoding(fileName) { + if !FileExist(fileName) { + MsgBox("File not found") + return "" + } + + buffer := FileRead(fileName, "RAW") + size := buffer.Size + + encodingType := this.DetectWithBom(buffer) + if (encodingType == "Utf8Bom") { + encodingType := "UTF-8" + } else if (encodingType == "UnicodeBom") { + encodingType := "UTF-16" + } + + if encodingType != "None" { + return encodingType + } + + encodingType := this.DetectWithoutBom(buffer, size) + if (encodingType == "GBK" || + encodingType == "Ansi") { + ; CP0 是 系统默认编码 + encodingType := "CP0" + } + + return encodingType != "None" ? encodingType : "None" + } + + DetectWithBom(buffer) { + if (buffer.Size >= 3 && this.ByteAt(buffer, 0) = this.UTF8Bom[1] && this.ByteAt(buffer, 1) = this.UTF8Bom[2] && this.ByteAt(buffer, 2) = this.UTF8Bom[3]) { + return "Utf8Bom" + } + + if (buffer.Size >= 2 && this.ByteAt(buffer, 0) = this.UTF16LeBom[1] && this.ByteAt(buffer, 1) = this.UTF16LeBom[2]) { + return "UnicodeBom" + } + + if (buffer.Size >= 2 && this.ByteAt(buffer, 0) = this.UTF16BeBom[1] && this.ByteAt(buffer, 1) = this.UTF16BeBom[2]) { + if (buffer.Size >= 4 && this.ByteAt(buffer, 2) = this.UTF32LeBom[3] && this.ByteAt(buffer, 3) = this.UTF32LeBom[4]) { + return "Utf32Bom" + } + return "BigEndianUnicodeBom" + } + + return "None" + } + + DetectWithoutBom(buffer, size) { + ; Check for UTF-8 encoding + encoding := this.CheckUtf8(buffer, size) + if encoding != "None" { + return encoding + } + + ; Check for ANSI encoding + if !this.ContainsZero(buffer, size) { + return this.CheckChinese(buffer, size) ? "GBK" : "Ansi" + } + + return "None" + } + + CheckUtf8(buffer, size) { + pos := 0 + while pos < size { + ch := this.ByteAt(buffer, pos) + pos++ + + if ch < 0x80 { + continue + } + + if ch >= 0xC2 && ch <= 0xDF { + if !this.IsContinuationByte(this.ByteAt(buffer, pos)) { + return "None" + } + pos++ + continue + } + + if ch >= 0xE0 && ch <= 0xEF { + if !this.IsContinuationByte(this.ByteAt(buffer, pos)) || !this.IsContinuationByte(this.ByteAt(buffer, pos + 1)) { + return "None" + } + pos += 2 + continue + } + + if ch >= 0xF0 && ch <= 0xF4 { + if !this.IsContinuationByte(this.ByteAt(buffer, pos)) || !this.IsContinuationByte(this.ByteAt(buffer, pos + 1)) || !this.IsContinuationByte(this.ByteAt(buffer, pos + 2)) { + return "None" + } + pos += 3 + continue + } + + return "None" + } + ; return "Utf8Nobom" + return "UTF-8" + } + + IsContinuationByte(byte) { + return byte >= 0x80 && byte <= 0xBF + } + + ContainsZero(buffer, size) { + pos := 0 + while pos < size { + if this.ByteAt(buffer, pos) = 0 { + return true + } + pos++ + } + return false + } + + CheckChinese(buffer, size) { + pos := 0 + while pos < size - 1 { + ch1 := this.ByteAt(buffer, pos) + ch2 := this.ByteAt(buffer, pos + 1) + pos += 2 + + if (ch1 >= 176 && ch1 <= 247 && ch2 >= 160 && ch2 <= 254) ; GB2312 + || (ch1 >= 129 && ch1 <= 254 && ch2 >= 64 && ch2 <= 254) ; GBK + || (ch1 >= 129 && ((ch2 >= 64 && ch2 <= 126) || (ch2 >= 161 && ch2 <= 254))) { ; Big5 + return true + } + } + return false + } + + ByteAt(buffer, index) { + return NumGet(buffer, index, "UChar") + } +} + +; 使用示例: +; detect := TextEncodingDetect() +; encoding := detect.DetectEncoding("C:\Users\Thunder\Downloads\Compressed\02 音名与钢琴键盘.srt") +; MsgBox "Detected Encoding: " encoding diff --git a/lib/MyTool.ahk b/lib/MyTool.ahk index 6244753..af83797 100644 --- a/lib/MyTool.ahk +++ b/lib/MyTool.ahk @@ -3,22 +3,22 @@ log_toggle := false ; 在路径中,获取程序的名称 -GetNameForPath(program_path){ - SplitPath program_path, &name +GetNameForPath(program_path) { + SplitPath program_path, &name, &dir, &ext, &name_no_ext, &drive return name } SearchProgram(target_app_path) { ; 程序正在运行 if (WinExist("ahk_exe " GetNameForPath(target_app_path))) { - return true + return true } else { - return false + return false } } -SelectedNoteProgram(note_app_names){ - Loop Parse note_app_names, "`n"{ +SelectedNoteProgram(note_app_names) { + Loop Parse note_app_names, "`n" { note_program := A_LoopField if (WinExist("ahk_exe " note_program)) { return note_program @@ -28,49 +28,57 @@ SelectedNoteProgram(note_app_names){ Exit } -ActivateProgram(process_name){ - if WinActive("ahk_exe " process_name){ - return +ActivateProgram(process_name) { + if WinActive("ahk_exe " process_name) { + return } if (WinExist("ahk_exe " process_name)) { - WinActivate ("ahk_exe " process_name) - Sleep 300 ; 给程序切换窗口的时间 + WinActivate ("ahk_exe " process_name) + Sleep 300 ; 给程序切换窗口的时间 } else { - MsgBox process_name " is not running" - Exit + MsgBox process_name " is not running" + Exit } } -IsPotplayerRunning(media_player_path){ +IsPotplayerRunning(media_player_path) { if SearchProgram(media_player_path) - return true + return true else - return false + return false } ; 获取Potplayer的标题 ; 获取失败的原因:因为Potplayer的exe可能存在多个线程,而WinGetTitle其中一个线程的标题,结果可能为空字符串 ; 参考:https://stackoverflow.com/questions/54570212/why-is-my-call-to-wingettitle-returning-an-empty-string -GetPotplayerTitle(potplayer_process_name){ +GetPotplayerTitle(potplayer_process_name) { ids := WinGetList("ahk_exe " potplayer_process_name) - for id in ids{ + + title := "" + for id in ids { + try { title := WinGetTitle("ahk_id " id) - if title == "" - continue - else - return title + } catch TargetError as err { + ; 目标窗口未找到 + continue + } + if title == "" + continue + else + return title } + Assert(title == "", "Error: Get Potplayer title failure!") } -MyLog(message){ +MyLog(message) { if log_toggle - MsgBox message + MsgBox message } ; 安全的递归 global running_count := 0 -SafeRecursion(){ +SafeRecursion() { global running_count running_count++ ToolTip("正在重试,第" running_count "次尝试...") @@ -83,17 +91,17 @@ SafeRecursion(){ } ; 等待释放指定按键 -ReleaseKeyboard(keyName){ - if GetKeyState(keyName){ - if KeyWait(keyName,"T2") == 0{ - SafeRecursion() - ReleaseKeyboard(keyName) - } +ReleaseKeyboard(keyName) { + if GetKeyState(keyName) { + if KeyWait(keyName, "T2") == 0 { + SafeRecursion() + ReleaseKeyboard(keyName) + } } running_count := 0 } -ReleaseCommonUseKeyboard(){ +ReleaseCommonUseKeyboard() { ReleaseKeyboard("Control") ReleaseKeyboard("Shift") ReleaseKeyboard("Alt") @@ -137,8 +145,8 @@ UrlDecode(Url, Enc := "UTF-8") { Return Url } -Assert(condition, exception_message){ - if (condition){ +Assert(condition, exception_message) { + if (condition) { MsgBox exception_message Exit } diff --git a/lib/PotplayerControl.ahk b/lib/PotplayerControl.ahk index 2ccd6b1..7dd7862 100644 --- a/lib/PotplayerControl.ahk +++ b/lib/PotplayerControl.ahk @@ -2,61 +2,60 @@ #Include MyTool.ahk class PotplayerControl { - __New(potplayer_process_name){ + __New(potplayer_process_name) { this._PotplayerProcessName := potplayer_process_name this.COMMAND_TYPE := 273 this.REQUEST_TYPE := 1024 this.hwnd := "" } - SendCommand(msg_type,wParam,lParam){ + SendCommand(msg_type, wParam, lParam) { hwnd := this.GetPotplayerHwnd() - return SendMessage(msg_type,wParam,lParam,hwnd) + return SendMessage(msg_type, wParam, lParam, hwnd) } - PostCommand(msg_type,wParam,lParam){ + PostCommand(msg_type, wParam, lParam) { hwnd := this.GetPotplayerHwnd() - return PostMessage(msg_type,wParam,lParam,hwnd) + return PostMessage(msg_type, wParam, lParam, hwnd) } - GetOncePotplayerHwnd(){ - if (this.hwnd != ""){ + GetOncePotplayerHwnd() { + if (this.hwnd != "") { return this.hwnd } return this.GetPotplayerHwnd() } - GetPotplayerHwnd(){ + GetPotplayerHwnd() { Assert(!WinExist("ahk_exe " this._PotplayerProcessName), "PotPlayer is not running") ids := WinGetList("ahk_exe " this._PotplayerProcessName) - for id in ids{ + for id in ids { title := WinGetTitle("ahk_id " id) - if (InStr(title, "PotPlayer")){ + if (InStr(title, "PotPlayer")) { this.hwnd := id return id } } } - ; 剪切板 - GetMediaPathToClipboard(){ - this.PostCommand(this.COMMAND_TYPE,10928,0) + GetMediaPathToClipboard() { + this.PostCommand(this.COMMAND_TYPE, 10928, 0) } - GetMediaTimestampToClipboard(){ - this.PostCommand(this.COMMAND_TYPE,10924,0) + GetMediaTimestampToClipboard() { + this.PostCommand(this.COMMAND_TYPE, 10924, 0) } - GetSubtitleToClipboard(){ - this.PostCommand(this.COMMAND_TYPE,10624,0) + GetSubtitleToClipboard() { + this.PostCommand(this.COMMAND_TYPE, 10624, 0) } - SaveImageToClipboard(){ - this.SendCommand(this.COMMAND_TYPE,10223,0) + SaveImageToClipboard() { + this.SendCommand(this.COMMAND_TYPE, 10223, 0) } ; 状态 - GetPlayStatus(){ - status := this.SendCommand(this.REQUEST_TYPE,20486,0) + GetPlayStatus() { + status := this.SendCommand(this.REQUEST_TYPE, 20486, 0) switch status { case -1: return "Stopped" @@ -67,104 +66,104 @@ class PotplayerControl { } return "Undefined" } - PlayOrPause(){ - this.PostCommand(this.COMMAND_TYPE,20001,0) + PlayOrPause() { + this.PostCommand(this.COMMAND_TYPE, 20001, 0) } - Play(){ + Play() { status := this.GetPlayStatus() - if (status != "Running"){ - this.PostCommand(this.COMMAND_TYPE,20000,0) + if (status != "Running") { + this.PostCommand(this.COMMAND_TYPE, 20000, 0) } } - PlayPause(){ + PlayPause() { status := this.GetPlayStatus() - if (status == "Running"){ - this.PostCommand(this.COMMAND_TYPE,20000,0) + if (status == "Running") { + this.PostCommand(this.COMMAND_TYPE, 20000, 0) } } - Stop(){ - this.PostCommand(this.COMMAND_TYPE,20002,0) + Stop() { + this.PostCommand(this.COMMAND_TYPE, 20002, 0) } ; 速度 - PreviousFrame(){ - this.PostCommand(this.COMMAND_TYPE,10242,0) + PreviousFrame() { + this.PostCommand(this.COMMAND_TYPE, 10242, 0) } - NextFrame(){ - this.PostCommand(this.COMMAND_TYPE,10241,0) + NextFrame() { + this.PostCommand(this.COMMAND_TYPE, 10241, 0) } - Forward(){ - this.PostCommand(this.COMMAND_TYPE,10060,0) + Forward() { + this.PostCommand(this.COMMAND_TYPE, 10060, 0) } - Backward(){ - this.PostCommand(this.COMMAND_TYPE,10059,0) + Backward() { + this.PostCommand(this.COMMAND_TYPE, 10059, 0) } - SpeedUp(){ - this.PostCommand(this.COMMAND_TYPE,10248,0) + SpeedUp() { + this.PostCommand(this.COMMAND_TYPE, 10248, 0) } - SpeedDown(){ - this.PostCommand(this.COMMAND_TYPE,10247,0) + SpeedDown() { + this.PostCommand(this.COMMAND_TYPE, 10247, 0) } - SpeedNormal(){ - this.PostCommand(this.COMMAND_TYPE,10246,0) + SpeedNormal() { + this.PostCommand(this.COMMAND_TYPE, 10246, 0) } ; 时间 - GetMediaTimeMilliseconds(){ - return this.SendCommand(this.REQUEST_TYPE,20484,0) + GetMediaTimeMilliseconds() { + return this.SendCommand(this.REQUEST_TYPE, 20484, 0) } ; 受【选项-播放-时间跨度-如果存在关键帧数据则以关键帧为移动单位】的potplayer后处理影响不够精准,关掉此选项则非常精准 - SetMediaTimeMilliseconds(ms){ - this.PostCommand(this.REQUEST_TYPE,20485, ms) + SetMediaTimeMilliseconds(ms) { + this.PostCommand(this.REQUEST_TYPE, 20485, ms) } - GetCurrentSecondsTime(){ - return Integer(this.GetMediaTimeMilliseconds()/1000) + GetCurrentSecondsTime() { + return Integer(this.GetMediaTimeMilliseconds() / 1000) } - GetTotalTimeSeconds(){ - return Integer(this.SendCommand(this.REQUEST_TYPE,20482,0)/1000) + GetTotalTimeSeconds() { + return Integer(this.SendCommand(this.REQUEST_TYPE, 20482, 0) / 1000) } - SetCurrentSecondsTime(seconds){ - if (seconds < 0){ + SetCurrentSecondsTime(seconds) { + if (seconds < 0) { seconds := 0 } - this.SetMediaTimeMilliseconds(seconds*1000) + this.SetMediaTimeMilliseconds(seconds * 1000) } - ForwardBySeconds(forward_seconds, potplayer_control){ - try{ - if(forward_seconds != 0){ + ForwardBySeconds(forward_seconds, potplayer_control) { + try { + if (forward_seconds != 0) { potplayer_control.SetMediaTimeMilliseconds(Integer(this.GetMediaTimeMilliseconds() + (forward_seconds * 1000))) - }else{ + } else { potplayer_control.Forward() } - }catch Error as err{ + } catch Error as err { MsgBox "Forward Seconds and Backward Seconds It can't be empty" } } - BackwardBySeconds(backward_seconds, potplayer_control){ - try{ - if(backward_seconds != 0){ + BackwardBySeconds(backward_seconds, potplayer_control) { + try { + if (backward_seconds != 0) { potplayer_control.SetMediaTimeMilliseconds(Integer(this.GetMediaTimeMilliseconds() - (backward_seconds * 1000))) - }else{ + } else { potplayer_control.Backward() } - }catch Error as err{ + } catch Error as err { MsgBox "Forward Seconds and Backward Seconds It can't be empty" } } ; A-B 循环 - SetStartPointOfTheABCycle(){ - this.PostCommand(this.COMMAND_TYPE,10249,0) + SetStartPointOfTheABCycle() { + this.PostCommand(this.COMMAND_TYPE, 10249, 0) } - SetEndPointOfTheABCycle(){ - this.PostCommand(this.COMMAND_TYPE,10250,0) + SetEndPointOfTheABCycle() { + this.PostCommand(this.COMMAND_TYPE, 10250, 0) } - CancelTheABCycle(){ + CancelTheABCycle() { ; 解除区段循环:起点 - this.PostCommand(this.COMMAND_TYPE,10251,0) + this.PostCommand(this.COMMAND_TYPE, 10251, 0) ; 解除区段循环:终点 - this.PostCommand(this.COMMAND_TYPE,10252,0) + this.PostCommand(this.COMMAND_TYPE, 10252, 0) } } @@ -173,4 +172,4 @@ class PotplayerControl { ; potplayer.SpeedUp() ; potplayer.SpeedDown() -; potplayer.SpeedNormal() \ No newline at end of file +; potplayer.SpeedNormal() diff --git a/lib/Redner.ahk b/lib/Redner.ahk new file mode 100644 index 0000000..53a5873 --- /dev/null +++ b/lib/Redner.ahk @@ -0,0 +1,149 @@ +#Requires AutoHotkey v2.0 + +RenderTemplate(backlink_template, media_data) { + backlink_template := RenderSrtTemplate(backlink_template, media_data, "") + + markdown_link_data := RenderNameAndTimeAndLink(app_config, media_data) + + if (InStr(backlink_template, "{title}") != 0) { + rendered_link := RenderTitle(app_config, markdown_link_data) + backlink_template := StrReplace(backlink_template, "{title}", rendered_link) + } + + if (InStr(backlink_template, "{link}") != 0) { + backlink_template := StrReplace(backlink_template, "{link}", markdown_link_data.link) + } + + if (InStr(backlink_template, "{name}") != 0) { + backlink_template := StrReplace(backlink_template, "{name}", media_data.Path) + } + + if (InStr(backlink_template, "{time}") != 0) { + backlink_template := StrReplace(backlink_template, "{time}", media_data.Time) + } + + if (InStr(backlink_template, "{subtitle}") != 0) { + backlink_template := RenderSubtitleTemplate(backlink_template, media_data) + } + + return backlink_template +} + +RenderSubtitleTemplate(target_string, media_data) { + if (InStr(target_string, "{subtitle}") != 0) { + if (media_data.Subtitle == "") { + media_data.Subtitle := GetMediaSubtitle() + } + target_string := StrReplace(target_string, "{subtitle}", media_data.Subtitle) + } + return target_string +} + +RenderTitle(app_config, markdown_link_data) { + result_link := "" + ; 生成word链接 + if (IsWordProgram()) { + result_link := "" markdown_link_data.title "" + + } else { + ; 生成MarkDown链接 + result_link := GenerateMarkdownLink(markdown_link_data.title, markdown_link_data.link) + } + return result_link +} + +RenderSrtTemplate(backlink_template, media_data, subtitle_data) { + if (InStr(backlink_template, '{subtitleTimeRange}') != 0 || + InStr(backlink_template, '{subtitleTimeStart}') != 0 || + InStr(backlink_template, '{subtitleTimeEnd}') != 0 || + InStr(backlink_template, '{subtitleOrigin}') != 0) { + + if (!subtitle_data) { + SplitPath media_data.Path, &name, &dir, &ext, &name_no_ext, &drive + subtitles_data := SubtitlesFromSrt(dir "/" name_no_ext ".srt") + subtitle_data := FindSubtitleByTimestamp(GetMediaTimeMilliseconds() + 1, subtitles_data) + } + + if (subtitle_data) { + if (InStr(backlink_template, '{subtitleTimeRange}') != 0) { + ; 1. 修改渲染回链的 link 中的 time + media_data.Time := MilliSecondToTimestamp(subtitle_data.timeStart + 1) "-" MilliSecondToTimestamp(subtitle_data.timeEnd - 1) + ; 2. 修改渲染回链的 title 中的 time + backlink_template := StrReplace(backlink_template, "{subtitleTimeRange}", RemoveMillisecondFormTimestamp(media_data.Time)) + } else if (InStr(backlink_template, '{subtitleTimeStart}') != 0) { + media_data.Time := MilliSecondToTimestamp(subtitle_data.timeStart + 1) + backlink_template := StrReplace(backlink_template, "{subtitleTimeStart}", RemoveMillisecondFormTimestamp(media_data.Time)) + } else if (InStr(backlink_template, '{subtitleTimeEnd}') != 0) { + media_data.Time := MilliSecondToTimestamp(subtitle_data.timeEnd - 1) + backlink_template := StrReplace(backlink_template, "{subtitleTimeEnd}", RemoveMillisecondFormTimestamp(media_data.Time)) + } + + if (InStr(backlink_template, '{subtitleOrigin}') != 0) { + backlink_template := StrReplace(backlink_template, "{subtitleOrigin}", subtitle_data.subtitle) + } + + return backlink_template + } + } + return backlink_template +} + +; // [用户想要的标题格式](mk-potplayer://open?path=1&aaa=123&time=456) +RenderNameAndTimeAndLink(app_config, media_data) { + ; B站的视频 + if (InStr(media_data.Path, "https://www.bilibili.com/video/")) { + ; 正常播放的情况 + name := StrReplace(GetPotplayerTitle(app_config.PotplayerProcessName), " - PotPlayer", "") + + ; 视频没有播放,已经停止的情况,不是暂停是停止 + if name == "PotPlayer" { + name := GetFileNameInPath(media_data.Path) + } + } else { + ; 本地视频 + name := GetFileNameInPath(media_data.Path) + } + ; 渲染 title + title := app_config.MarkdownTitle + title := StrReplace(title, "{name}", name) + title := StrReplace(title, "{time}", media_data.Time) + ; 渲染 title 中的 字幕模板 + title := RenderSubtitleTemplate(title, media_data) + + markdown2potplayer_link := GenerateMarkdownLink2PotplayerLink(app_config, media_data) + + result := {} + result.title := title + result.link := markdown2potplayer_link + return result +} + +GenerateMarkdownLink(markdown_title, markdown_link) { + if (IsNotionProgram()) { + result := "[" markdown_title "](http://127.0.0.1:33660/" markdown_link ")" + } else { + result := "[" markdown_title "](" markdown_link ")" + } + return result +} + +GenerateMarkdownLink2PotplayerLink(app_config, media_data) { + return app_config.UrlProtocol "?path=" ProcessUrl(media_data.Path) "&time=" media_data.Time +} + +RenderImage(markdown_image_template, media_data, image) { + identifier := "{image}" + image_templates := TemplateConvertedToTemplates(markdown_image_template, identifier) + For index, image_template in image_templates { + if (image_template == identifier) { + SendImage2NoteApp(image) + } else { + rendered_template := RenderTemplate(image_template, media_data) + if (IsWordProgram() && InStr(image_template, "{title}")) { + SendText2wordApp(rendered_template) + } else { + SendText2NoteApp(rendered_template) + } + } + } +} \ No newline at end of file diff --git a/lib/ReduceTime.ahk b/lib/ReduceTime.ahk index 9c57395..63b054b 100644 --- a/lib/ReduceTime.ahk +++ b/lib/ReduceTime.ahk @@ -2,51 +2,55 @@ ; 修改秒数并返回新的时间格式 ReduceTime(originalTime, secondsToModify) { - newSeconds := TimestampToMilliSecond(originalTime) - (secondsToModify * 1000) + newSeconds := TimestampToMilliSecond(originalTime) - (secondsToModify * 1000) - if (newSeconds < 0){ - newSeconds := 0 - } + if (newSeconds < 0) { + newSeconds := 0 + } - return MilliSecondToTimestamp(newSeconds) + return MilliSecondToTimestamp(newSeconds) } GetMilliseconds(originalTime) { - ms := "" - if (InStr(originalTime,".")){ - ms := SubStr(originalTime, InStr(originalTime,".")) - } - return ms + ms := "" + if (InStr(originalTime, ".")) { + ms := SubStr(originalTime, InStr(originalTime, ".")) + } + return ms } ; 将时间字符串转换为毫秒 TimestampToMilliSecond(timeStr) { - if (timeStr = "" || - timeStr = "0") { - return 0 - } - parts := StrSplit(timeStr , ":") - milliseconds := 0 - - if(InStr(timeStr, ".") > 0) { - seconds_millis := StrSplit(parts[-1], ".") - parts[-1] := seconds_millis.Get(1) - milliseconds := Integer(seconds_millis.Get(2)) - } else { - milliseconds := 0 - } - - Loop parts.Length - parts[A_Index] := Integer(parts[A_Index]) - - if(parts.Length = 1) - milliseconds += Integer(parts.Get(1)) * 1000 - else if(parts.Length = 2) - milliseconds += (Integer(parts.Get(1)) * 60 + Integer(parts.Get(2))) * 1000 - else if(parts.Length = 3) - milliseconds += (Integer(parts.Get(1)) * 3600 + Integer(parts.Get(2)) * 60 + Integer(parts.Get(3))) * 1000 - - return milliseconds + if (timeStr = "" || + timeStr = "0") { + return 0 + } + parts := StrSplit(timeStr, ":") + milliseconds := 0 + + if (InStr(timeStr, ".") > 0) { + seconds_millis := StrSplit(parts[-1], ".") + parts[-1] := seconds_millis.Get(1) + milliseconds := Integer(seconds_millis.Get(2)) + } else { + milliseconds := 0 + } + + Loop parts.Length + parts[A_Index] := Integer(parts[A_Index]) + + if (parts.Length = 1) + milliseconds += Integer(parts.Get(1)) * 1000 + else if (parts.Length = 2) + milliseconds += (Integer(parts.Get(1)) * 60 + Integer(parts.Get(2))) * 1000 + else if (parts.Length = 3) + milliseconds += (Integer(parts.Get(1)) * 3600 + Integer(parts.Get(2)) * 60 + Integer(parts.Get(3))) * 1000 + + return milliseconds +} + +RemoveMillisecondFormTimestamp(timestamp) { + return RegExReplace(timestamp, "\.\d{3}") } ; 测试不同的时间戳格式 ; timestamps := ['0.123', '1', '1:01', '1:01:01', '1:01:01.123'] @@ -54,46 +58,45 @@ TimestampToMilliSecond(timeStr) { ; MsgBox TimeToMilliSecond(timestamps[A_Index]) ; 将毫秒转换回原始格式 -MilliSecondToTimestamp(milliseconds){ - if (milliseconds = "" || - milliseconds = 0) { - return "0" - } - - ; 1. 确定毫秒中包含多少完整的小时,然后从总毫秒数中减去这些小时对应的毫秒数 - ; 2. 计算剩余毫秒数中包含多少完整的分钟,并同样从剩余毫秒数中减去 - ; 3. 计算剩余毫秒数中包含多少完整的秒,并从剩余毫秒数中减去 - ; 4. 剩余的就是不足一秒的毫秒数 - hours := milliseconds // 3600000 - remainder := Mod(milliseconds, 3600000) - - minutes := remainder // 60000 - remainder := Mod(remainder, 60000) - - seconds := remainder // 1000 - millis := Mod(remainder, 1000) - - ; MsgBox "Hours:" hours '`n' "minutes:" minutes '`n' "seconds:" seconds '`n' "millis:" millis - - result := "" - if hours > 0 - result .= Format("{:02}", hours) ":" - - ; milliseconds >= 3600000 表示大于等于1小时,则显示分钟,无论分钟是否为0 - if minutes > 0 || milliseconds >= 3600000 - result .= Format("{:02}", minutes) ":" - - if seconds > 0 || milliseconds >= 60000 - result .= Format("{:02}", seconds) - - if millis > 0 && milliseconds >= 1000 - result .= "." Format("{:03}", millis) - ; 如果毫秒小于1000,则显示0.x毫秒 - else if milliseconds < 1000 - result .= "0." Format("{:03}", milliseconds) - return result +MilliSecondToTimestamp(milliseconds) { + if (milliseconds == "" || + milliseconds == 0) { + return "00:00" + } + + ; 1. 确定毫秒中包含多少完整的小时,然后从总毫秒数中减去这些小时对应的毫秒数 + ; 2. 计算剩余毫秒数中包含多少完整的分钟,并同样从剩余毫秒数中减去 + ; 3. 计算剩余毫秒数中包含多少完整的秒,并从剩余毫秒数中减去 + ; 4. 剩余的就是不足一秒的毫秒数 + hours := milliseconds // 3600000 + remainder := Mod(milliseconds, 3600000) + + minutes := remainder // 60000 + remainder := Mod(remainder, 60000) + + seconds := remainder // 1000 + millis := Mod(remainder, 1000) + + ; MsgBox "Hours:" hours '`n' "minutes:" minutes '`n' "seconds:" seconds '`n' "millis:" millis + + result := "" + if hours > 0 + result .= Format("{:02}", hours) ":" + + if milliseconds > 0 + result .= Format("{:02}", minutes) ":" + + if milliseconds > 0 + result .= Format("{:02}", seconds) + + if millis > 0 + result .= "." Format("{:03}", millis) + ; 如果毫秒小于1000,则显示0.x毫秒 + else if milliseconds < 1000 + result .= "00:00." Format("{:03}", milliseconds) + return result } ; 测试不同的毫秒值 ; milliseconds_values := [123, 1000, 60000, 61000, 3600000,3661000, 3661123] ; Loop milliseconds_values.Length -; MsgBox MilliSecondToTimestamp(milliseconds_values[A_Index]) \ No newline at end of file +; MsgBox MilliSecondToTimestamp(milliseconds_values[A_Index]) diff --git a/lib/TemplateParser.ahk b/lib/TemplateParser.ahk index 2634bed..3944b1e 100644 --- a/lib/TemplateParser.ahk +++ b/lib/TemplateParser.ahk @@ -13,13 +13,18 @@ TemplateConvertedToTemplates(template, delimiter) { while (true) { delimiterIndex := InStr(template, delimiter, false, searchIndex) - ; 情况2: 搜索结束 - if (delimiterIndex = 0) + ; 情况1: 搜索结束 + if (delimiterIndex = 0) { + if (searchIndex > 0 && searchIndex < StrLen(template)) { + result.Push(SubStr(template, searchIndex)) + } break - ; 情况1: 搜索到 {image}本身 + } + ; 情况2: 搜索到 identifier自身 if (delimiterIndex == searchIndex) { result.Push(delimiter) } else { + ; 情况3:正常分割 result.Push(SubStr(template, searchIndex, delimiterIndex - searchIndex)) result.Push(delimiter) } @@ -31,10 +36,10 @@ TemplateConvertedToTemplates(template, delimiter) { return result } +; test ; testString := "{image}123123{image}{image}456456{image}" ; delimiterString := "{image}" ; result := TemplateConvertedToTemplates(testString, delimiterString) -; ; 打印结果 ; for index, item in result ; MsgBox("Item " . index . ": " . item) diff --git a/lib/entity/Config.ahk b/lib/entity/Config.ahk index 5335f37..d2345b8 100644 --- a/lib/entity/Config.ahk +++ b/lib/entity/Config.ahk @@ -2,131 +2,135 @@ #Include ../sqlite/SqliteControl.ahk #Include ../MyTool.ahk -Class Config{ +Class Config { ; 其他微调设置 - PotplayerPath{ + PotplayerPath { get => GetKey("path") - set => UpdateOrIntert("path",Value) + set => UpdateOrIntert("path", Value) } - PotplayerProcessName{ + PotplayerProcessName { get => GetNameForPath(this.PotplayerPath) } - IsStop{ + IsStop { get => GetKey("is_stop") - set => UpdateOrIntert("is_stop",Value) + set => UpdateOrIntert("is_stop", Value) } - ReduceTime{ + ReduceTime { get => GetKey("reduce_time") - set => UpdateOrIntert("reduce_time",Value) + set => UpdateOrIntert("reduce_time", Value) } - NoteAppName{ + NoteAppName { get => GetKey("app_name") - set => UpdateOrIntert("app_name",Value) + set => UpdateOrIntert("app_name", Value) } - MarkdownPathIsEncode{ + MarkdownPathIsEncode { get => GetKey("path_is_encode") - set => UpdateOrIntert("path_is_encode",Value) + set => UpdateOrIntert("path_is_encode", Value) } - MarkdownRemoveSuffixOfVideoFile{ + MarkdownRemoveSuffixOfVideoFile { get => GetKey("remove_suffix_of_video_file") - set => UpdateOrIntert("remove_suffix_of_video_file",Value) + set => UpdateOrIntert("remove_suffix_of_video_file", Value) } - UrlProtocol{ + UrlProtocol { get => GetKey("url_protocol") - set => UpdateOrIntert("url_protocol",Value) + set => UpdateOrIntert("url_protocol", Value) } - SendImageDelays{ + SendImageDelays { get => GetKey("send_image_delays") - set => UpdateOrIntert("send_image_delays",Value) + set => UpdateOrIntert("send_image_delays", Value) } - + ; ============回链的设置================ - SubtitleTemplate{ + SubtitleTemplate { get => GetKey("subtitle_template") - set => UpdateOrIntert("subtitle_template",Value) + set => UpdateOrIntert("subtitle_template", Value) } ; backlink_template - MarkdownTemplate{ + MarkdownTemplate { get => GetKey("template") - set => UpdateOrIntert("template",Value) + set => UpdateOrIntert("template", Value) } - MarkdownImageTemplate{ + MarkdownImageTemplate { get => GetKey("image_template") - set => UpdateOrIntert("image_template",Value) + set => UpdateOrIntert("image_template", Value) } - MarkdownTitle{ + MarkdownTitle { get => GetKey("title") - set => UpdateOrIntert("title",Value) + set => UpdateOrIntert("title", Value) } ; ============回链快捷键相关================ - HotkeyBacklink{ + HotkeySubtitle { + get => GetKey("hotkey_subtitle") + set => UpdateOrIntert("hotkey_subtitle", Value) + } + HotkeyBacklink { get => GetKey("hotkey_backlink") - set => UpdateOrIntert("hotkey_backlink",Value) + set => UpdateOrIntert("hotkey_backlink", Value) } - HotkeyIamgeBacklink{ + HotkeyIamgeBacklink { get => GetKey("hotkey_iamge_backlink") - set => UpdateOrIntert("hotkey_iamge_backlink",Value) + set => UpdateOrIntert("hotkey_iamge_backlink", Value) } - HotkeyAbFragment{ + HotkeyAbFragment { get => GetKey("hotkey_ab_fragment") - set => UpdateOrIntert("hotkey_ab_fragment",Value) + set => UpdateOrIntert("hotkey_ab_fragment", Value) } - AbFragmentDetectionDelays{ + AbFragmentDetectionDelays { get => GetKey("ab_fragment_detection_delays") - set => UpdateOrIntert("ab_fragment_detection_delays",Value) + set => UpdateOrIntert("ab_fragment_detection_delays", Value) } - LoopAbFragment{ + LoopAbFragment { get => GetKey("loop_ab_fragment") - set => UpdateOrIntert("loop_ab_fragment",Value) + set => UpdateOrIntert("loop_ab_fragment", Value) } - HotkeyAbCirculation{ + HotkeyAbCirculation { get => GetKey("hotkey_ab_circulation") - set => UpdateOrIntert("hotkey_ab_circulation",Value) + set => UpdateOrIntert("hotkey_ab_circulation", Value) } ; ============映射Potplayer快捷键相关================ - HotkeyPreviousFrame{ + HotkeyPreviousFrame { get => GetKey("hotkey_previous_frame") - set => UpdateOrIntert("hotkey_previous_frame",Value) + set => UpdateOrIntert("hotkey_previous_frame", Value) } - HotkeyNextFrame{ + HotkeyNextFrame { get => GetKey("hotkey_next_frame") - set => UpdateOrIntert("hotkey_next_frame",Value) + set => UpdateOrIntert("hotkey_next_frame", Value) } - HotkeyForward{ + HotkeyForward { get => GetKey("hotkey_forward") - set => UpdateOrIntert("hotkey_forward",Value) + set => UpdateOrIntert("hotkey_forward", Value) } - ForwardSeconds{ + ForwardSeconds { get => GetKey("forward_seconds") - set => UpdateOrIntert("forward_seconds",Value) + set => UpdateOrIntert("forward_seconds", Value) } - HotkeyBackward{ + HotkeyBackward { get => GetKey("hotkey_backward") - set => UpdateOrIntert("hotkey_backward",Value) + set => UpdateOrIntert("hotkey_backward", Value) } - BackwardSeconds{ + BackwardSeconds { get => GetKey("backward_seconds") - set => UpdateOrIntert("backward_seconds",Value) + set => UpdateOrIntert("backward_seconds", Value) } - HotkeyPlayOrPause{ + HotkeyPlayOrPause { get => GetKey("hotkey_play_or_pause") - set => UpdateOrIntert("hotkey_play_or_pause",Value) + set => UpdateOrIntert("hotkey_play_or_pause", Value) } - HotkeyStop{ + HotkeyStop { get => GetKey("hotkey_stop") - set => UpdateOrIntert("hotkey_stop",Value) + set => UpdateOrIntert("hotkey_stop", Value) } } \ No newline at end of file diff --git a/lib/gui/Gui.ahk b/lib/gui/Gui.ahk index a939478..d09b75d 100644 --- a/lib/gui/Gui.ahk +++ b/lib/gui/Gui.ahk @@ -53,52 +53,56 @@ Constructor(i18n_local) { Edit_note_app_name := myGui.Add("Edit", "x160 y48 w162 h63 +Multi", "Obsidian.exe`nTypora.exe") myGui.Add("Text", "x160 y121 w123 h23", i18n_local.Gui_note_names_tips) - myGui.Add("Text", "x40 y140 w63 h23", i18n_local.Gui_subtitle_template_name) - Edit_subtitle_template := myGui.Add("Edit", "x160 y140 w162 h30 +Multi", "字幕:`n{subtitle}") + myGui.Add("Text", "x40 y136 w63 h23", i18n_local.Gui_subtitle_template_name) + Edit_subtitle_template := myGui.Add("Edit", "x160 y136 w162 h30 +Multi", "字幕:`n{subtitle}") + Button_srt_to_backlink_mdfile := myGui.Add("Button", "x384 y136 w103 h23", "srt转回链md") - myGui.Add("Text", "x40 y190 w63 h23", i18n_local.Gui_backlink_name) - Edit_title := myGui.Add("Edit", "x160 y190 w148 h23", "{name} | {time}") + myGui.Add("Text", "x40 y185 w63 h23", i18n_local.Gui_backlink_name) + Edit_title := myGui.Add("Edit", "x160 y185 w148 h23", "{name} | {time}") - myGui.Add("Text", "x40 y240 w51 h23", i18n_local.Gui_backlink_template) - Edit_markdown_template := myGui.Add("Edit", "x160 y240 w149 h48 +Multi", "`n视频:{title}`n") + myGui.Add("Text", "x40 y223 w51 h23", i18n_local.Gui_backlink_template) + Edit_markdown_template := myGui.Add("Edit", "x160 y223 w149 h48 +Multi", "`n视频:{title}`n") - myGui.Add("Text", "x40 y295 w77 h23", i18n_local.Gui_image_backlink_tempalte_name) - Edit_image_template := myGui.Add("Edit", "x160 y295 w151 h66 +Multi", "`n图片:{image}`n视频:{title}`n") + myGui.Add("Text", "x40 y284 w77 h23", i18n_local.Gui_image_backlink_tempalte_name) + Edit_image_template := myGui.Add("Edit", "x160 y284 w151 h66 +Multi", "`n图片:{image}`n视频:{title}`n") - myGui.Add("Text", "x40 y365 w111", i18n_local.Gui_send_image_delays) - Edit_send_image_delays := myGui.Add("Edit", "x160 y365 w120 h21") - myGui.Add("Text", "x288 y365 w22 h23", "ms") + myGui.Add("Text", "x40 y355 w111", i18n_local.Gui_send_image_delays) + Edit_send_image_delays := myGui.Add("Edit", "x160 y355 w120 h21") + myGui.Add("Text", "x288 y355 w22 h23", "ms") ; =======快捷键========= - myGui.Add("Text", "x40 y399 w105 h23", i18n_local.Gui_hotkey_backlink) - hk_backlink := myGui.Add("Hotkey", "x160 y399 w156 h21", "!g") + myGui.Add("Text", "x40 y387 w105 h23", i18n_local.Gui_hotkey_title) + hk_subtitle := myGui.Add("Hotkey", "x160 y387 w156 h21", "!t") - myGui.Add("Text", "x40 y431 w105 h32", i18n_local.Gui_hotkey_image_and_backlink) - hk_image_backlink := myGui.Add("Hotkey", "x160 y431 w156 h21", "^!g") + myGui.Add("Text", "x40 y416 w105 h23", i18n_local.Gui_hotkey_backlink) + hk_backlink := myGui.Add("Hotkey", "x160 y416 w156 h21", "!g") - myGui.Add("Text", "x40 y455 w114 h23", i18n_local.Gui_hotkey_ab_fragment) - hk_ab_fragment := myGui.Add("Hotkey", "x160 y455 w156 h21", "F1") + myGui.Add("Text", "x40 y448 w105 h32", i18n_local.Gui_hotkey_image_and_backlink) + hk_image_backlink := myGui.Add("Hotkey", "x160 y448 w156 h21", "^!g") - myGui.Add("Text", "x40 y484 w98 h23", i18n_local.Gui_hotkey_ab_fragment_detection_delays) - Edit_ab_fragment_detection_delays := myGui.Add("Edit", "x160 y484 w120 h21", "1000") - myGui.Add("Text", "x288 y484 w31 h23", "ms") + myGui.Add("Text", "x40 y472 w114 h23", i18n_local.Gui_hotkey_ab_fragment) + hk_ab_fragment := myGui.Add("Hotkey", "x160 y472 w156 h21", "F1") - CheckBox_loop_ab_fragment := myGui.Add("CheckBox", "x160 y505 w120 h23", i18n_local.Gui_is_loop_ab_fragment) + myGui.Add("Text", "x40 y501 w98 h23", i18n_local.Gui_hotkey_ab_fragment_detection_delays) + Edit_ab_fragment_detection_delays := myGui.Add("Edit", "x160 y501 w120 h21", "1000") + myGui.Add("Text", "x288 y501 w31 h23", "ms") - myGui.Add("Text", "x40 y530 w105 h12", i18n_local.Gui_hotkey_ab_circulation) - hk_ab_circulation := myGui.Add("Hotkey", "x160 y530 w156 h21") + CheckBox_loop_ab_fragment := myGui.Add("CheckBox", "x160 y518 w120 h23", i18n_local.Gui_is_loop_ab_fragment) + + myGui.Add("Text", "x40 y547 w105 h12", i18n_local.Gui_hotkey_ab_circulation) + hk_ab_circulation := myGui.Add("Hotkey", "x160 y547 w156 h21") ; =======其他设置========= - myGui.Add("Text", "x40 y565 w105 h36", i18n_local.Gui_edit_url_protocol) - Edit_url_protocol := myGui.Add("Edit", "x160 y565 w156 h21", "jv://open") + myGui.Add("Text", "x40 y582 w105 h36", i18n_local.Gui_edit_url_protocol) + Edit_url_protocol := myGui.Add("Edit", "x160 y582 w156 h21", "jv://open") - myGui.Add("Text", "x40 y601 w60 h23", i18n_local.Gui_reduce_time) - Edit_reduce_time := myGui.Add("Edit", "x160 y601 w120 h21", "0") + myGui.Add("Text", "x40 y618 w60 h23", i18n_local.Gui_reduce_time) + Edit_reduce_time := myGui.Add("Edit", "x160 y618 w120 h21", "0") - CheckBox_is_stop := myGui.Add("CheckBox", "x160 y625 w69 h23", i18n_local.Gui_is_stop) - CheckBox_remove_suffix_of_video_file := myGui.Add("CheckBox", "x160 y649 w150 h23", i18n_local.Gui_remove_suffix_of_video_file) - CheckBox_path_is_encode := myGui.Add("CheckBox", "x160 y673 w120 h23", i18n_local.Gui_is_path_encode) - CheckBox_bootup := myGui.Add("CheckBox", "x160 y697 w120 h23", i18n_local.Gui_bootup) + CheckBox_is_stop := myGui.Add("CheckBox", "x160 y642 w69 h23", i18n_local.Gui_is_stop) + CheckBox_remove_suffix_of_video_file := myGui.Add("CheckBox", "x160 y666 w150 h23", i18n_local.Gui_remove_suffix_of_video_file) + CheckBox_path_is_encode := myGui.Add("CheckBox", "x160 y690 w120 h23", i18n_local.Gui_is_path_encode) + CheckBox_bootup := myGui.Add("CheckBox", "x160 y714 w120 h23", i18n_local.Gui_bootup) Tab.UseTab(2) myGui.Add("Text", "x86 y24 w42 h23", i18n_local.Gui_hotkey_previous_frame) @@ -126,6 +130,7 @@ Constructor(i18n_local) { guiData.controls.Button_potplayer := Button_potplayer guiData.controls.Edit_note_app_name := Edit_note_app_name guiData.controls.Edit_subtitle_template := Edit_subtitle_template + guiData.controls.Button_srt_to_backlink_mdfile := Button_srt_to_backlink_mdfile guiData.controls.Edit_title := Edit_title guiData.controls.Edit_markdown_template := Edit_markdown_template guiData.controls.Edit_image_template := Edit_image_template diff --git a/lib/gui/GuiControl.ahk b/lib/gui/GuiControl.ahk index 0ec7086..93f1c2b 100644 --- a/lib/gui/GuiControl.ahk +++ b/lib/gui/GuiControl.ahk @@ -3,65 +3,112 @@ #Include ..\BootUp.ahk #Include ..\..\markdown2potplayer.ahk -InitGui(app_config, potplayer_control){ +InitGui(app_config, potplayer_control) { guiData := creationGui() ; 回显:Potplayer路径 guiData.controls.Edit_potplayer.Value := app_config.PotplayerPath ; 点击选择potplayer路径 - guiData.controls.Button_potplayer.OnEvent("Click",SelectPotplayerProgram) - SelectPotplayerProgram(*){ + guiData.controls.Button_potplayer.OnEvent("Click", SelectPotplayerProgram) + SelectPotplayerProgram(*) { SelectedFile := FileSelect(1, , "Open a file", "Text Documents (*.exe)") - if SelectedFile{ + if SelectedFile { guiData.controls.edit_potplayer.Value := SelectedFile } } - guiData.controls.Button_potplayer.OnEvent("LoseFocus",(*) => app_config.PotplayerPath := guiData.controls.edit_potplayer.Value) - + guiData.controls.Button_potplayer.OnEvent("LoseFocus", (*) => app_config.PotplayerPath := guiData.controls.edit_potplayer.Value) + ; 回显:笔记软件名称 guiData.controls.Edit_note_app_name.Value := app_config.NoteAppName - guiData.controls.Edit_note_app_name.OnEvent("LoseFocus",(*) => app_config.NoteAppName := guiData.controls.Edit_note_app_name.Value) - + guiData.controls.Edit_note_app_name.OnEvent("LoseFocus", (*) => app_config.NoteAppName := guiData.controls.Edit_note_app_name.Value) + ; 回显:字幕模板 guiData.controls.Edit_subtitle_template.Value := app_config.SubtitleTemplate - guiData.controls.Edit_subtitle_template.OnEvent("LoseFocus",(*) => app_config.SubtitleTemplate := guiData.controls.Edit_subtitle_template.Value) + guiData.controls.Edit_subtitle_template.OnEvent("LoseFocus", (*) => app_config.SubtitleTemplate := guiData.controls.Edit_subtitle_template.Value) + guiData.controls.Button_srt_to_backlink_mdfile.OnEvent("Click", SelectSrtFiles) + SelectSrtFiles(*) { + SelectedFiles := FileSelect("M3", , "Open a file", "Text Documents (*.srt)") + if SelectedFiles.Length = 0 { + return + } + + InputBoxObj := InputBox("请输入视频文件的后缀(默认.mp4)", "提示", "", ".mp4") + videoFileExtension := ".mp4" + if InputBoxObj.Result = "Cancel" + return + else + videoFileExtension := InputBoxObj.Value + + for FileName in SelectedFiles { + subtitles := SubtitlesFromSrt(FileName) + if (subtitles) { + md_content := "" + for subtitle in subtitles { + SplitPath FileName, &name, &dir, &ext, &name_no_ext, &drive + videoFilePath := dir "\" name_no_ext videoFileExtension + + media_data := MediaData(videoFilePath, MilliSecondToTimestamp(subtitle.timeStart), subtitle.subtitle) + rendered_template := RenderSrtTemplate(app_config.SubtitleTemplate, media_data, subtitle) + rendered_template := RenderTemplate(rendered_template, media_data) "`r`n`r`n" + + md_content := md_content rendered_template + } + if (md_content != "") { + SplitPath FileName, &name, &dir, &ext, &name_no_ext, &drive + md_path := dir "\" name_no_ext ".md" + + if (FileExist(md_path)) { + result := MsgBox("文件:" md_path " 已经存在,是否进行覆盖?", , "YesNo") + if (result == "No") + return + FileDelete(md_path) + } + FileEncoding("CP0") + FileAppend(md_content, md_path) + + ToolTip("文件: " md_path " 生成成功!") + SetTimer () => ToolTip(), -2000 + } + } + } + } ; 回显:回链标题 guiData.controls.Edit_title.Value := app_config.MarkdownTitle - guiData.controls.Edit_title.OnEvent("LoseFocus",(*) => app_config.MarkdownTitle := guiData.controls.Edit_title.Value) - + guiData.controls.Edit_title.OnEvent("LoseFocus", (*) => app_config.MarkdownTitle := guiData.controls.Edit_title.Value) + ; 回显:回链模板 guiData.controls.Edit_markdown_template.Value := app_config.MarkdownTemplate - guiData.controls.Edit_markdown_template.OnEvent("LoseFocus",(*) => app_config.MarkdownTemplate := guiData.controls.Edit_markdown_template.Value) - + guiData.controls.Edit_markdown_template.OnEvent("LoseFocus", (*) => app_config.MarkdownTemplate := guiData.controls.Edit_markdown_template.Value) + ; 回显:图片回链模板 guiData.controls.Edit_image_template.Value := app_config.MarkdownImageTemplate - guiData.controls.Edit_image_template.OnEvent("LoseFocus",(*) => app_config.MarkdownImageTemplate := guiData.controls.Edit_image_template.Value) + guiData.controls.Edit_image_template.OnEvent("LoseFocus", (*) => app_config.MarkdownImageTemplate := guiData.controls.Edit_image_template.Value) ; 回显:发送图片延迟 guiData.controls.Edit_send_image_delays.Value := app_config.SendImageDelays - guiData.controls.Edit_send_image_delays.OnEvent("LoseFocus",(*) => app_config.SendImageDelays := guiData.controls.Edit_send_image_delays.Value) + guiData.controls.Edit_send_image_delays.OnEvent("LoseFocus", (*) => app_config.SendImageDelays := guiData.controls.Edit_send_image_delays.Value) ; 回显: 回链快捷键 guiData.controls.hk_backlink.Value := app_config.HotkeyBacklink guiData.controls.hk_backlink.OnEvent("Change", Update_Hk_Backlink) - Update_Hk_Backlink(GuiCtrlObj, Info){ + Update_Hk_Backlink(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyBacklink, GuiCtrlObj.Value, Potplayer2Obsidian) app_config.HotkeyBacklink := GuiCtrlObj.Value } - + ; 回显: 图片+回链快捷键 guiData.controls.hk_image_backlink.Value := app_config.HotkeyIamgeBacklink guiData.controls.hk_image_backlink.OnEvent("Change", Update_Hk_Image_Backlink) - Update_Hk_Image_Backlink(GuiCtrlObj, Info){ + Update_Hk_Image_Backlink(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyIamgeBacklink, GuiCtrlObj.Value, Potplayer2ObsidianImage) app_config.HotkeyIamgeBacklink := GuiCtrlObj.Value } - + ; 回显: ab片段快捷键 guiData.controls.hk_ab_fragment.Value := app_config.HotkeyAbFragment guiData.controls.hk_ab_fragment.OnEvent("Change", Update_Hk_Ab_Fragment) - Update_Hk_Ab_Fragment(GuiCtrlObj, Info){ + Update_Hk_Ab_Fragment(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyAbFragment, GuiCtrlObj.Value, Potplayer2ObsidianFragment) app_config.HotkeyAbFragment := GuiCtrlObj.Value } @@ -77,7 +124,7 @@ InitGui(app_config, potplayer_control){ ; 回显: ab循环快捷键 guiData.controls.hk_ab_circulation.Value := app_config.HotkeyAbCirculation guiData.controls.hk_ab_circulation.OnEvent("Change", Update_Hk_Ab_Circulation) - Update_Hk_Ab_Circulation(GuiCtrlObj, Info){ + Update_Hk_Ab_Circulation(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyAbCirculation, GuiCtrlObj.Value, Potplayer2ObsidianFragment) app_config.HotkeyAbCirculation := GuiCtrlObj.Value } @@ -85,23 +132,23 @@ InitGui(app_config, potplayer_control){ ;=================其他设置================= ; 回显: Url协议 guiData.controls.Edit_url_protocol.Value := app_config.UrlProtocol - guiData.controls.Edit_url_protocol.OnEvent("LoseFocus",(*) => app_config.UrlProtocol := guiData.controls.Edit_url_protocol.Value) + guiData.controls.Edit_url_protocol.OnEvent("LoseFocus", (*) => app_config.UrlProtocol := guiData.controls.Edit_url_protocol.Value) ; 回显:减少的时间 guiData.controls.Edit_reduce_time.Value := app_config.ReduceTime - guiData.controls.Edit_reduce_time.OnEvent("LoseFocus",(*) => app_config.ReduceTime := guiData.controls.Edit_reduce_time.Value) + guiData.controls.Edit_reduce_time.OnEvent("LoseFocus", (*) => app_config.ReduceTime := guiData.controls.Edit_reduce_time.Value) ; 回显:是否暂停 guiData.controls.CheckBox_is_stop.Value := app_config.IsStop guiData.controls.CheckBox_is_stop.OnEvent("Click", (*) => app_config.IsStop := guiData.controls.CheckBox_is_stop.Value) - + guiData.controls.CheckBox_remove_suffix_of_video_file.Value := app_config.MarkdownRemoveSuffixOfVideoFile guiData.controls.CheckBox_remove_suffix_of_video_file.OnEvent("Click", (*) => app_config.MarkdownRemoveSuffixOfVideoFile := guiData.controls.CheckBox_remove_suffix_of_video_file.Value) - + ; 回显:路径是否编码 guiData.controls.CheckBox_path_is_encode.Value := app_config.MarkdownPathIsEncode guiData.controls.checkBox_path_is_encode.OnEvent("Click", (*) => app_config.MarkdownPathIsEncode := guiData.controls.checkBox_path_is_encode.Value) - + ; 回显: 是否开机启动 guiData.controls.CheckBox_bootup.Value := get_boot_up() guiData.controls.CheckBox_bootup.OnEvent("Click", (*) => adaptive_bootup()) @@ -110,7 +157,7 @@ InitGui(app_config, potplayer_control){ ; 回显: 快捷键 上一帧 guiData.controls.hk_previous_frame.Value := app_config.HotkeyPreviousFrame guiData.controls.hk_previous_frame.OnEvent("Change", Update_Hk_Previous_Frame) - Update_Hk_Previous_Frame(GuiCtrlObj, Info){ + Update_Hk_Previous_Frame(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyPreviousFrame, GuiCtrlObj.Value, (*) => potplayer_control.PreviousFrame()) app_config.HotkeyPreviousFrame := GuiCtrlObj.Value } @@ -118,17 +165,17 @@ InitGui(app_config, potplayer_control){ ; 回显: 快捷键 下一帧 guiData.controls.hk_next_frame.Value := app_config.HotkeyNextFrame guiData.controls.hk_next_frame.OnEvent("Change", Update_Hk_Next_Frame) - Update_Hk_Next_Frame(GuiCtrlObj, Info){ + Update_Hk_Next_Frame(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyNextFrame, GuiCtrlObj.Value, (*) => potplayer_control.NextFrame()) app_config.HotkeyNextFrame := GuiCtrlObj.Value } ; 回显: 快捷键 前进 guiData.controls.Edit_forward_seconds.Value := app_config.ForwardSeconds - guiData.controls.Edit_forward_seconds.OnEvent("Change",(*) => app_config.ForwardSeconds := guiData.controls.Edit_forward_seconds.Value) + guiData.controls.Edit_forward_seconds.OnEvent("Change", (*) => app_config.ForwardSeconds := guiData.controls.Edit_forward_seconds.Value) guiData.controls.hk_forward.Value := app_config.HotkeyForward guiData.controls.hk_forward.OnEvent("Change", Update_Hk_Forward) - Update_Hk_Forward(GuiCtrlObj, Info){ + Update_Hk_Forward(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyForward, GuiCtrlObj.Value, (*) => potplayer_control.ForwardBySeconds(app_config.ForwardSeconds)) app_config.HotkeyForward := GuiCtrlObj.Value } @@ -138,7 +185,7 @@ InitGui(app_config, potplayer_control){ guiData.controls.Edit_backward_seconds.OnEvent("Change", (*) => app_config.BackwardSeconds := guiData.controls.Edit_backward_seconds.Value) guiData.controls.hk_backward.Value := app_config.HotkeyBackward guiData.controls.hk_backward.OnEvent("Change", Update_Hk_Backward) - Update_Hk_Backward(GuiCtrlObj, Info){ + Update_Hk_Backward(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyBackward, GuiCtrlObj.Value, (*) => potplayer_control.BackwardBySeconds(app_config.BackwardSeconds)) app_config.HotkeyBackward := GuiCtrlObj.Value } @@ -146,7 +193,7 @@ InitGui(app_config, potplayer_control){ ; 回显: 快捷键 播放/暂停 guiData.controls.hk_play_or_pause.Value := app_config.HotkeyPlayOrPause guiData.controls.hk_play_or_pause.OnEvent("Change", Update_Hk_Play_Or_Pause) - Update_Hk_Play_Or_Pause(GuiCtrlObj, Info){ + Update_Hk_Play_Or_Pause(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyPlayOrPause, GuiCtrlObj.Value, (*) => potplayer_control.PlayOrPause()) app_config.HotkeyPlayOrPause := GuiCtrlObj.Value } @@ -154,7 +201,7 @@ InitGui(app_config, potplayer_control){ ; 回显: 快捷键 停止 guiData.controls.hk_stop.Value := app_config.HotkeyStop guiData.controls.hk_stop.OnEvent("Change", Update_Hk_Stop) - Update_Hk_Stop(GuiCtrlObj, Info){ + Update_Hk_Stop(GuiCtrlObj, Info) { RefreshHotkey(app_config.HotkeyStop, GuiCtrlObj.Value, (*) => potplayer_control.Stop()) app_config.HotkeyStop := GuiCtrlObj.Value } diff --git a/lib/gui/GuiExample.ahk b/lib/gui/GuiExample.ahk index 120edcd..ccfadd0 100644 --- a/lib/gui/GuiExample.ahk +++ b/lib/gui/GuiExample.ahk @@ -1,4 +1,3 @@ - #Requires Autohotkey v2 #SingleInstance force @@ -21,10 +20,10 @@ if A_LineFile = A_ScriptFullPath && !A_IsCompiled { myMenu.Default := "&Open" myMenu.ClickCount := 2 - myMenu.Rename("&Open" , "打开") - myMenu.Rename("E&xit" , "退出") - myMenu.Rename("&Pause Script" , "暂停脚本") - myMenu.Rename("&Suspend Hotkeys" , "暂停热键") + myMenu.Rename("&Open", "打开") + myMenu.Rename("E&xit", "退出") + myMenu.Rename("&Pause Script", "暂停脚本") + myMenu.Rename("&Suspend Hotkeys", "暂停热键") myGui.Show("w485 h774") } @@ -44,52 +43,56 @@ Constructor() { Edit_note_app_name := myGui.Add("Edit", "x160 y48 w162 h63 +Multi", "Obsidian.exe`nTypora.exe") myGui.Add("Text", "x160 y121 w123 h23", "多个笔记软件每行一个") - myGui.Add("Text", "x40 y140 w63 h23", "字幕模板") - Edit_note_app_name := myGui.Add("Edit", "x160 y140 w162 h30 +Multi", "字幕:`n{subtitle}") + myGui.Add("Text", "x40 y136 w63 h23", "字幕模板") + Edit_note_app_name := myGui.Add("Edit", "x160 y136 w162 h30 +Multi", "字幕:`n{subtitle}") + Button_srt_to_backlink_mdfile := myGui.Add("Button", "x384 y136 w103 h23", "srt转回链md") - myGui.Add("Text", "x40 y208 w63 h23", "回链的名称") - Edit_title := myGui.Add("Edit", "x160 y208 w148 h23", "{name} | {time}") + myGui.Add("Text", "x40 y185 w63 h23", "回链的名称") + Edit_title := myGui.Add("Edit", "x160 y185 w148 h23", "{name} | {time}") - myGui.Add("Text", "x40 y240 w51 h23", "回链模板") - Edit_markdown_template := myGui.Add("Edit", "x160 y240 w149 h48 +Multi", "`n视频:{title}`n") + myGui.Add("Text", "x40 y223 w51 h23", "回链模板") + Edit_markdown_template := myGui.Add("Edit", "x160 y223 w149 h48 +Multi", "`n视频:{title}`n") - myGui.Add("Text", "x40 y295 w77 h23", "图片回链模板") - Edit_image_template := myGui.Add("Edit", "x160 y295 w151 h66 +Multi", "`n图片:{image}`n视频:{title}`n") + myGui.Add("Text", "x40 y284 w77 h23", "图片回链模板") + Edit_image_template := myGui.Add("Edit", "x160 y284 w151 h66 +Multi", "`n图片:{image}`n视频:{title}`n") - myGui.Add("Text", "x40 y365 w111", "图片粘贴延迟") - Edit6 := myGui.Add("Edit", "x160 y365 w120 h21") - myGui.Add("Text", "x288 y365 w22 h23", "ms") + myGui.Add("Text", "x40 y355 w111", "图片粘贴延迟") + Edit6 := myGui.Add("Edit", "x160 y355 w120 h21") + myGui.Add("Text", "x288 y355 w22 h23", "ms") ; =======快捷键========= - myGui.Add("Text", "x40 y399 w105 h23", "回链快捷键") - hk_backlink := myGui.Add("Hotkey", "x160 y399 w156 h21", "!g") + myGui.Add("Text", "x40 y387 w105 h23", "字幕快捷键") + hk_subtitle := myGui.Add("Hotkey", "x160 y387 w156 h21", "!t") + + myGui.Add("Text", "x40 y416 w105 h23", "回链快捷键") + hk_backlink := myGui.Add("Hotkey", "x160 y416 w156 h21", "!g") - myGui.Add("Text", "x40 y431 w105 h32", "图片+回链快捷键") - hk_image_backlink := myGui.Add("Hotkey", "x160 y431 w156 h21", "^!g") + myGui.Add("Text", "x40 y448 w105 h32", "图片+回链快捷键") + hk_image_backlink := myGui.Add("Hotkey", "x160 y448 w156 h21", "^!g") - myGui.Add("Text", "x40 y455 w114 h23", "A-B片段快捷键") - hk_ab_fragment := myGui.Add("Hotkey", "x160 y455 w156 h21","F1") + myGui.Add("Text", "x40 y472 w114 h23", "A-B片段快捷键") + hk_ab_fragment := myGui.Add("Hotkey", "x160 y472 w156 h21", "F1") - myGui.Add("Text", "x40 y484 w98 h23", "A-B片段检测延迟") - Edit_ab_fragment_detection_delays := myGui.Add("Edit", "x160 y484 w120 h21","1000") - myGui.Add("Text", "x288 y484 w31 h23", "ms") + myGui.Add("Text", "x40 y501 w98 h23", "A-B片段检测延迟") + Edit_ab_fragment_detection_delays := myGui.Add("Edit", "x160 y501 w120 h21", "1000") + myGui.Add("Text", "x288 y501 w31 h23", "ms") - CheckBox_loop_ab_fragment := myGui.Add("CheckBox", "x160 y501 w120 h23", "循环播放片段") + CheckBox_loop_ab_fragment := myGui.Add("CheckBox", "x160 y518 w120 h23", "循环播放片段") - myGui.Add("Text", "x40 y530 w105 h12", "A-B循环快捷键") - hk_ab_circulation := myGui.Add("Hotkey", "x160 y530 w156 h21") + myGui.Add("Text", "x40 y547 w105 h12", "A-B循环快捷键") + hk_ab_circulation := myGui.Add("Hotkey", "x160 y547 w156 h21") ; =======其他设置========= - myGui.Add("Text", "x40 y565 w105 h36", "修改协议【谨慎】此项重启生效") - Edit_url_protocol := myGui.Add("Edit", "x160 y565 w156 h21", "jv://open") + myGui.Add("Text", "x40 y582 w105 h36", "修改协议【谨慎】此项重启生效") + Edit_url_protocol := myGui.Add("Edit", "x160 y582 w156 h21", "jv://open") - myGui.Add("Text", "x40 y601 w60 h23", "减少的时间") - Edit_reduce_time := myGui.Add("Edit", "x160 y601 w120 h21", "0") + myGui.Add("Text", "x40 y618 w60 h23", "减少的时间") + Edit_reduce_time := myGui.Add("Edit", "x160 y618 w120 h21", "0") - CheckBox_is_stop := myGui.Add("CheckBox", "x160 y625 w69 h23", "暂停") - CheckBox_remove_suffix_of_video_file := myGui.Add("CheckBox", "x160 y649 w150 h23", "本地视频移除文件后缀名") - CheckBox_path_is_encode := myGui.Add("CheckBox", "x160 y673 w120 h23", "路径编码") - CheckBox_bootup := myGui.Add("CheckBox", "x160 y697 w120 h23", "开机启动") + CheckBox_is_stop := myGui.Add("CheckBox", "x160 y642 w69 h23", "暂停") + CheckBox_remove_suffix_of_video_file := myGui.Add("CheckBox", "x160 y666 w150 h23", "本地视频移除文件后缀名") + CheckBox_path_is_encode := myGui.Add("CheckBox", "x160 y690 w120 h23", "路径编码") + CheckBox_bootup := myGui.Add("CheckBox", "x160 y714 w120 h23", "开机启动") Tab.UseTab(2) myGui.Add("Text", "x86 y24 w42 h23", "上一帧") @@ -111,6 +114,6 @@ Constructor() { Tab.UseTab() myGui.Add("Link", "x432 y755 w48 h12", "检查更新") - + return myGui } \ No newline at end of file diff --git a/lib/gui/i18n/en-US.ini b/lib/gui/i18n/en-US.ini index b330e55..ddbf9a8 100644 --- a/lib/gui/i18n/en-US.ini +++ b/lib/gui/i18n/en-US.ini @@ -10,6 +10,7 @@ Gui_backlink_name=Backlink Name Gui_backlink_template=Backlink Template Gui_image_backlink_tempalte_name=Image Backlink Template Gui_send_image_delays=Delays after pasting images +Gui_hotkey_title=Subtitle Hotkey Gui_hotkey_backlink=Backlink Hotkey Gui_hotkey_image_and_backlink=Image + Backlink Hotkey Gui_hotkey_ab_fragment=A-B Segment Hotkey diff --git a/lib/gui/i18n/zh-CN.ini b/lib/gui/i18n/zh-CN.ini index ec251dc..55dca70 100644 --- a/lib/gui/i18n/zh-CN.ini +++ b/lib/gui/i18n/zh-CN.ini @@ -10,6 +10,7 @@ Gui_backlink_name= Gui_backlink_template=ģ Gui_image_backlink_tempalte_name=ͼƬģ Gui_send_image_delays=ͼƬճӳ +Gui_hotkey_title=Ļݼ Gui_hotkey_backlink=ݼ Gui_hotkey_image_and_backlink=ͼƬ+ݼ Gui_hotkey_ab_fragment=A-BƬοݼ diff --git a/lib/note2potplayer/note2potplayer.ahk b/lib/note2potplayer/note2potplayer.ahk index 817c7da..91c36b5 100644 --- a/lib/note2potplayer/note2potplayer.ahk +++ b/lib/note2potplayer/note2potplayer.ahk @@ -13,7 +13,7 @@ potplayer := {} potplayer.info := EnrichInfo(potplayer_path) potplayer.control := PotplayerControl(potplayer.info.path) -EnrichInfo(potplayer_path){ +EnrichInfo(potplayer_path) { info := {} info.path := potplayer_path @@ -31,39 +31,39 @@ EnrichInfo(potplayer_path){ ; 2. 主逻辑 AppMain() -AppMain(){ +AppMain() { CallbackPotplayer() } ; 【主逻辑】Potplayer的回调函数(回链) -CallbackPotplayer(){ +CallbackPotplayer() { url := ReceivParameter() - if url{ + if url { ParseUrl(url) - }else{ + } else { MsgBox "至少传递1个参数" } ExitApp } -ReceivParameter(){ +ReceivParameter() { ; 获取命令行参数的数量 paramCount := A_Args.Length ; 如果没有参数,显示提示信息 if (paramCount = 0) { - return false + return false } params := "" ; 循环遍历参数并显示在控制台 - for n, param in A_Args{ + for n, param in A_Args { params .= param " " } return Trim(params) } -ParseUrl(url){ +ParseUrl(url) { ;url := "jv://open?path=https://www.bilibili.com/video/123456/?spm_id_from=..search-card.all.click&time=00:01:53.824" ; MsgBox url url := UrlDecode(url) @@ -83,9 +83,9 @@ ParseUrl(url){ parameters_map[key] := value } } - + ; 1.2 对path参数特殊处理,因为路径中可能是网址 - path := SubStr(parameters_of_url,1, InStr(parameters_of_url, "&time=") -1) + path := SubStr(parameters_of_url, 1, InStr(parameters_of_url, "&time=") - 1) path := StrReplace(path, "path=", "") parameters_map["path"] := path @@ -97,38 +97,38 @@ ParseUrl(url){ potplayer.jump.timeSpan := parameters_map["time"] ; 情况0:是同一个视频进行跳转,之前可能设置了AB循环,所以此处先取消A-B循环 - if(potplayer.info.isRunning - && potplayer.control.GetPlayStatus() != "Stopped" - && IsSameVideo(potplayer.jump.path)){ - potplayer.control.CancelTheABCycle() + if (potplayer.info.isRunning + && potplayer.control.GetPlayStatus() != "Stopped" + && IsSameVideo(potplayer.jump.path)) { + potplayer.control.CancelTheABCycle() } ; 情况1:单个时间戳 00:01:53 - if(IsSingleTimestamp(potplayer.jump.timeSpan)){ + if (IsSingleTimestamp(potplayer.jump.timeSpan)) { JumpToSingleTimestamp(potplayer.jump.path, potplayer.jump.timeSpan) ; 情况2:时间戳片段 00:01:53-00:02:53 - } else if(IsAbFragment(potplayer.jump.timeSpan)){ - if(GetKeyName("loop_ab_fragment")){ + } else if (IsAbFragment(potplayer.jump.timeSpan)) { + if (GetKeyName("loop_ab_fragment")) { JumpToAbCirculation(potplayer.jump.path, potplayer.jump.timeSpan) - }else{ + } else { JumpToAbFragment(potplayer.jump.path, potplayer.jump.timeSpan) } ; 情况3:时间戳循环 00:01:53∞00:02:53 - } else if(IsAbCirculation(potplayer.jump.timeSpan)){ + } else if (IsAbCirculation(potplayer.jump.timeSpan)) { JumpToAbCirculation(potplayer.jump.path, potplayer.jump.timeSpan) } ExitApp() } ; 解析时间片段字符串 -TimeSpanToTime(media_time){ +TimeSpanToTime(media_time) { ; 1. 解析时间戳 time_separator := ["∞", "-"] index_of := "" - Loop time_separator.Length{ + Loop time_separator.Length { index_of := InStr(media_time, time_separator[A_Index]) - if(index_of > 0){ + if (index_of > 0) { break } } @@ -141,44 +141,45 @@ TimeSpanToTime(media_time){ } ; 判断当前播放的视频,是否是跳转的视频 -IsSameVideo(jump_path){ - ; 判断网络视频 - if(InStr(jump_path,"http")){ - current_path := GetPotplayerpath() - if(InStr(jump_path,current_path)){ - return true - } - - GetPotplayerpath(){ - A_Clipboard := "" - potplayer.control.GetMediaPathToClipboard() - ClipWait 1,0 - return A_Clipboard - } - } - - ; 判断本地视频 - potplayer_title := WinGetTitle("ahk_id " potplayer.control.GetOncePotplayerHwnd()) - if (InStr(potplayer_title, GetNameForPath(jump_path))) { +IsSameVideo(jump_path) { + ; 判断网络视频 + if (InStr(jump_path, "http")) { + current_path := GetPotplayerpath() + if (InStr(jump_path, current_path)) { return true } + + GetPotplayerpath() { + A_Clipboard := "" + potplayer.control.GetMediaPathToClipboard() + ClipWait 1, 0 + return A_Clipboard + } + } + + ; 判断本地视频 + potplayer_title := WinGetTitle("ahk_id " potplayer.control.GetOncePotplayerHwnd()) + video_name := GetNameForPath(UrlDecode(jump_path)) + if (InStr(potplayer_title, video_name)) { + return true + } } ; 字符串中不包含"-、∞",则为单个时间戳 -IsSingleTimestamp(media_time){ - if(InStr(media_time, "-") > 0 || InStr(media_time, "∞") > 0) +IsSingleTimestamp(media_time) { + if (InStr(media_time, "-") > 0 || InStr(media_time, "∞") > 0) return false else return true } ; 使用时间戳跳转 -OpenPotplayerAndJumpToTimestamp(media_path, media_time){ +OpenPotplayerAndJumpToTimestamp(media_path, media_time) { run_command := potplayer_path . " `"" . media_path . "`" /seek=" . media_time . " " . potplayer.info.openWindow - try{ + try { Run run_command } catch Error as err - if err.Extra{ + if err.Extra { MsgBox "错误:" err.Extra MsgBox run_command } else { @@ -186,28 +187,28 @@ OpenPotplayerAndJumpToTimestamp(media_path, media_time){ } } -IsAbFragment(media_time){ - if(InStr(media_time, "-") > 0) +IsAbFragment(media_time) { + if (InStr(media_time, "-") > 0) return true else return false } -JumpToSingleTimestamp(path, time){ - if(potplayer.info.isRunning - && potplayer.control.GetPlayStatus() != "Stopped" - && IsSameVideo(potplayer.jump.path)){ - potplayer.control.SetMediaTimeMilliseconds(TimestampToMilliSecond(time)) - potplayer.control.Play() - }else{ +JumpToSingleTimestamp(path, time) { + if (potplayer.info.isRunning + && potplayer.control.GetPlayStatus() != "Stopped" + && IsSameVideo(potplayer.jump.path)) { + potplayer.control.SetMediaTimeMilliseconds(TimestampToMilliSecond(time)) + potplayer.control.Play() + } else { OpenPotplayerAndJumpToTimestamp(path, time) } } -JumpToAbFragment(media_path, media_time_span){ +JumpToAbFragment(media_path, media_time_span) { ; 1. 解析时间戳 potplayer.jump.time := TimeSpanToTime(media_time_span) - + ; 2. 跳转 CallPotplayer() Sleep 500 @@ -215,7 +216,7 @@ JumpToAbFragment(media_path, media_time_span){ flag_ab_fragment := true Hotkey "Esc", CancelAbFragment - CancelAbFragment(*){ + CancelAbFragment(*) { flag_ab_fragment := false Hotkey "Esc", "off" } @@ -227,16 +228,16 @@ JumpToAbFragment(media_path, media_time_span){ ; 异常情况:用户关闭Potplayer if (!IsPotplayerRunning(potplayer_path)) { break - ; 异常情况:用户停止播放视频 + ; 异常情况:用户停止播放视频 } else if (potplayer.control.GetPlayStatus() != "Running") { break } - ; 异常情况:不是同一个视频 + ; todo: 异常情况:不是同一个视频 ; - 在播放B站视频时,可以加载视频列表,这样用户就会切换视频,此时就要结束循环 else if (!IsSameVideo(media_path)) {break} ; - 另一种思路:当前循环的时间超过了时间期间,就结束循环; +5是为了防止误差 - else if (past >= duration + 5000) { - break - } + ; else if (past >= duration + 5000) { + ; break + ; } ; 正常情况:当前播放时间超过了结束时间、用户手动调整时间,超过了结束时间 current_time := potplayer.control.GetMediaTimeMilliseconds() @@ -250,17 +251,17 @@ JumpToAbFragment(media_path, media_time_span){ } } -IsAbCirculation(time_span){ - if(InStr(time_span, "∞") > 0) +IsAbCirculation(time_span) { + if (InStr(time_span, "∞") > 0) return true else return false } -JumpToAbCirculation(media_path, media_time_span){ +JumpToAbCirculation(media_path, media_time_span) { call_data := {} call_data.media_path := media_path potplayer.jump.time := time := TimeSpanToTime(media_time_span) - + ; 2. 跳转 CallPotplayer() @@ -272,19 +273,19 @@ JumpToAbCirculation(media_path, media_time_span){ potplayer.control.SetEndPointOfTheABCycle() } -CallPotplayer(){ - if(potplayer.info.isRunning +CallPotplayer() { + if (potplayer.info.isRunning && potplayer.control.GetPlayStatus() != "Stopped" - && IsSameVideo(potplayer.jump.path)){ + && IsSameVideo(potplayer.jump.path)) { potplayer.control.SetMediaTimeMilliseconds(TimestampToMilliSecond(potplayer.jump.time.start)) potplayer.control.Play() - }else{ + } else { ; 播放指定视频 PlayVideo(potplayer.jump.path, potplayer.jump.time.start) } } -PlayVideo(media_path, time_start){ - if(potplayer.info.isRunning && potplayer.control.GetPlayStatus() != "Stopped"){ +PlayVideo(media_path, time_start) { + if (potplayer.info.isRunning && potplayer.control.GetPlayStatus() != "Stopped") { potplayer.control.Stop() } OpenPotplayerAndJumpToTimestamp(media_path, time_start) @@ -292,12 +293,12 @@ PlayVideo(media_path, time_start){ potplayer.control.Play() } ; 已开Potplayer跳转到下一个视频,判断当前potplayer播放器的状态 -WaitForPotplayerToFinishLoadingTheVideo(video_name){ +WaitForPotplayerToFinishLoadingTheVideo(video_name) { WinWaitActive("ahk_exe " GetNameForPath(potplayer_path)) while (true) { - if(WinGetTitle("ahk_id " potplayer.control.GetOncePotplayerHwnd()) != "PotPlayer" - && potplayer.control.GetPlayStatus() == "Running"){ + if (WinGetTitle("ahk_id " potplayer.control.GetOncePotplayerHwnd()) != "PotPlayer" + && potplayer.control.GetPlayStatus() == "Running") { break } Sleep 1000 diff --git a/lib/socket/SocketService.ahk b/lib/socket/SocketService.ahk new file mode 100644 index 0000000..82dff11 --- /dev/null +++ b/lib/socket/SocketService.ahk @@ -0,0 +1,49 @@ +#Requires AutoHotkey v2.0 + +#Include Socket.ahk + +InitServer() { + sock := winsock("server", callback, "IPV4") + sock.Bind("0.0.0.0", 33660) + sock.Listen() + + callback(sock, event, err) { + if (sock.name = "server") || instr(sock.name, "serving-") { + if (event = "accept") { + sock.Accept(&addr, &newsock) ; pass &addr param to extract addr of connected machine + } else if (event = "close") { + } else if (event = "read") { + If !(buf := sock.Recv()).size + return + + ; 返回html + html_body := '

open potplayer...

' + httpResponse := "HTTP/1.1 200 0K`r`n" + . "Content-Type: text/html; charset=UTF-8`r`n" + . "Content-Length: " StrLen(html_body) "`r`n" + . "`r`n" + httpResponse := httpResponse html_body + strbuf := Buffer(StrPut(httpResponse, "UTF-8")) + StrPut(httpResponse, strbuf, "UTF-8") + sock.Send(strbuf) + sock.ConnectFinish() + + ; 得到回链 + request := strget(buf, "UTF-8") + RegExMatch(request, "GET /(.+?) HTTP/1.1", &match) + if (match == "") { + return + } + backlink := match[1] + if (!InStr(backlink, "path=")) { + return + } + + ; 打开potplayer + Run(backlink) + ; 关闭 notion打开的网页标签 + Send "^w" + } + } + } +} \ No newline at end of file diff --git a/lib/sqlite/SqliteControl.ahk b/lib/sqlite/SqliteControl.ahk index 767d7b2..a39d9d6 100644 --- a/lib/sqlite/SqliteControl.ahk +++ b/lib/sqlite/SqliteControl.ahk @@ -37,22 +37,19 @@ InitConfigSqlite() { ; 字幕模板 subtitle_template: "字幕:{subtitle}", ; 回链的设置 - title: "{name} | {time} | {subtitleTemplate}", + title: "{name} | {time}", template: "`n" . "视频:{title}" - . "`n" - . "{subtitleTemplate}" . "`n", image_template: "`n" . "图片:{image}" . "`n" . "视频:{title}" - . "`n" - . "{subtitleTemplate}" . "`n", ; 回链快捷键相关 + hotkey_subtitle: "!t", hotkey_backlink: "!g", hotkey_iamge_backlink: "^!g", hotkey_ab_fragment: "F1", diff --git a/lib/srt.ahk b/lib/srt.ahk new file mode 100644 index 0000000..24e745a --- /dev/null +++ b/lib/srt.ahk @@ -0,0 +1,81 @@ +#Requires AutoHotkey v2.0 +#Include CharsetDetect.ahk + +SubtitlesFromSrt(srtPath) { + if not FileExist(srtPath) { + MsgBox "The target file:" srtPath " does not exist." + Exit + } + + detect := TextEncodingDetect() + FileEncoding(detect.DetectEncoding(srtPath)) + + srtContent := FileRead(srtPath) + + pattern := "(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})\R([\s\S]*?)(?:\R\R|\z)" + + ; 查找所有匹配项并转换时间为毫秒 + subtitles_data := [] + pos := 1 + while (pos := RegExMatch(srtContent, pattern, &match, pos)) { + subtitles_data.Push({ + timeStart: SrtTimeToMs(match[1]), + timeEnd: SrtTimeToMs(match[2]), + subtitle: Trim(match[3]) + }) + pos += match.Len + } + return subtitles_data +} + +; 函数:根据时间戳查找对应的字幕 +FindSubtitleByTimestamp(timestamp, subtitles_data) { + for subtitle_data in subtitles_data { + if (subtitle_data.timeStart <= timestamp && timestamp <= subtitle_data.timeEnd) { + return subtitle_data + } + } + return false +} + +; 测试 +; subtitles := SubtitlesFromSrt("../test.srt") + +; for subtitle in subtitles { +; MsgBox("找到匹配的字幕:`n开始时间: " MsToSrtTime(subtitle.startTime) +; "`n结束时间: " MsToSrtTime(subtitle.endTime) +; "`n字幕内容: " subtitle.subtitle) +; } + +; userTimestamp := 180000 ; 假设用户输入的时间戳是180秒(180000毫秒) +; result := FindSubtitleByTimestamp(userTimestamp, subtitles) + +; if (result) { +; MsgBox("找到匹配的字幕:`n开始时间: " MsToSrtTime(result.startTime) +; "`n结束时间: " MsToSrtTime(result.endTime) +; "`n字幕内容: " result.subtitle) +; } else { +; MsgBox("未找到匹配的字幕。") +; } + +; 辅助函数 +; 将毫秒转换回SRT时间格式 +MsToSrtTime(ms) { + hours := Floor(ms / 3600000) + minutes := Floor((ms - hours * 3600000) / 60000) + seconds := Floor((ms - hours * 3600000 - minutes * 60000) / 1000) + milliseconds := ms - hours * 3600000 - minutes * 60000 - seconds * 1000 + + return Format("{:02d}:{:02d}:{:02d},{:03d}", hours, minutes, seconds, milliseconds) +} + +; SrtTimeToMs 函数定义(与之前相同) +SrtTimeToMs(timeStr) { + timeParts := StrSplit(timeStr, ":") + ms := StrSplit(timeParts[3], ",")[2] + + return (Integer(timeParts[1]) * 3600000) + ; 小时 + (Integer(timeParts[2]) * 60000) + ; 分钟 + (Integer(StrSplit(timeParts[3], ",")[1]) * 1000) + ; 秒 + Integer(ms) ; 毫秒 +} \ No newline at end of file diff --git a/markdown2potplayer.ahk b/markdown2potplayer.ahk index f49ea80..305a8c7 100644 --- a/markdown2potplayer.ahk +++ b/markdown2potplayer.ahk @@ -5,8 +5,9 @@ #Include "lib\ReduceTime.ahk" #Include "lib\TemplateParser.ahk" #Include "lib\sqlite\SqliteControl.ahk" -#Include lib\socket\Socket.ahk - +#Include lib\srt.ahk +#Include lib\Redner.ahk +#Include lib\socket\SocketService.ahk #Include lib\entity\Config.ahk #Include lib\entity\MediaData.ahk #Include lib\PotplayerControl.ahk @@ -31,54 +32,9 @@ main() { RegisterHotKey() } -InitServer() { - sock := winsock("server", callback, "IPV4") - sock.Bind("0.0.0.0", 33660) - sock.Listen() - - callback(sock, event, err) { - if (sock.name = "server") || instr(sock.name, "serving-") { - if (event = "accept") { - sock.Accept(&addr, &newsock) ; pass &addr param to extract addr of connected machine - } else if (event = "close") { - } else if (event = "read") { - If !(buf := sock.Recv()).size - return - - ; 返回html - html_body := '

open potplayer...

' - httpResponse := "HTTP/1.1 200 0K`r`n" - . "Content-Type: text/html; charset=UTF-8`r`n" - . "Content-Length: " StrLen(html_body) "`r`n" - . "`r`n" - httpResponse := httpResponse html_body - strbuf := Buffer(StrPut(httpResponse, "UTF-8")) - StrPut(httpResponse, strbuf, "UTF-8") - sock.Send(strbuf) - sock.ConnectFinish() - - ; 得到回链 - request := strget(buf, "UTF-8") - RegExMatch(request, "GET /(.+?) HTTP/1.1", &match) - if (match == "") { - return - } - backlink := match[1] - if (!InStr(backlink, "path=")) { - return - } - - ; 打开potplayer - Run(backlink) - ; 关闭 notion打开的网页标签 - Send "^w" - } - } - } -} - RegisterHotKey() { HotIf CheckCurrentProgram + Hotkey(app_config.HotkeySubtitle " Up", Potplayer2Obsidian) Hotkey(app_config.HotkeyBacklink " Up", Potplayer2Obsidian) Hotkey(app_config.HotkeyIamgeBacklink " Up", Potplayer2ObsidianImage) Hotkey(app_config.HotkeyAbFragment " Up", Potplayer2ObsidianFragment) @@ -134,55 +90,33 @@ CheckCurrentProgram(*) { } ; 【主逻辑】将Potplayer的播放链接粘贴到Obsidian中 -Potplayer2Obsidian(*) { +Potplayer2Obsidian(hotkey) { ReleaseCommonUseKeyboard() - media_data := MediaData(GetMediaPath(), GetMediaTime(), GetMediaSubtitle()) - - rendered_backlink_template := RenderBacklinkTemplate(app_config.MarkdownTemplate, media_data) + media_data := MediaData(GetMediaPath(), GetMediaTime(), "") - PauseMedia() + if (hotkey == (app_config.HotkeySubtitle " Up")) { + backlink_template := app_config.SubtitleTemplate - if (IsWordProgram()) { - SendText2wordApp(rendered_backlink_template) + rendered_template := RenderTemplate(app_config.SubtitleTemplate, media_data) } else { - SendText2NoteApp(rendered_backlink_template) + rendered_template := RenderTemplate(app_config.MarkdownTemplate, media_data) } -} -RenderSubtitleTemplate(target_string, media_data) { - if (InStr(target_string, "{subtitleTemplate}") != 0) { - if (media_data.Subtitle) { - rendered_subtitle_template := StrReplace(app_config.SubtitleTemplate, "{subtitle}", media_data.Subtitle) - target_string := StrReplace(target_string, "{subtitleTemplate}", rendered_subtitle_template) - } else { - target_string := StrReplace(target_string, "{subtitleTemplate}", "") - } - } - return target_string -} - -RenderBacklinkTemplate(backlink_template, media_data) { - ; 目前可用的 4个 关键字 {name}、{time}、{subtitle}、{image} - ; 2个 模板 {title}、{subtitleTemplate} + PauseMedia() - ; 渲染 标题 - if (InStr(backlink_template, "{title}") != 0) { - rendered_link := RenderTitle(app_config, media_data) - backlink_template := StrReplace(backlink_template, "{title}", rendered_link) + if (IsWordProgram()) { + SendText2wordApp(rendered_template) + } else { + SendText2NoteApp(rendered_template) } - - ; 2. 渲染 字幕模板 - backlink_template := RenderSubtitleTemplate(backlink_template, media_data) - - return backlink_template } ; 【主逻辑】粘贴图像 Potplayer2ObsidianImage(*) { ReleaseCommonUseKeyboard() - media_data := MediaData(GetMediaPath(), GetMediaTime(), GetMediaSubtitle()) + media_data := MediaData(GetMediaPath(), GetMediaTime(), "") image := SaveImage() PauseMedia() @@ -201,14 +135,13 @@ GetMediaTime() { } return time } + +GetMediaTimeMilliseconds() { + return potplayer_control.GetMediaTimeMilliseconds() +} GetMediaSubtitle() { subtitle_from_otplayer := "" - if (InStr(app_config.MarkdownTitle, "{subtitleTemplate}") != 0 || - InStr(app_config.MarkdownTemplate, "{subtitleTemplate}") != 0 - InStr(app_config.MarkdownImageTemplate, "{subtitleTemplate}") != 0) { - - subtitle_from_otplayer := PressDownHotkey(potplayer_control.GetSubtitleToClipboard) - } + subtitle_from_otplayer := PressDownHotkey(potplayer_control.GetSubtitleToClipboard) return subtitle_from_otplayer } PressDownHotkey(operate_potplayer) { @@ -236,19 +169,6 @@ PauseMedia() { } } -RenderTitle(app_config, media_data) { - markdown_link_data := GenerateBackLinkData(app_config, media_data) - ; 生成word链接 - if (IsWordProgram()) { - result_link := "" markdown_link_data.title "" - - } else { - ; 生成MarkDown链接 - result_link := GenerateMarkdownLink(markdown_link_data.title, markdown_link_data.link) - } - return result_link -} - IsWordProgram() { target_program := SelectedNoteProgram(app_config.NoteAppName) return target_program == "wps.exe" || target_program == "winword.exe" @@ -261,45 +181,6 @@ IsNotionProgram() { || target_program == "firefox.exe" } -; // [用户想要的标题格式](mk-potplayer://open?path=1&aaa=123&time=456) -GenerateBackLinkData(app_config, media_data) { - ; B站的视频 - if (InStr(media_data.Path, "https://www.bilibili.com/video/")) { - ; 正常播放的情况 - name := StrReplace(GetPotplayerTitle(app_config.PotplayerProcessName), " - PotPlayer", "") - - ; 视频没有播放,已经停止的情况,不是暂停是停止 - if name == "PotPlayer" { - name := GetFileNameInPath(media_data.Path) - } - } else { - ; 本地视频 - name := GetFileNameInPath(media_data.Path) - } - ; 渲染 title - title := app_config.MarkdownTitle - title := StrReplace(title, "{name}", name) - title := StrReplace(title, "{time}", media_data.Time) - ; 渲染 title 中的 字幕模板 - title := RenderSubtitleTemplate(title, media_data) - - markdown2potplayer_link := app_config.UrlProtocol "?path=" ProcessUrl(media_data.Path) "&time=" media_data.Time - - result := {} - result.title := title - result.link := markdown2potplayer_link - return result -} - -GenerateMarkdownLink(markdown_title, markdown_link) { - if (IsNotionProgram()) { - result := "[" markdown_title "](http://127.0.0.1:33660/" markdown_link ")" - } else { - result := "[" markdown_title "](" markdown_link ")" - } - return result -} - GetFileNameInPath(path) { name := GetNameForPath(path) if (app_config.MarkdownRemoveSuffixOfVideoFile != "0") { @@ -308,23 +189,6 @@ GetFileNameInPath(path) { return name } -RenderImage(markdown_image_template, media_data, image) { - identifier := "{image}" - image_templates := TemplateConvertedToTemplates(markdown_image_template, identifier) - For index, image_template in image_templates { - if (image_template == identifier) { - SendImage2NoteApp(image) - } else { - rendered_template := RenderBacklinkTemplate(image_template, media_data) - if (IsWordProgram() && InStr(image_template, "{title}")) { - SendText2wordApp(rendered_template) - } else { - SendText2NoteApp(rendered_template) - } - } - } -} - RemoveSuffix(name) { index_of := InStr(name, ".", , -1) if (index_of = 0) { @@ -337,13 +201,18 @@ RemoveSuffix(name) { ; 路径地址处理 ProcessUrl(media_path) { ; 进行Url编码 - if (app_config.MarkdownPathIsEncode != "0") { - media_path := UrlEncode(media_path) - } else { + if (app_config.MarkdownPathIsEncode != "0" || ; 全系urlencode的bug:如果路径中存在"\["会让,在【ob的预览模式】下(回链会被ob自动urlencode),"\"离奇消失变为,"[";例如:G:\BaiduSyncdisk\123\[456] 在bug下变为:G:\BaiduSyncdisk\123[456] <= 丢失了"\" ; 所以先将"\["替换为"%5C["(\的urlencode编码%5C)。变为:G:\BaiduSyncdisk\123%5C[456] - media_path := StrReplace(media_path, "\[", "%5C[") - media_path := StrReplace(media_path, "\!", "%5C!") + ; Typora能打开 + InStr(media_path, "\[") != 0 || + InStr(media_path, "\!") != 0) { + media_path := UrlEncode(media_path) + } else { + ; ob 能打开,Typora打不开 + ; media_path := StrReplace(media_path, "\[", "%5C[") + ; media_path := StrReplace(media_path, "\!", "%5C!") + ; 但是 obidian中的potplayer回链路径有空格,在obsidian的预览模式【无法渲染】,所以将空格进行Url编码 media_path := StrReplace(media_path, " ", "%20") } @@ -404,7 +273,7 @@ Potplayer2ObsidianFragment(HotkeyName) { if (PressHotkeyCount == 1) { ; 第一次按下快捷键,记录时间 - fragment_start_time := GetMediaTime() + fragment_time_start := GetMediaTime() ; 通知用户 ToolTip("已经记录起点的时间!请再次按下快捷键,记录终点的时间。按Esc取消") SetTimer () => ToolTip(), -2000 @@ -417,35 +286,35 @@ Potplayer2ObsidianFragment(HotkeyName) { Hotkey("Escape Up", "off") } } else if (PressHotkeyCount == 2) { - Assert(fragment_start_time == "", "未设置起点时间,无法生成该片段的链接!") + Assert(fragment_time_start == "", "未设置起点时间,无法生成该片段的链接!") ; 重置计数器 PressHotkeyCount := 0 Hotkey("Escape Up", "off") ; 第二次按下快捷键,记录时间 - fragment_end_time := GetMediaTime() + fragment_time_end := GetMediaTime() ; 如果终点时间小于起点时间,就交换两个时间 - if (TimestampToMilliSecond(fragment_end_time) < TimestampToMilliSecond(fragment_start_time)) { - temp := fragment_start_time - fragment_start_time := fragment_end_time - fragment_end_time := temp + if (TimestampToMilliSecond(fragment_time_end) < TimestampToMilliSecond(fragment_time_start)) { + temp := fragment_time_start + fragment_time_start := fragment_time_end + fragment_time_end := temp ; 释放内存 temp := "" } media_path := GetMediaPath() - if fragment_start_time == fragment_end_time { - fragment_time := fragment_start_time + if fragment_time_start == fragment_time_end { + fragment_time := fragment_time_start } else if HotkeyName == app_config.HotkeyAbFragment " Up" { - fragment_time := fragment_start_time "-" fragment_end_time + fragment_time := fragment_time_start "-" fragment_time_end } else if HotkeyName == app_config.HotkeyAbCirculation " Up" { - fragment_time := fragment_start_time "∞" fragment_end_time + fragment_time := fragment_time_start "∞" fragment_time_end } ; 生成片段链接 - markdown_link := RenderBacklinkTemplate(app_config.MarkdownTemplate, MediaData(media_path, fragment_time, GetMediaSubtitle())) + markdown_link := RenderTemplate(app_config.MarkdownTemplate, MediaData(media_path, fragment_time, "")) PauseMedia() ; 发送到笔记软件