├── .gitignore
├── LICENSE
├── README.md
├── assets
├── Untitled-2024-06-13-0228.png
├── ba221f08ea6fa4f224889186c12606d7.png
├── image-20231214154748461.png
├── image-20231214195533699.png
├── image-20231216154341755.png
├── image-20231216234628300.png
├── image-20231216235029002.png
├── image-20231229004156479.png
├── image-20231229004839082.png
├── image-20231229163509526.png
├── image-20240126130251886.png
├── image-20240210193023137.png
└── image-20240217172403274.png
├── doc
└── README_EN.md
├── lib
├── BootUp.ahk
├── CharsetDetect.ahk
├── JSON.ahk
├── MyTool.ahk
├── NetRequest.ahk
├── PotplayerControl.ahk
├── PotplayerFragment.ahk
├── Render.ahk
├── TemplateParser.ahk
├── TimeTool.ahk
├── entity
│ ├── Config.ahk
│ └── MediaData.ahk
├── gui
│ ├── Gui.ahk
│ ├── GuiControl.ahk
│ ├── GuiExample.ahk
│ └── i18n
│ │ ├── I18n.ahk
│ │ ├── LCID.ahk
│ │ ├── en-US.ini
│ │ └── zh-CN.ini
├── ico.ico
├── icon.png
├── note2potplayer
│ ├── RegisterUrlProtocol.ahk
│ ├── note2potplayer.ahk
│ └── sqlite
│ │ ├── Class_SQLiteDB.ahk
│ │ └── SqliteControl.ahk
├── socket
│ ├── Socket.ahk
│ ├── SocketService.ahk
│ └── example
│ │ ├── _socket.ahk
│ │ └── _socket_example.ahk
├── sqlite
│ ├── Class_SQLiteDB.ahk
│ ├── SQLite3.dll
│ ├── Sample_Class_SQLiteDB.ahk
│ └── SqliteControl.ahk
├── srt.ahk
└── word
│ └── word.ahk
└── markdown2potplayer.ahk
/.gitignore:
--------------------------------------------------------------------------------
1 | config.db
2 | markdown2potplayer.exe
3 | note2potplayer.exe
4 | word.exe
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 livelycode
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [README_EN](./doc/README_EN.md)
2 |
3 | # 使用说明
4 |
5 | 效果:
6 |
7 | 
8 |
9 | ## 1 前置准备
10 |
11 | 1. 运行脚本文件:`markdown2potplayer`
12 | 2. 双击右下角的托盘图标
13 |
14 | 
15 |
16 | **修改1**:修改Potplayer的主程序路径,为你本机的路径
17 |
18 | **修改2**:指定笔记软件的软件名称
19 |
20 | - `说明`:只会按照从上至下的顺序,给1个笔记软件粘贴回链
21 | - 例如:此处同时配置了obsidian和typora
22 | - 情况1:在obsidian和typora同时打开的情况下,只会粘贴到obsidian中
23 | - 情况2:只有typora打开,则粘贴到typora中
24 |
25 |
26 | ## 2 使用
27 |
28 | 1. 打开`markdown2potplayer`
29 | 1. 打开obsidian
30 | 1. 打开potplayer
31 |
32 | 3. 在笔记软件、或者potplayer`窗口激活`的状态下,按热键Alt+G(默认),即可自动粘贴**视频的回链**到obsidian中
33 | 4. 在笔记软件、或者potplayer`窗口激活`的状态下,按热键Ctrl+Alt+G(默认),即可自动粘贴**图片+视频的回链**到obsidian中
34 |
35 | # 高级设置
36 |
37 | ## 关于notion
38 |
39 | 1. notion是运行在浏览器中,目前浏览器众多
40 | 2. 暂时支持如下
41 | 1. 微软Edge:msedge.exe
42 | 2. 谷歌:chrome.exe
43 | 3. 360极速版:360chrome.exe
44 | 4. 火狐:firefox.exe
45 |
46 | 3. **请 鼠标左键 点击notion中的链接,不要使用新建标签页打开 例如:Ctrl + 鼠标左键、鼠标中键**
47 |
48 |
49 |
50 | 
51 |
52 |
53 |
54 | ## 模板的修改
55 |
56 | 
57 |
58 | 此处修改的是**粘贴数据的模板**,一共有`6`个模板项。
59 |
60 |
61 |
62 | **注意**:这5项,不是哪个位置都可以用
63 |
64 | - 字幕模板:只能用`{subtitle}`
65 | - 回链的名称:只能用`{name}`、`{time}`、`{subtitleTemplate}`
66 | - 回链模板:只能用`{title}`、`{subtitleTemplate}`
67 | - 视频回链模板:只能用`{image}`、`{title}`、`{subtitleTemplate}`
68 |
69 |
70 |
71 | 逐一说明:
72 |
73 | - `{name}`:代表视频的文件名称,也就是`[`视频**名称**`]`
74 | - `{time}`:代表当前播放视频的时间,也就是`[`视频**时间**`]`
75 | - `{title}`:**代表整个markdown格式的链接**,例如`[百度](https://www.baidu.com)`也就是说,此处是markdown格式的potplayer回链
76 | - `{image}`:代表**图片粘贴的位置**
77 | - `{subtitle}`: 代表的是当前在potplayer播放的视频中,能够复制的字幕
78 | - `{subtitleTemplate}`: 代表的是 字幕模板 ,**如果当前播放的视频中没有字幕时,字幕模板将不会有数据产生**,也就是说没有字幕,则`{subtitleTemplate}`消失
79 |
80 | 关于字幕模板:
81 | - `{time}`: 代表 potplayer中的当前时间戳
82 | - `{subtitle}`: 代表 potplayer播放器中的当前字幕
83 | - `{subtitleOrigin}`: 代表 .str字幕文件中的 当前时间戳的字幕
84 | - `{subtitleTimeRange}`: 代表 .str字幕文件中的 开始和结束时间。例如: 01:02-03:04
85 | - `{subtitleTimeStart}`: 代表 .str字幕文件中的 开始时间。例如: 01:02
86 | - `{subtitleTimeEnd}`: 代表 .str字幕文件中的 结束时间。例如: 03:04
87 |
88 | **注意**: `srt转md`功能中的数据样式,受到 字幕模板 中的样式进行控制
89 |
90 |
91 | ### 示例1
92 |
93 | 我想要`Alt+G`是这个效果
94 |
95 | 
96 |
97 | 此处应该这么填
98 |
99 | 1. 先确定**回链中的`[]`内的名称**
100 |
101 | ```
102 | {name} | {time}
103 | ```
104 |
105 | 2. 再确定**整个模板的数据**
106 |
107 | ````
108 | ```Video
109 | title: {title}
110 | ```
111 | ````
112 |
113 | 最终效果
114 |
115 | 
116 |
117 | ### 示例2
118 |
119 | 我想要`Ctrl+Alt+G`是这个效果
120 |
121 | 
122 |
123 | 视频回链模板此处应该这么填
124 |
125 | ````ini
126 | ```video
127 | title:{title}
128 | image:{image}
129 | ```
130 | ````
131 |
132 |
133 |
134 | ### 示例3
135 |
136 | **思源笔记的换行**请使用html的`
`标签,参考:[#16](https://github.com/livelycode36/markdown2potplayer/issues/16)
137 |
138 | ```html
139 | {title}
140 | {image}
141 |
142 | ```
143 |
144 | ### 示例4
145 | 
146 |
147 |
148 | ## 播放B站视频
149 |
150 | 1. Potplayer需要提前安装插件:[chen310/BilibiliPotPlayer](https://github.com/chen310/BilibiliPotPlayer)
151 |
152 | 2. 按照插件的使用文档,在potplayer中播放视频
153 | 3. 使用快捷键打时间戳即可
154 |
155 |
156 |
157 | ## 字幕导航
158 |
159 | 需要在视频的同目录下, 同名的srt字幕文件, 例如视频名称为test.mp4 字幕文件为 test.srt
160 |
161 | - 【单次】使用srt字幕快速定位当前时间的上一句、当前句、下一句
162 | - 【循环】使用srt字幕快速定位当前时间的上一句、当前句、下一句
163 |
164 | 实际上potplayer也有这个功能
165 |
166 | - Home键: 上一对白
167 | - End键: 下一对白
168 | - Ctrl+Home: 当前字幕起点
169 |
170 |
171 | ## AB片段
172 |
173 | **使用**:
174 |
175 | 1. 首次,按快捷键记录起点
176 |
177 | 2. 再次,按快捷键记录终点,并生成回链,插入到笔记软件中
178 |
179 |
180 |
181 | **注意**:
182 |
183 | 1. 当起点**大于**终点时,例如:起点05:00,终点01:00,则`互换起点终点`,起点01:00,终点05:00
184 | 2. 当按下起点的快捷键,**想取消**,按`Esc`即可
185 |
186 |
187 |
188 | ### AB片段
189 |
190 | 播放**单次**,在起点播放,在终点暂停
191 |
192 | 播放之后,不想在终点暂停,按`Esc`即可取消终点暂停
193 |
194 |
195 |
196 | ### AB循环
197 |
198 | 使用Potplayer自带的"AB区段循环"实现,默认**无限播放**
199 |
200 | **关闭AB区段循环**:Potplayer**默认快捷键`\`**
201 |
202 |
203 |
204 | ## 视频文件的后缀名
205 |
206 | 控制名称中是否包含文件名的后缀
207 |
208 | 
209 |
210 |
211 |
212 | ## 地址是否编码
213 |
214 | 控制视频地址,是否使用编码
215 |
216 | **关闭编码的效果**
217 |
218 | 
219 |
220 | 注意
221 |
222 | - 目前发现的bug:
223 | 1. 全系urlencode的bug:如果路径中存在`\[`、`\!`会让,在【ob的预览模式】下(回链会被ob自动urlencode),`\[`中的`\`消失变为,`[`;例如:`G:\BaiduSyncdisk\123\[456]789.mp4` 在bug下变为:`G:\BaiduSyncdisk\123[456]789.mp4` <= `\[`丢失了`\`,所以即使关闭编码也会强制在`\[`出现在路径中,将`\[`中的`\`进行编码。如果不想被编码,请不要这样给视频文件命名 或 使用`-`、`_`等替代
224 | 2. 关闭编码之后,假如视频的路径中有`空格`,在obsidian的预览模式,回链`不会渲染为链接`,所以即使关闭编码也会强制将空格进行编码。如果不想空格也被编码,可以去掉文件中的空格 或 使用`-`、`_`等替代空格
225 | - 可能还有其他符号也有类似的问题,但暂未发现
226 |
227 |
228 | ## 为什么有时候跳转的时间不够准确?
229 | 因为在跳转时间之后, potplayer会根据关键帧, 对时间进行修正,所以会有误差。
230 | 在Potplayer的设置中关闭此项,即可解决。
231 |
232 | 
233 |
234 |
235 | ## 自定义跳转协议
236 |
237 | 适合自定义协议的人使用【谨慎】
238 |
239 | 修改的是此处
240 |
241 | 
242 |
243 |
244 |
245 | ## 多国语言
246 |
247 | 1. 在这里有语言的代号:[LCID.ahk](./lib/gui/i18n/LCID.ahk)
248 | 2. 这是示例
249 | 1. [en-US.ini](./lib/gui/i18n/en-US.ini)
250 | 2. [zh-CN.ini](./lib/gui/i18n/zh-CN.ini)
251 |
252 | - **注意:ini文件请使用使用系统的默认 ANSI 编码!**
253 | - 参考:[IniRead| AutoHotkey v2](https://www.autohotkey.com/docs/v2/lib/IniRead.htm)
254 |
255 |
256 |
257 | # 开发
258 |
259 | 1. 克隆仓库
260 | 2. 下载安装:[AutoHotkey](https://www.autohotkey.com/)的v2版本
261 | 3. 打开AutoHotkey Dash,点击`Compile`按照提示安装`Ahk2Exe`
262 | 4. 使用`Ahk2Exe`编译,如下文件
263 | 1. 主程序:`markdown2potplayer.ahk`
264 | 2. 控制potplayer:`\lib\note2potplayer\note2potplayer.ahk`
265 | 3. word文档链接的形式:`\lib\word\word.ahk`
266 |
267 |
268 |
269 | # 鸣谢
270 |
271 | 感谢
272 |
273 | - [金](https://github.com/fireflysss)
274 | - [YIRU69](https://github.com/YIRU69)
275 | - 蚕子
276 |
277 | 给予的帮助与建议!
--------------------------------------------------------------------------------
/assets/Untitled-2024-06-13-0228.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/Untitled-2024-06-13-0228.png
--------------------------------------------------------------------------------
/assets/ba221f08ea6fa4f224889186c12606d7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/ba221f08ea6fa4f224889186c12606d7.png
--------------------------------------------------------------------------------
/assets/image-20231214154748461.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20231214154748461.png
--------------------------------------------------------------------------------
/assets/image-20231214195533699.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20231214195533699.png
--------------------------------------------------------------------------------
/assets/image-20231216154341755.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20231216154341755.png
--------------------------------------------------------------------------------
/assets/image-20231216234628300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20231216234628300.png
--------------------------------------------------------------------------------
/assets/image-20231216235029002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20231216235029002.png
--------------------------------------------------------------------------------
/assets/image-20231229004156479.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20231229004156479.png
--------------------------------------------------------------------------------
/assets/image-20231229004839082.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20231229004839082.png
--------------------------------------------------------------------------------
/assets/image-20231229163509526.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20231229163509526.png
--------------------------------------------------------------------------------
/assets/image-20240126130251886.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20240126130251886.png
--------------------------------------------------------------------------------
/assets/image-20240210193023137.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20240210193023137.png
--------------------------------------------------------------------------------
/assets/image-20240217172403274.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livelycode36/markdown2potplayer/34dba23ba397ac893a3e0372dfa53fd8805eef4b/assets/image-20240217172403274.png
--------------------------------------------------------------------------------
/doc/README_EN.md:
--------------------------------------------------------------------------------
1 | # Usage Instructions
2 |
3 | ## 1. Preparation
4 |
5 | 1. Run the script file: `markdown2potplayer`
6 | 2. Double-click the tray icon in the bottom right corner 
7 |
8 | 
9 |
10 | **Modification 1**: Update the path to Potplayer's main program(potplayer 播放器的路径) to match your local path.
11 |
12 | **Modification 2**: Specify the name of your note-taking software(笔记软件的程序名称).
13 |
14 | - `Note`: The backlink will only be pasted into one note-taking software, following the order from top to bottom.
15 | - For example, if both Obsidian and Typora are configured:
16 | - Case 1: If both Obsidian and Typora are open, the backlink will only be pasted into Obsidian.
17 | - Case 2: If only Typora is open, the backlink will be pasted into Typora.
18 |
19 | ## 2. Usage
20 |
21 | 1. Open `markdown2potplayer`.
22 | 2. Open Obsidian.
23 | 3. Open Potplayer.
24 |
25 | 4. When the note-taking software or Potplayer window is active, press the hotkey Alt+G (default) to automatically paste the **video backlink** (视频回链) into Obsidian.
26 | 5. When the note-taking software or Potplayer window is active, press the hotkey Ctrl+Alt+G (default) to automatically paste the **image + video backlink** into Obsidian.
27 |
28 | # Advanced Settings
29 |
30 | ## About Notion
31 |
32 | 1. Notion runs in a browser, and there are many browsers available.
33 | 2. Currently supported browsers include:
34 | - Microsoft Edge: `msedge.exe`
35 | - Google Chrome: `chrome.exe`
36 | - 360 Speed Browser: `360chrome.exe`
37 | - Firefox: `firefox.exe`
38 |
39 | 3. **Please click on the link in Notion with the left mouse button, do not use the open in a new tab option, such as Ctrl + left mouse button or middle mouse button.**
40 |
41 | 
42 |
43 | ## Modifying Templates
44 |
45 | 
46 |
47 | There are `5` template items for **pasting templates**.
48 |
49 | **Note**: Not all positions can use these four items.
50 |
51 | - Subtitle Template: Only `{subtitle}` can be used.
52 | - Backlink Name: Only `{name}` and `{time}` can be used.
53 | - Backlink Template: Only `{title}` can be used.
54 | - Video Backlink Template: Only `{image}` and `{title}` can be used.
55 |
56 | Explanation:
57 |
58 | - `{name}`: Represents the video file name, i.e., `[Video **Name**]`.
59 | - `{time}`: Represents the current playback time of the video, i.e., `[Video **Time**]`.
60 | - `{title}`: **Represents the entire Markdown format link**, e.g., `[google](https://www.google.com)`. This means that this is the Markdown format backlink for Potplayer.
61 | - `{image}`: Represents the **position for pasting the image**.
62 | - `{subtitle}`: represents the subtitles that can be copied from the video currently being played in PotPlayer.
63 | - `{subtitleTemplate}`: represents the subtitle template. **If there are no subtitles in the currently playing video, no data will be generated for the subtitle template**, meaning if there are no subtitles, `{subtitleTemplate}` will disappear.
64 |
65 | Subtitle Template Variables:
66 | - `{time}`: Current timestamp in PotPlayer
67 | - `{subtitle}`: Currently displayed subtitle in PotPlayer
68 | - `{subtitleOrigin}`: Original subtitle text at current timestamp from .srt file
69 | - `{subtitleTimeRange}`: Time range in .srt file (format: HH:MM:SS-HH:MM:SS)
70 | - `{subtitleTimeStart}`: Start time in .srt file (e.g. 00:01:02)
71 | - `{subtitleTimeEnd}`: End time in .srt file (e.g. 00:03:04)
72 |
73 | **Note**: The data styling in `SRT-to-Markdown` conversion is controlled by these template variables.
74 |
75 |
76 | ### Example 1
77 |
78 | I want the effect of `Alt+G` to be this:
79 |
80 | 
81 |
82 | This should be filled in as follows:
83 |
84 | 1. First, determine the **name inside the `[]` of the backlink**:
85 |
86 | ```
87 | {name} | {time}
88 | ```
89 |
90 | 2. Then, determine the **data for the entire template**:
91 |
92 | ````
93 | ```Video
94 | title: {title}
95 | ```
96 | ````
97 |
98 | Final effect:
99 |
100 | 
101 |
102 | ### Example 2
103 |
104 | I want the effect of `Ctrl+Alt+G` to be this:
105 |
106 | 
107 |
108 | The video backlink template should be filled in as follows:
109 |
110 | ````ini
111 | ```video
112 | title:{title}
113 | image:{image}
114 | ```
115 | ````
116 |
117 | ## Playing Bilibili Videos
118 |
119 | 1. Potplayer needs to have the plugin installed: [chen310/BilibiliPotPlayer](https://github.com/chen310/BilibiliPotPlayer).
120 | 2. Follow the plugin's usage documentation to play videos in Potplayer.
121 | 3. Use the hotkey to mark timestamps.
122 |
123 |
124 | ## Subtitle Navigation
125 |
126 | Requirements:
127 | - An SRT file with identical name as the video must exist in the same directory
128 | (e.g. `test.mp4` requires `test.srt`)
129 |
130 | Navigation Modes:
131 | - **Single-play**: Jump to previous/current/next subtitle line based on current timestamp
132 | - **Loop-mode**: Continuously cycle through previous/current/next subtitle lines
133 |
134 | Native PotPlayer Shortcuts:
135 | - `Home`: Previous dialogue line
136 | - `End`: Next dialogue line
137 | - `Ctrl+Home`: Jump to start timestamp of current subtitle
138 |
139 |
140 | ## AB Segments
141 |
142 | **Usage**:
143 |
144 | 1. Press the hotkey once to record the start point.
145 | 2. Press the hotkey again to record the end point and generate the backlink, which is then inserted into the note-taking software.
146 |
147 | **Note
148 |
149 | **:
150 |
151 | 1. When the start point is **greater than** the end point, e.g., start point 05:00, end point 01:00, the start and end points will be **swapped**, so the start point becomes 01:00 and the end point 05:00.
152 | 2. If you press the hotkey for the start point and **want to cancel**, press `Esc`.
153 |
154 | ### AB Segment
155 |
156 | Play **once**, start playing at the start point, and pause at the end point.
157 |
158 | After playing, if you don't want to pause at the end point, press `Esc` to cancel the end point pause.
159 |
160 | ### AB Loop
161 |
162 | Use Potplayer's built-in "AB Section Loop" for **infinite playback**.
163 |
164 | **To close the AB section loop**: Use Potplayer's **default hotkey `\`**.
165 |
166 | ## Video File Extensions
167 |
168 | Control whether the file name includes the file extension.
169 |
170 | 
171 |
172 | ## URL Encoding
173 |
174 | Control whether the video URL is encoded.
175 |
176 | **Effect of disabling encoding**:
177 |
178 | 
179 |
180 | Note:
181 |
182 | - Known bugs:
183 | - Full URL encoding bug: If the path contains `\[` or `\!`, in **Obsidian's preview mode** (where the backlink is automatically URL-encoded by Obsidian), `\[` will lose the `\` and become `[`. For example: `G:\BaiduSyncdisk\123\[456]789.mp4` becomes `G:\BaiduSyncdisk\123[456]789.mp4` under the bug, so even if encoding is disabled, `\[` in the path will be forcibly encoded. To avoid this, please do not name your video files this way, or use `-`, `_`, etc., as alternatives.
184 | - After disabling encoding, if the video path contains `spaces`, the backlink will **not render as a link** in Obsidian's preview mode, so spaces will also be forcibly encoded. To avoid this, you can remove spaces from the file name or use `-`, `_`, etc., as alternatives.
185 | - There may be other symbols with similar issues, but none have been discovered so far.
186 |
187 | ## Why isn't the seek time always precise?
188 | This happens because Potplayer adjusts the timing based on keyframes after seeking, which may cause slight inaccuracies.
189 | You can fix this by disabling this feature in Potplayer's settings.
190 |
191 | 
192 |
193 |
194 | ## Custom Protocol
195 |
196 | For those who use custom protocols (use with caution):
197 |
198 | Modify here:
199 |
200 | 
201 |
202 | ## Multilingual Support
203 |
204 | 1. Language codes can be found here: [LCID.ahk](../lib/gui/i18n/LCID.ahk).
205 | 2. Examples:
206 | - [en-US.ini](../lib/gui/i18n/en-US.ini)
207 | - [zh-CN.ini](../lib/gui/i18n/zh-CN.ini)
208 |
209 | - **Note: Please use the system's default ANSI encoding for the ini files!**
210 | - Reference: [IniRead | AutoHotkey v2](https://www.autohotkey.com/docs/v2/lib/IniRead.htm)
211 |
212 |
213 |
214 | # Development
215 |
216 | 1. Clone the repository.
217 | 2. Download and install the v2 version of [AutoHotkey](https://www.autohotkey.com/).
218 | 3. Open AutoHotkey Dash, click on `Compile`, and follow the prompts to install `Ahk2Exe`.
219 | 4. Compile using `Ahk2Exe` with the following files:
220 | 1. Main program: `markdown2potplayer.ahk`
221 | 2. PotPlayer control: `\lib\note2potplayer\note2potplayer.ahk`
222 | 3. Word document link format: `\lib\word\word.ahk`
223 |
224 |
225 |
226 | # Acknowledgments
227 |
228 | Special thanks to:
229 |
230 | - [Jin](https://github.com/fireflysss)
231 | - [YIRU69](https://github.com/YIRU69)
232 | - Silkworm
233 |
234 | for their help and suggestions!
--------------------------------------------------------------------------------
/lib/BootUp.ahk:
--------------------------------------------------------------------------------
1 | #Requires AutoHotkey v2.0
2 |
3 | key := "markdown2potplayer"
4 | value := "`"" A_ScriptDir "\" key ".exe`""
5 |
6 | set_boot_up(){
7 | RegWrite value, "REG_SZ", "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", key
8 | }
9 |
10 | get_boot_up(){
11 | try
12 | regValue := RegRead("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", key)
13 | catch as OSError ; if the key doesn't exist
14 | return false
15 |
16 | if (regValue == value)
17 | return true
18 | else
19 | return false
20 | }
21 |
22 | remove_boot_up(){
23 | try
24 | RegDelete("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", key)
25 | catch as OSError ; if the key doesn't exist
26 | return false
27 | }
28 |
29 | adaptive_bootup(){
30 | if get_boot_up(){
31 | remove_boot_up()
32 | MsgBox "Startup launch: OFF"
33 | }
34 | else{
35 | set_boot_up()
36 | MsgBox "Startup launch: ON"
37 | }
38 | }
--------------------------------------------------------------------------------
/lib/CharsetDetect.ahk:
--------------------------------------------------------------------------------
1 | class TextEncodingDetect {
2 | UTF8Bom := [0xEF, 0xBB, 0xBF]
3 | UTF16LeBom := [0xFF, 0xFE]
4 | UTF16BeBom := [0xFE, 0xFF]
5 | UTF32LeBom := [0xFF, 0xFE, 0x00, 0x00]
6 |
7 | DetectEncoding(fileName) {
8 | if !FileExist(fileName) {
9 | MsgBox("File not found")
10 | return ""
11 | }
12 |
13 | buffer := FileRead(fileName, "RAW")
14 | size := buffer.Size
15 |
16 | encodingType := this.DetectWithBom(buffer)
17 | if (encodingType == "Utf8Bom") {
18 | encodingType := "UTF-8"
19 | } else if (encodingType == "UnicodeBom") {
20 | encodingType := "UTF-16"
21 | }
22 |
23 | if encodingType != "None" {
24 | return encodingType
25 | }
26 |
27 | encodingType := this.DetectWithoutBom(buffer, size)
28 | if (encodingType == "GBK" ||
29 | encodingType == "Ansi") {
30 | ; CP0 是 系统默认编码
31 | encodingType := "CP0"
32 | }
33 |
34 | return encodingType != "None" ? encodingType : "None"
35 | }
36 |
37 | DetectWithBom(buffer) {
38 | 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]) {
39 | return "Utf8Bom"
40 | }
41 |
42 | if (buffer.Size >= 2 && this.ByteAt(buffer, 0) = this.UTF16LeBom[1] && this.ByteAt(buffer, 1) = this.UTF16LeBom[2]) {
43 | return "UnicodeBom"
44 | }
45 |
46 | if (buffer.Size >= 2 && this.ByteAt(buffer, 0) = this.UTF16BeBom[1] && this.ByteAt(buffer, 1) = this.UTF16BeBom[2]) {
47 | if (buffer.Size >= 4 && this.ByteAt(buffer, 2) = this.UTF32LeBom[3] && this.ByteAt(buffer, 3) = this.UTF32LeBom[4]) {
48 | return "Utf32Bom"
49 | }
50 | return "BigEndianUnicodeBom"
51 | }
52 |
53 | return "None"
54 | }
55 |
56 | DetectWithoutBom(buffer, size) {
57 | ; Check for UTF-8 encoding
58 | encoding := this.CheckUtf8(buffer, size)
59 | if encoding != "None" {
60 | return encoding
61 | }
62 |
63 | ; Check for ANSI encoding
64 | if !this.ContainsZero(buffer, size) {
65 | return this.CheckChinese(buffer, size) ? "GBK" : "Ansi"
66 | }
67 |
68 | return "None"
69 | }
70 |
71 | CheckUtf8(buffer, size) {
72 | pos := 0
73 | while pos < size {
74 | ch := this.ByteAt(buffer, pos)
75 | pos++
76 |
77 | if ch < 0x80 {
78 | continue
79 | }
80 |
81 | if ch >= 0xC2 && ch <= 0xDF {
82 | if !this.IsContinuationByte(this.ByteAt(buffer, pos)) {
83 | return "None"
84 | }
85 | pos++
86 | continue
87 | }
88 |
89 | if ch >= 0xE0 && ch <= 0xEF {
90 | if !this.IsContinuationByte(this.ByteAt(buffer, pos)) || !this.IsContinuationByte(this.ByteAt(buffer, pos + 1)) {
91 | return "None"
92 | }
93 | pos += 2
94 | continue
95 | }
96 |
97 | if ch >= 0xF0 && ch <= 0xF4 {
98 | if !this.IsContinuationByte(this.ByteAt(buffer, pos)) || !this.IsContinuationByte(this.ByteAt(buffer, pos + 1)) || !this.IsContinuationByte(this.ByteAt(buffer, pos + 2)) {
99 | return "None"
100 | }
101 | pos += 3
102 | continue
103 | }
104 |
105 | return "None"
106 | }
107 | ; return "Utf8Nobom"
108 | return "UTF-8"
109 | }
110 |
111 | IsContinuationByte(byte) {
112 | return byte >= 0x80 && byte <= 0xBF
113 | }
114 |
115 | ContainsZero(buffer, size) {
116 | pos := 0
117 | while pos < size {
118 | if this.ByteAt(buffer, pos) = 0 {
119 | return true
120 | }
121 | pos++
122 | }
123 | return false
124 | }
125 |
126 | CheckChinese(buffer, size) {
127 | pos := 0
128 | while pos < size - 1 {
129 | ch1 := this.ByteAt(buffer, pos)
130 | ch2 := this.ByteAt(buffer, pos + 1)
131 | pos += 2
132 |
133 | if (ch1 >= 176 && ch1 <= 247 && ch2 >= 160 && ch2 <= 254) ; GB2312
134 | || (ch1 >= 129 && ch1 <= 254 && ch2 >= 64 && ch2 <= 254) ; GBK
135 | || (ch1 >= 129 && ((ch2 >= 64 && ch2 <= 126) || (ch2 >= 161 && ch2 <= 254))) { ; Big5
136 | return true
137 | }
138 | }
139 | return false
140 | }
141 |
142 | ByteAt(buffer, index) {
143 | return NumGet(buffer, index, "UChar")
144 | }
145 | }
146 |
147 | ; 使用示例:
148 | ; detect := TextEncodingDetect()
149 | ; encoding := detect.DetectEncoding("C:\Users\Thunder\Downloads\Compressed\02 音名与钢琴键盘.srt")
150 | ; MsgBox "Detected Encoding: " encoding
151 |
--------------------------------------------------------------------------------
/lib/JSON.ahk:
--------------------------------------------------------------------------------
1 | #Requires AutoHotkey v2.0
2 |
3 | ; From: https: // github.com / dcazrael / autohotkey_libraries
4 | class JSON {
5 | ; Example ===================================================================================
6 | ; ===========================================================================================
7 |
8 | ; Msgbox "The idea here is to create several nested arrays, save to text with jxon_dump(), and then reload the array with jxon_load(). The resulting array should be the same.`r`n`r`nThis is what this example shows."
9 | ; a := Map(), b := Map(), c := Map(), d := Map(), e := Map(), f := Map() ; Object() is more technically correct than {} but both will work.
10 |
11 | ; d["g"] := 1, d["h"] := 2, d["i"] := ["purple","pink","pippy red"]
12 | ; e["g"] := 1, e["h"] := 2, e["i"] := Map("1","test1","2","test2","3","test3")
13 | ; f["g"] := 1, f["h"] := 2, f["i"] := [1,2,Map("a",1.0009,"b",2.0003,"c",3.0001)]
14 |
15 | ; a["test1"] := "test11", a["d"] := d
16 | ; b["test3"] := "test33", b["e"] := e
17 | ; c["test5"] := "test55", c["f"] := f
18 |
19 | ; myObj := Map()
20 | ; myObj["a"] := a, myObj["b"] := b, myObj["c"] := c, myObj["test7"] := "test77", myObj["test8"] := "test88"
21 |
22 | ; g := ["blue","green","red"], myObj["h"] := g ; add linear array for testing
23 |
24 | ; q := Chr(34)
25 | ; textData2 := Jxon_dump(myObj,4) ; ===> convert array to JSON
26 | ; msgbox "JSON output text:`r`n===========================================`r`n(Should match second output.)`r`n`r`n" textData2
27 |
28 | ; newObj := Jxon_load(&textData2) ; ===> convert json back to array
29 |
30 | ; textData3 := Jxon_dump(newObj,4) ; ===> break down array into 2D layout again, should be identical
31 | ; msgbox "Second output text:`r`n===========================================`r`n(should be identical to first output)`r`n`r`n" textData3
32 |
33 | ; msgbox "textData2 = textData3: " ((textData2=textData3) ? "true" : "false")
34 |
35 | ; ===========================================================================================
36 | ; End Example ; =============================================================================
37 | ; ===========================================================================================
38 |
39 | ; originally posted by user coco on AutoHotkey.com
40 | ; https://github.com/cocobelgica/AutoHotkey-JSON
41 |
42 | static Load(&src, args*) {
43 | key := "", is_key := false
44 | stack := [ tree := [] ]
45 | next := '"{[01234567890-tfn'
46 | pos := 0
47 |
48 | while ( (ch := SubStr(src, ++pos, 1)) != "" ) {
49 | if InStr(" `t`n`r", ch)
50 | continue
51 | if !InStr(next, ch, true) {
52 | testArr := StrSplit(SubStr(src, 1, pos), "`n")
53 |
54 | ln := testArr.Length
55 | col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1))
56 |
57 | msg := Format("{}: line {} col {} (char {})"
58 | , (next == "") ? ["Extra data", ch := SubStr(src, pos)][1]
59 | : (next == "'") ? "Unterminated string starting at"
60 | : (next == "\") ? "Invalid \escape"
61 | : (next == ":") ? "Expecting ':' delimiter"
62 | : (next == '"') ? "Expecting object key enclosed in double quotes"
63 | : (next == '"}') ? "Expecting object key enclosed in double quotes or object closing '}'"
64 | : (next == ",}") ? "Expecting ',' delimiter or object closing '}'"
65 | : (next == ",]") ? "Expecting ',' delimiter or array closing ']'"
66 | : [ "Expecting JSON value(string, number, [true, false, null], object or array)"
67 | , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1]
68 | , ln, col, pos)
69 |
70 | throw Error(msg, -1, ch)
71 | }
72 |
73 | obj := stack[1]
74 | is_array := (obj is Array)
75 |
76 | if i := InStr("{[", ch) { ; start new object / map?
77 | val := (i = 1) ? Map() : Array() ; ahk v2
78 |
79 | is_array ? obj.Push(val) : obj[key] := val
80 | stack.InsertAt(1,val)
81 |
82 | next := '"' ((is_key := (ch == "{")) ? "}" : "{[]0123456789-tfn")
83 | } else if InStr("}]", ch) {
84 | stack.RemoveAt(1)
85 | next := (stack[1]==tree) ? "" : (stack[1] is Array) ? ",]" : ",}"
86 | } else if InStr(",:", ch) {
87 | is_key := (!is_array && ch == ",")
88 | next := is_key ? '"' : '"{[0123456789-tfn'
89 | } else { ; string | number | true | false | null
90 | if (ch == '"') { ; string
91 | i := pos
92 | while i := InStr(src, '"',, i+1) {
93 | val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C")
94 | if (SubStr(val, -1) != "\")
95 | break
96 | }
97 | if !i ? (pos--, next := "'") : 0
98 | continue
99 |
100 | pos := i ; update pos
101 |
102 | val := StrReplace(val, "\/", "/")
103 | val := StrReplace(val, '\"', '"')
104 | , val := StrReplace(val, "\b", "`b")
105 | , val := StrReplace(val, "\f", "`f")
106 | , val := StrReplace(val, "\n", "`n")
107 | , val := StrReplace(val, "\r", "`r")
108 | , val := StrReplace(val, "\t", "`t")
109 |
110 | i := 0
111 | while i := InStr(val, "\",, i+1) {
112 | if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0
113 | continue 2
114 |
115 | xxxx := Abs("0x" . SubStr(val, i+2, 4)) ; \uXXXX - JSON unicode escape sequence
116 | if (xxxx < 0x100)
117 | val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6)
118 | }
119 |
120 | if is_key {
121 | key := val, next := ":"
122 | continue
123 | }
124 | } else { ; number | true | false | null
125 | val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos)
126 |
127 | if IsInteger(val)
128 | val += 0
129 | else if IsFloat(val)
130 | val += 0
131 | else if (val == "true" || val == "false")
132 | val := (val == "true")
133 | else if (val == "null")
134 | val := ""
135 | else if is_key {
136 | pos--, next := "#"
137 | continue
138 | }
139 |
140 | pos += i-1
141 | }
142 |
143 | is_array ? obj.Push(val) : obj[key] := val
144 | next := obj == tree ? "" : is_array ? ",]" : ",}"
145 | }
146 | }
147 |
148 | return tree[1]
149 | }
150 |
151 | static Dump(obj, indent:="", lvl:=1) {
152 | if IsObject(obj) {
153 | If !(obj is Array || obj is Map || obj is String || obj is Number)
154 | throw Error("Object type not supported.", -1, Format("