add dedup script

This commit is contained in:
polv committed 2026-02-03 12:48:21 +07:00
1 parent 2b47af46de
commit 62c6775e67
7 files changed
+986 -35

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
{
"editor.tabSize": 2,
"json.validate.enable": false,
"[javascript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.maxTokenizationLineLength": 2500,
},
"[vue]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
},
}
@@ -1,5 +1,6 @@
--- ---
interval: 2 interval: 2
dedup: true
video: '/みんなうっかり間違える!?中国語 様態補語と程度補語の文法の注意点とは [m15ta7yPgjw].mp4' video: '/みんなうっかり間違える!?中国語 様態補語と程度補語の文法の注意点とは [m15ta7yPgjw].mp4'
--- ---
@@ -16,7 +17,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>0:14</summary>
![0:18](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/0_18.png) ![0:18](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/0_18.png)
</details>
<br/>
@@ -28,7 +34,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>0:26</summary>
![0:34](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/0_34.png) ![0:34](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/0_34.png)
</details>
<br/>
@@ -44,6 +55,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>1:04</summary>
![1:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_10.png) ![1:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_10.png)
@@ -89,6 +103,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![1:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_32.png) ![1:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_32.png)
</details>
<br/>
@@ -96,7 +112,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>1:34</summary>
![1:36](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_36.png) ![1:36](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_36.png)
</details>
<br/>
@@ -104,11 +125,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>1:38</summary>
![1:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_40.png) ![1:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_40.png)
![1:42](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_42.png) ![1:42](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_42.png)
</details>
<br/>
@@ -120,6 +146,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>1:46</summary>
![1:48](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_48.png) ![1:48](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/1_48.png)
@@ -145,6 +174,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![2:00](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_00.png) ![2:00](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_00.png)
</details>
<br/>
@@ -152,11 +183,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>2:02</summary>
![2:04](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_04.png) ![2:04](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_04.png)
![2:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_06.png) ![2:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_06.png)
</details>
<br/>
@@ -168,6 +204,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>2:10</summary>
![2:12](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_12.png) ![2:12](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_12.png)
@@ -177,6 +216,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![2:16](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_16.png) ![2:16](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_16.png)
</details>
<br/>
@@ -184,6 +225,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>2:18</summary>
![2:20](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_20.png) ![2:20](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_20.png)
@@ -197,6 +241,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![2:26](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_26.png) ![2:26](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_26.png)
</details>
<br/>
@@ -208,6 +254,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>2:30</summary>
![2:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_32.png) ![2:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/2_32.png)
@@ -269,6 +318,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![3:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_02.png) ![3:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_02.png)
</details>
<br/>
@@ -276,6 +327,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>3:04</summary>
![3:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_06.png) ![3:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_06.png)
@@ -285,6 +339,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![3:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_10.png) ![3:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_10.png)
</details>
<br/>
@@ -292,11 +348,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>3:12</summary>
![3:14](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_14.png) ![3:14](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_14.png)
![3:16](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_16.png) ![3:16](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_16.png)
</details>
<br/>
@@ -308,6 +369,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>3:20</summary>
![3:22](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_22.png) ![3:22](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_22.png)
@@ -341,6 +405,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![3:38](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_38.png) ![3:38](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_38.png)
</details>
<br/>
@@ -348,11 +414,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>3:40</summary>
![3:42](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_42.png) ![3:42](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_42.png)
![3:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_44.png) ![3:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_44.png)
</details>
<br/>
@@ -364,6 +435,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>3:48</summary>
![3:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_50.png) ![3:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/3_50.png)
@@ -393,6 +467,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![4:04](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_04.png) ![4:04](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_04.png)
</details>
<br/>
@@ -400,11 +476,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>4:06</summary>
![4:08](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_08.png) ![4:08](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_08.png)
![4:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_10.png) ![4:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_10.png)
</details>
<br/>
@@ -412,7 +493,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>4:12</summary>
![4:14](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_14.png) ![4:14](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_14.png)
</details>
<br/>
@@ -420,6 +506,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>4:16</summary>
![4:18](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_18.png) ![4:18](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_18.png)
@@ -437,6 +526,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![4:26](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_26.png) ![4:26](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_26.png)
</details>
<br/>
@@ -448,6 +539,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>4:30</summary>
![4:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_32.png) ![4:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_32.png)
@@ -457,6 +551,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![4:36](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_36.png) ![4:36](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_36.png)
</details>
<br/>
@@ -464,6 +560,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>4:38</summary>
![4:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_40.png) ![4:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_40.png)
@@ -485,6 +584,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![4:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_50.png) ![4:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_50.png)
</details>
<br/>
@@ -492,6 +593,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>4:52</summary>
![4:54](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_54.png) ![4:54](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/4_54.png)
@@ -517,6 +621,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![5:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_06.png) ![5:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_06.png)
</details>
<br/>
@@ -524,6 +630,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>5:08</summary>
![5:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_10.png) ![5:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_10.png)
@@ -585,6 +694,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![5:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_40.png) ![5:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_40.png)
</details>
<br/>
@@ -592,7 +703,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>5:42</summary>
![5:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_44.png) ![5:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_44.png)
</details>
<br/>
@@ -600,6 +716,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>5:46</summary>
![5:48](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_48.png) ![5:48](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_48.png)
@@ -621,6 +740,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![5:58](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_58.png) ![5:58](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/5_58.png)
</details>
<br/>
@@ -628,6 +749,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>6:00</summary>
![6:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_02.png) ![6:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_02.png)
@@ -649,6 +773,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![6:12](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_12.png) ![6:12](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_12.png)
</details>
<br/>
@@ -660,6 +786,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>6:16</summary>
![6:18](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_18.png) ![6:18](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_18.png)
@@ -673,6 +802,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![6:24](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_24.png) ![6:24](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_24.png)
</details>
<br/>
@@ -680,6 +811,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>6:26</summary>
![6:28](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_28.png) ![6:28](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_28.png)
@@ -717,6 +851,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![6:46](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_46.png) ![6:46](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_46.png)
</details>
<br/>
@@ -724,6 +860,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>6:48</summary>
![6:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_50.png) ![6:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/6_50.png)
@@ -785,6 +924,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![7:20](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_20.png) ![7:20](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_20.png)
</details>
<br/>
@@ -796,6 +937,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>7:24</summary>
![7:26](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_26.png) ![7:26](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_26.png)
@@ -829,6 +973,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![7:42](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_42.png) ![7:42](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_42.png)
</details>
<br/>
@@ -836,6 +982,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>7:44</summary>
![7:46](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_46.png) ![7:46](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_46.png)
@@ -853,6 +1002,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![7:54](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_54.png) ![7:54](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_54.png)
</details>
<br/>
@@ -860,7 +1011,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>7:56</summary>
![7:58](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_58.png) ![7:58](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/7_58.png)
</details>
<br/>
@@ -868,6 +1024,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>8:00</summary>
![8:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_02.png) ![8:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_02.png)
@@ -897,6 +1056,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![8:16](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_16.png) ![8:16](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_16.png)
</details>
<br/>
@@ -904,6 +1065,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>8:18</summary>
![8:20](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_20.png) ![8:20](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_20.png)
@@ -913,6 +1077,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![8:24](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_24.png) ![8:24](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_24.png)
</details>
<br/>
@@ -920,7 +1086,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>8:26</summary>
![8:28](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_28.png) ![8:28](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_28.png)
</details>
<br/>
@@ -928,11 +1099,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>8:30</summary>
![8:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_32.png) ![8:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_32.png)
![8:34](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_34.png) ![8:34](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_34.png)
</details>
<br/>
@@ -944,6 +1120,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>8:38</summary>
![8:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_40.png) ![8:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_40.png)
@@ -969,6 +1148,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![8:52](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_52.png) ![8:52](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_52.png)
</details>
<br/>
@@ -976,11 +1157,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>8:54</summary>
![8:56](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_56.png) ![8:56](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_56.png)
![8:58](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_58.png) ![8:58](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/8_58.png)
</details>
<br/>
@@ -988,7 +1174,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>9:00</summary>
![9:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_02.png) ![9:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_02.png)
</details>
<br/>
@@ -996,6 +1187,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>9:04</summary>
![9:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_06.png) ![9:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_06.png)
@@ -1017,6 +1211,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![9:16](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_16.png) ![9:16](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_16.png)
</details>
<br/>
@@ -1028,11 +1224,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>9:20</summary>
![9:22](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_22.png) ![9:22](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_22.png)
![9:24](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_24.png) ![9:24](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_24.png)
</details>
<br/>
@@ -1040,7 +1241,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>9:26</summary>
![9:28](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_28.png) ![9:28](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_28.png)
</details>
<br/>
@@ -1048,6 +1254,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>9:30</summary>
![9:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_32.png) ![9:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_32.png)
@@ -1065,6 +1274,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![9:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_40.png) ![9:40](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_40.png)
</details>
<br/>
@@ -1072,11 +1283,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>9:42</summary>
![9:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_44.png) ![9:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_44.png)
![9:46](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_46.png) ![9:46](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_46.png)
</details>
<br/>
@@ -1084,7 +1300,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>9:48</summary>
![9:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_50.png) ![9:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_50.png)
</details>
<br/>
@@ -1092,6 +1313,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>9:52</summary>
![9:54](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_54.png) ![9:54](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/9_54.png)
@@ -1109,6 +1333,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![10:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_02.png) ![10:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_02.png)
</details>
<br/>
@@ -1116,7 +1342,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>10:04</summary>
![10:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_06.png) ![10:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_06.png)
</details>
<br/>
@@ -1124,11 +1355,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>10:08</summary>
![10:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_10.png) ![10:10](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_10.png)
![10:12](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_12.png) ![10:12](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_12.png)
</details>
<br/>
@@ -1140,6 +1376,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>10:16</summary>
![10:18](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_18.png) ![10:18](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_18.png)
@@ -1177,6 +1416,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![10:36](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_36.png) ![10:36](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_36.png)
</details>
<br/>
@@ -1188,11 +1429,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>10:40</summary>
![10:42](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_42.png) ![10:42](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_42.png)
![10:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_44.png) ![10:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_44.png)
</details>
<br/>
@@ -1208,6 +1454,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>10:50</summary>
![10:52](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_52.png) ![10:52](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/10_52.png)
@@ -1237,6 +1486,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![11:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_06.png) ![11:06](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_06.png)
</details>
<br/>
@@ -1248,6 +1499,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>11:10</summary>
![11:12](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_12.png) ![11:12](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_12.png)
@@ -1269,6 +1523,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![11:22](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_22.png) ![11:22](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_22.png)
</details>
<br/>
@@ -1276,6 +1532,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>11:24</summary>
![11:26](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_26.png) ![11:26](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_26.png)
@@ -1285,6 +1544,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![11:30](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_30.png) ![11:30](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_30.png)
</details>
<br/>
@@ -1296,6 +1557,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>11:34</summary>
![11:36](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_36.png) ![11:36](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_36.png)
@@ -1313,6 +1577,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![11:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_44.png) ![11:44](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_44.png)
</details>
<br/>
@@ -1320,6 +1586,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>11:46</summary>
![11:48](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_48.png) ![11:48](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_48.png)
@@ -1329,6 +1598,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![11:52](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_52.png) ![11:52](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_52.png)
</details>
<br/>
@@ -1336,6 +1607,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>11:54</summary>
![11:56](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_56.png) ![11:56](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/11_56.png)
@@ -1345,6 +1619,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![12:00](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_00.png) ![12:00](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_00.png)
</details>
<br/>
@@ -1352,6 +1628,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>12:02</summary>
![12:04](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_04.png) ![12:04](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_04.png)
@@ -1373,6 +1652,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![12:14](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_14.png) ![12:14](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_14.png)
</details>
<br/>
@@ -1384,6 +1665,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>12:18</summary>
![12:20](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_20.png) ![12:20](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_20.png)
@@ -1409,6 +1693,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![12:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_32.png) ![12:32](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_32.png)
</details>
<br/>
@@ -1420,6 +1706,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>12:36</summary>
![12:38](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_38.png) ![12:38](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_38.png)
@@ -1445,6 +1734,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![12:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_50.png) ![12:50](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_50.png)
</details>
<br/>
@@ -1452,6 +1743,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
<details>
<summary>12:52</summary>
![12:54](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_54.png) ![12:54](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/12_54.png)
@@ -1469,5 +1763,7 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
![13:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/13_02.png) ![13:02](./みんなうっかり間違える!?中国語_様態補語と程度補語の文法の注意点とは/13_02.png)
</details>
<br/>
+321
View File
@@ -0,0 +1,321 @@
// @ts-check
/**
* group_similar.js
*
* Reads a markdown file containing timestamped screenshot references like:
* ![0:11](folder/0_11.png)
* ![0:13](folder/0_13.png)
* ...
*
* Consecutive images that are visually similar get collapsed into a
* <details> block anchored under the first (non-similar) image.
*
* Usage:
* node group_similar.js <input.md> [output.md] [--threshold 0.005]
*
* - output.md defaults to input.md (overwrites in place)
* - --threshold: similarity cutoff 0-1. Images with a diff ratio
* BELOW this value are considered "similar". Default: 0.005 (0.5%).
*
* Dependencies:
* npm install sharp
* npm install --save-dev @types/node (for //@ts-check)
*/
import { readFileSync, writeFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
// @ts-ignore - sharp ships no bundled .d.ts; types are not needed at runtime
import sharp from "sharp";
// ---------------------------------------------------------------------------
// Types (JSDoc only — kept in sync with every function signature)
// ---------------------------------------------------------------------------
/**
* @typedef {{ type: "image", alt: string, src: string, raw: string }} ImageNode
* @typedef {{ type: "text", raw: string }} TextNode
* @typedef {ImageNode | TextNode} Line
* @typedef {{ blanks: TextNode[], image: ImageNode }} Child
* @typedef {{ type: "group", anchor: ImageNode, children: Child[] }} GroupNode
* @typedef {Line | GroupNode} OutputNode
* @typedef {{ comparisons: number, grouped: number }} Stats
*/
// ---------------------------------------------------------------------------
// Image comparison (returns 0 = identical … 1 = completely different)
// ---------------------------------------------------------------------------
const COMPARE_WIDTH = 64;
const COMPARE_HEIGHT = 64;
/**
* @param {string} imgPath
* @returns {Promise<Buffer>}
*/
async function loadSmall(imgPath) {
return sharp(imgPath)
.resize(COMPARE_WIDTH, COMPARE_HEIGHT, { fit: "cover" })
.raw()
.toBuffer();
}
/**
* Mean squared error normalised to [0, 1].
* @param {Buffer} bufA
* @param {Buffer} bufB
* @returns {number}
*/
function mse(bufA, bufB) {
if (bufA.length !== bufB.length) return 1;
let sum = 0;
for (let i = 0; i < bufA.length; i++) {
const d = bufA[i] - bufB[i];
sum += d * d;
}
return sum / bufA.length / (255 * 255);
}
/**
* @param {string} pathA
* @param {string} pathB
* @param {number} threshold
* @returns {Promise<{ similar: boolean, score: number }>}
*/
async function compare(pathA, pathB, threshold) {
const [a, b] = await Promise.all([loadSmall(pathA), loadSmall(pathB)]);
const score = mse(a, b);
return { similar: score < threshold, score };
}
// ---------------------------------------------------------------------------
// Markdown parsing
// ---------------------------------------------------------------------------
const IMG_RE = /^!\[([^\]]*)\]\(([^)]+)\)\s*$/;
/**
* @param {string} line
* @returns {Line}
*/
function parseLine(line) {
const m = line.match(IMG_RE);
if (m) return { type: "image", alt: m[1], src: m[2], raw: line };
return { type: "text", raw: line };
}
/**
* @param {Line} node
* @returns {boolean}
*/
function isBlank(node) {
return node.type === "text" && node.raw.trim() === "";
}
// ---------------------------------------------------------------------------
// Core grouping logic
// ---------------------------------------------------------------------------
/**
* Walk parsed lines and group consecutive similar images.
* Blank lines between similar images travel with the group; non-blank
* non-image text always breaks the lookahead.
*
* @param {Line[]} lines
* @param {string} baseDir
* @param {number} threshold
* @param {Stats} stats - mutated in place for the caller's summary
* @returns {Promise<OutputNode[]>}
*/
async function groupImages(lines, baseDir, threshold, stats) {
/** @type {OutputNode[]} */
const result = [];
let i = 0;
while (i < lines.length) {
const line = lines[i];
if (line.type !== "image") {
result.push(line);
i++;
continue;
}
/** @type {ImageNode} */
const anchor = line;
/** @type {Child[]} */
const children = [];
let j = i + 1;
// lookahead
while (j < lines.length) {
/** @type {TextNode[]} */
const blanks = [];
while (j < lines.length && isBlank(lines[j])) {
// isBlank already guarantees type === "text"
blanks.push(/** @type {TextNode} */ (lines[j]));
j++;
}
if (j >= lines.length || lines[j].type !== "image") {
j -= blanks.length; // rewind - blanks stay outside
break;
}
/** @type {ImageNode} */
const candidate = /** @type {ImageNode} */ (lines[j]);
const { similar } = await compare(
resolve(baseDir, anchor.src),
resolve(baseDir, candidate.src),
threshold,
);
stats.comparisons++;
if (similar) {
stats.grouped++;
children.push({ blanks, image: candidate });
j++;
} else {
j -= blanks.length; // rewind - blanks stay outside
break;
}
}
if (children.length > 0) {
result.push({ type: "group", anchor, children });
} else {
result.push(anchor);
}
i = j;
}
return result;
}
// ---------------------------------------------------------------------------
// Rendering
// ---------------------------------------------------------------------------
/**
* @param {ImageNode} node
* @returns {string}
*/
function renderImage(node) {
return `![${node.alt}](${node.src})`;
}
/**
* @param {GroupNode} node
* @returns {string}
*/
function renderGroup(node) {
const parts = [];
parts.push(renderImage(node.anchor));
// First child's blanks are the original spacing between anchor and the
// next image. Emit them so multiple blank lines are preserved.
// Then guarantee at least one blank line exists before <details>.
const firstBlanks = node.children[0].blanks;
if (firstBlanks.length === 0) parts.push("");
for (const b of firstBlanks) parts.push(b.raw);
parts.push("<details>");
parts.push(`<summary>${node.anchor.alt}</summary>`);
parts.push(""); // 1 blank line after <summary>
// first child: blanks already emitted above
parts.push(renderImage(node.children[0].image));
for (let ci = 1; ci < node.children.length; ci++) {
const { blanks, image } = node.children[ci];
for (const b of blanks) parts.push(b.raw);
parts.push(renderImage(image));
}
parts.push("</details>");
parts.push("<br/>");
return parts.join("\n");
}
/**
* @param {OutputNode[]} nodes
* @returns {string}
*/
function render(nodes) {
return nodes
.map((n) => (n.type === "group" ? renderGroup(n) : n.raw))
.join("\n");
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Read a markdown file, group similar consecutive images into <details>
* blocks, and write the result.
*
* @param {string} inputPath - path to the source .md file
* @param {string} outputPath - path to write (may be the same as inputPath)
* @param {number} [threshold=0.005] - MSE cutoff; lower = stricter grouping
* @returns {Promise<Stats>} - comparison stats (useful for callers)
*/
export async function renderFile(inputPath, outputPath, threshold = 0.005) {
const resolvedInput = resolve(inputPath);
const resolvedOutput = resolve(outputPath);
const baseDir = dirname(resolvedInput);
console.log(`Reading : ${resolvedInput}`);
console.log(`Threshold : ${threshold}`);
const raw = readFileSync(resolvedInput, "utf8");
const lines = raw.split("\n").map(parseLine);
/** @type {Stats} */
const stats = { comparisons: 0, grouped: 0 };
const grouped = await groupImages(lines, baseDir, threshold, stats);
const output = render(grouped);
writeFileSync(resolvedOutput, output, "utf8");
console.log(
`Comparisons: ${stats.comparisons} | grouped: ${stats.grouped}`,
);
console.log(`Wrote : ${resolvedOutput}`);
return stats;
}
// ---------------------------------------------------------------------------
// CLI entry-point (runs only when executed directly)
// ---------------------------------------------------------------------------
const __filename = fileURLToPath(import.meta.url);
const isMain =
process.argv[1] && resolve(process.argv[1]) === resolve(__filename);
if (isMain) {
const args = process.argv.slice(2);
const flagIdx = args.indexOf("--threshold");
const threshold = flagIdx !== -1 ? parseFloat(args[flagIdx + 1]) : 0.005;
const positional =
flagIdx === -1
? args
: args.filter(
(/** @type {string} */ _a, /** @type {number} */ i) =>
i !== flagIdx && i !== flagIdx + 1,
);
const inputPath = positional[0];
const outputPath = positional[1] || positional[0];
if (!inputPath) {
console.error(
"Usage: node group_similar.js <input.md> [output.md] [--threshold 0.005]",
);
process.exit(1);
}
await renderFile(inputPath, outputPath, threshold);
}
+1
View File
@@ -6,6 +6,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"sharp": "^0.34.5",
"vite": "^7.3.1", "vite": "^7.3.1",
"vitepress": "2.0.0-alpha.15", "vitepress": "2.0.0-alpha.15",
"vue": "^3.5.27", "vue": "^3.5.27",
+292
View File
@@ -11,6 +11,9 @@ importers:
gray-matter: gray-matter:
specifier: ^4.0.3 specifier: ^4.0.3
version: 4.0.3 version: 4.0.3
sharp:
specifier: ^0.34.5
version: 0.34.5
vite: vite:
specifier: ^7.3.1 specifier: ^7.3.1
version: 7.3.1(@types/node@20.19.30) version: 7.3.1(@types/node@20.19.30)
@@ -186,6 +189,9 @@ packages:
search-insights: search-insights:
optional: true optional: true
'@emnapi/runtime@1.8.1':
resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==}
'@esbuild/aix-ppc64@0.27.2': '@esbuild/aix-ppc64@0.27.2':
resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -348,6 +354,143 @@ packages:
'@iconify/types@2.0.0': '@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
'@img/colour@1.0.0':
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
engines: {node: '>=18'}
'@img/sharp-darwin-arm64@0.34.5':
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-x64@0.34.5':
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-darwin-arm64@1.2.4':
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.2.4':
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-linux-arm64@1.2.4':
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
'@img/sharp-win32-arm64@0.34.5':
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [win32]
'@img/sharp-win32-ia32@0.34.5':
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
'@img/sharp-win32-x64@0.34.5':
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
'@jridgewell/sourcemap-codec@1.5.5': '@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
@@ -714,6 +857,10 @@ packages:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'} engines: {node: '>=6'}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
devlop@1.1.0: devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
@@ -914,6 +1061,15 @@ packages:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
engines: {node: '>=4'} engines: {node: '>=4'}
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
hasBin: true
sharp@0.34.5:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
shell-quote@1.8.3: shell-quote@1.8.3:
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -1296,6 +1452,11 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- '@algolia/client-search' - '@algolia/client-search'
'@emnapi/runtime@1.8.1':
dependencies:
tslib: 2.8.1
optional: true
'@esbuild/aix-ppc64@0.27.2': '@esbuild/aix-ppc64@0.27.2':
optional: true optional: true
@@ -1380,6 +1541,102 @@ snapshots:
'@iconify/types@2.0.0': {} '@iconify/types@2.0.0': {}
'@img/colour@1.0.0': {}
'@img/sharp-darwin-arm64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.2.4
optional: true
'@img/sharp-darwin-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.2.4
optional: true
'@img/sharp-libvips-darwin-arm64@1.2.4':
optional: true
'@img/sharp-libvips-darwin-x64@1.2.4':
optional: true
'@img/sharp-libvips-linux-arm64@1.2.4':
optional: true
'@img/sharp-libvips-linux-arm@1.2.4':
optional: true
'@img/sharp-libvips-linux-ppc64@1.2.4':
optional: true
'@img/sharp-libvips-linux-riscv64@1.2.4':
optional: true
'@img/sharp-libvips-linux-s390x@1.2.4':
optional: true
'@img/sharp-libvips-linux-x64@1.2.4':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
optional: true
'@img/sharp-linux-arm64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.2.4
optional: true
'@img/sharp-linux-arm@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.2.4
optional: true
'@img/sharp-linux-ppc64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.2.4
optional: true
'@img/sharp-linux-riscv64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-riscv64': 1.2.4
optional: true
'@img/sharp-linux-s390x@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.2.4
optional: true
'@img/sharp-linux-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.2.4
optional: true
'@img/sharp-linuxmusl-arm64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
optional: true
'@img/sharp-linuxmusl-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
optional: true
'@img/sharp-wasm32@0.34.5':
dependencies:
'@emnapi/runtime': 1.8.1
optional: true
'@img/sharp-win32-arm64@0.34.5':
optional: true
'@img/sharp-win32-ia32@0.34.5':
optional: true
'@img/sharp-win32-x64@0.34.5':
optional: true
'@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/sourcemap-codec@1.5.5': {}
'@opentelemetry/api@1.9.0': {} '@opentelemetry/api@1.9.0': {}
@@ -1714,6 +1971,8 @@ snapshots:
dequal@2.0.3: {} dequal@2.0.3: {}
detect-libc@2.1.2: {}
devlop@1.1.0: devlop@1.1.0:
dependencies: dependencies:
dequal: 2.0.3 dequal: 2.0.3
@@ -1947,6 +2206,39 @@ snapshots:
extend-shallow: 2.0.1 extend-shallow: 2.0.1
kind-of: 6.0.3 kind-of: 6.0.3
semver@7.7.3: {}
sharp@0.34.5:
dependencies:
'@img/colour': 1.0.0
detect-libc: 2.1.2
semver: 7.7.3
optionalDependencies:
'@img/sharp-darwin-arm64': 0.34.5
'@img/sharp-darwin-x64': 0.34.5
'@img/sharp-libvips-darwin-arm64': 1.2.4
'@img/sharp-libvips-darwin-x64': 1.2.4
'@img/sharp-libvips-linux-arm': 1.2.4
'@img/sharp-libvips-linux-arm64': 1.2.4
'@img/sharp-libvips-linux-ppc64': 1.2.4
'@img/sharp-libvips-linux-riscv64': 1.2.4
'@img/sharp-libvips-linux-s390x': 1.2.4
'@img/sharp-libvips-linux-x64': 1.2.4
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
'@img/sharp-linux-arm': 0.34.5
'@img/sharp-linux-arm64': 0.34.5
'@img/sharp-linux-ppc64': 0.34.5
'@img/sharp-linux-riscv64': 0.34.5
'@img/sharp-linux-s390x': 0.34.5
'@img/sharp-linux-x64': 0.34.5
'@img/sharp-linuxmusl-arm64': 0.34.5
'@img/sharp-linuxmusl-x64': 0.34.5
'@img/sharp-wasm32': 0.34.5
'@img/sharp-win32-arm64': 0.34.5
'@img/sharp-win32-ia32': 0.34.5
'@img/sharp-win32-x64': 0.34.5
shell-quote@1.8.3: {} shell-quote@1.8.3: {}
shiki@3.21.0: shiki@3.21.0:
+2
View File
@@ -0,0 +1,2 @@
onlyBuiltDependencies:
- sharp
+61 -35
View File
@@ -1,61 +1,68 @@
// @ts-check // @ts-check
import { WebSocketServer } from 'ws'; import { WebSocketServer } from "ws";
import { readFileSync, mkdirSync, writeFileSync } from "fs"; import { readFileSync, mkdirSync, writeFileSync } from "fs";
import { join as joinPath } from 'path' import { join as joinPath } from "path";
import matter from 'gray-matter'; import matter from "gray-matter";
const PORT = 8080 import { renderFile } from "./group-similar.js";
const PORT = 8080;
const wss = new WebSocketServer({ port: PORT }); const wss = new WebSocketServer({ port: PORT });
console.log(`WebSocket server running on ws://localhost:${PORT}`); console.log(`WebSocket server running on ws://localhost:${PORT}`);
wss.on('connection', (ws) => { wss.on("connection", (ws) => {
console.log('New client connected'); console.log("New client connected");
let filename = '' let filename = "";
const ROOT_DIR = 'docs'; const ROOT_DIR = "docs";
ws.on('message', (message, isBinary) => { ws.on("message", (message, isBinary) => {
if (message instanceof Buffer && isBinary) { if (message instanceof Buffer && isBinary) {
writeFileSync(joinPath(ROOT_DIR, filename), message); writeFileSync(joinPath(ROOT_DIR, filename), message);
return; return;
} }
const { name, ...payload } = JSON.parse(message.toString('utf-8')); const { name, ...payload } = JSON.parse(message.toString("utf-8"));
switch (name) { switch (name) {
case 'download_q': { case "download_q":
{
const { q, duration } = payload; const { q, duration } = payload;
console.log(`Start ${q}`) console.log(`Start ${q}`);
const q_stem = q.replace(/\.[a-z0-9]+$/, "").replace(/\[.+?\]/g, '').replace(/\s+/g, '_').trim(); const q_stem = q
.replace(/\.[a-z0-9]+$/, "")
.replace(/\[.+?\]/g, "")
.replace(/\s+/g, "_")
.trim();
try { try {
mkdirSync(joinPath(ROOT_DIR, q_stem)); mkdirSync(joinPath(ROOT_DIR, q_stem));
} catch (e) {} } catch (e) {}
let isChanged = false; let isChanged = false;
const ts = []; const ts = [];
let { data, content } = matter(readFileSync(joinPath(ROOT_DIR, `${q_stem}.md`), 'utf-8')); const mdFile = joinPath(ROOT_DIR, `${q_stem}.md`);
data.video = '/' + q; let { data, content } = matter(readFileSync(mdFile, "utf-8"));
data.video = "/" + q;
const interval = data.interval; const { dedup, interval } = data;
if (typeof interval === 'number') { if (typeof interval === "number") {
for (let t = 0; t < duration; t += interval) { for (let t = 0; t < duration; t += interval) {
const min = Math.floor(t / 60) const min = Math.floor(t / 60);
const sec = Math.floor(t - min * 60) const sec = Math.floor(t - min * 60);
content += `\n${min}:${sec.toString().padStart(2, '0')}\n`; content += `\n${min}:${sec.toString().padStart(2, "0")}\n`;
} }
} }
const re = /^(?<min>\d{1,2}):(?<sec>\d{2})\s*$/; const re = /^(?<min>\d{1,2}):(?<sec>\d{2})\s*$/;
const lines = content.split('\n') const lines = content.split("\n");
content = '' content = "";
for (const line of lines) { for (const line of lines) {
const m = re.exec(line); const m = re.exec(line);
if (m?.groups) { if (m?.groups) {
@@ -70,23 +77,42 @@ wss.on('connection', (ws) => {
} }
if (isChanged) { if (isChanged) {
writeFileSync(joinPath(ROOT_DIR, `${q_stem}.md`), matter.stringify(content, data)); writeFileSync(mdFile, matter.stringify(content, data));
if (dedup) {
renderFile(
mdFile,
mdFile,
typeof dedup === "number" ? dedup : undefined,
).then((stats) => {
// Don't log a thing yet.
});
}
ws.send(JSON.stringify({ name: "seek_q_ts", q, ts })); ws.send(JSON.stringify({ name: "seek_q_ts", q, ts }));
} }
} break; }
case 'timed_q' : { break;
case "timed_q":
{
const { q, t } = payload; const { q, t } = payload;
const q_stem = q.replace(/\.[a-z0-9]+$/, "").replace(/\[.+?\]/g, '').trim(); const q_stem = q
filename = t ? joinPath(q_stem, `${t}.png`) : ''; .replace(/\.[a-z0-9]+$/, "")
} break; .replace(/\[.+?\]/g, "")
case 'done' : { .trim();
const { q } = payload filename = t ? joinPath(q_stem, `${t}.png`) : "";
console.log(`Done ${q}`) }
} break; break;
case "done":
{
const { q } = payload;
console.log(`Done ${q}`);
}
break;
} }
}); });
ws.on('close', () => { ws.on("close", () => {
console.log('Client disconnected'); console.log("Client disconnected");
}); });
}); });