add dedup script
This commit is contained in:
1 parent
2b47af46de
commit
62c6775e67
7 files changed
+1009
-58
No files matched your search
Vendored
+13
@@ -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>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -28,7 +34,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>0:26</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -44,6 +55,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>1:04</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -89,6 +103,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -96,7 +112,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>1:34</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -104,11 +125,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>1:38</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -120,6 +146,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>1:46</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -145,6 +174,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -152,11 +183,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>2:02</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -168,6 +204,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>2:10</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -177,6 +216,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -184,6 +225,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>2:18</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -197,6 +241,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -208,6 +254,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>2:30</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -269,6 +318,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -276,6 +327,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>3:04</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -285,6 +339,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -292,11 +348,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>3:12</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -308,6 +369,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>3:20</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -341,6 +405,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -348,11 +414,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>3:40</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -364,6 +435,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>3:48</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -393,6 +467,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -400,11 +476,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>4:06</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -412,7 +493,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>4:12</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -420,6 +506,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>4:16</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -437,6 +526,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -448,6 +539,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>4:30</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -457,6 +551,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -464,6 +560,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>4:38</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -485,6 +584,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -492,6 +593,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>4:52</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -517,6 +621,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -524,6 +630,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>5:08</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -585,6 +694,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -592,7 +703,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>5:42</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -600,6 +716,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>5:46</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -621,6 +740,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -628,6 +749,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>6:00</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -649,6 +773,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -660,6 +786,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>6:16</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -673,6 +802,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -680,6 +811,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>6:26</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -717,6 +851,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -724,6 +860,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>6:48</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -785,6 +924,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -796,6 +937,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>7:24</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -829,6 +973,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -836,6 +982,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>7:44</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -853,6 +1002,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -860,7 +1011,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>7:56</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -868,6 +1024,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>8:00</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -897,6 +1056,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -904,6 +1065,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>8:18</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -913,6 +1077,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -920,7 +1086,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>8:26</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -928,11 +1099,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>8:30</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -944,6 +1120,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>8:38</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -969,6 +1148,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -976,11 +1157,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>8:54</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -988,7 +1174,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>9:00</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -996,6 +1187,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>9:04</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1017,6 +1211,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1028,11 +1224,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>9:20</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1040,7 +1241,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>9:26</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1048,6 +1254,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>9:30</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1065,6 +1274,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1072,11 +1283,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>9:42</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1084,7 +1300,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>9:48</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1092,6 +1313,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>9:52</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1109,6 +1333,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1116,7 +1342,12 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>10:04</summary>
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1124,11 +1355,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>10:08</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1140,6 +1376,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>10:16</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1177,6 +1416,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1188,11 +1429,16 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>10:40</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1208,6 +1454,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>10:50</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1237,6 +1486,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1248,6 +1499,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>11:10</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1269,6 +1523,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1276,6 +1532,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>11:24</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1285,6 +1544,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1296,6 +1557,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>11:34</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1313,6 +1577,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1320,6 +1586,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>11:46</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1329,6 +1598,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1336,6 +1607,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>11:54</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1345,6 +1619,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1352,6 +1628,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>12:02</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1373,6 +1652,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1384,6 +1665,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>12:18</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1409,6 +1693,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1420,6 +1706,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>12:36</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1445,6 +1734,8 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1452,6 +1743,9 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>12:52</summary>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -1469,5 +1763,7 @@ video: '/みんなうっかり間違える!?中国語 様態補語と程度
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
</details>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
// @ts-check
|
||||||
|
/**
|
||||||
|
* group_similar.js
|
||||||
|
*
|
||||||
|
* Reads a markdown file containing timestamped screenshot references like:
|
||||||
|
* 
|
||||||
|
* 
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* 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 ``;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
|||||||
Generated
+292
@@ -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:
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
onlyBuiltDependencies:
|
||||||
|
- sharp
|
||||||
@@ -1,92 +1,118 @@
|
|||||||
// @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;
|
{
|
||||||
console.log(`Start ${q}`)
|
const { q, duration } = payload;
|
||||||
|
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 = [];
|
const mdFile = joinPath(ROOT_DIR, `${q_stem}.md`);
|
||||||
|
let { data, content } = matter(readFileSync(mdFile, "utf-8"));
|
||||||
|
data.video = "/" + q;
|
||||||
|
|
||||||
let { data, content } = matter(readFileSync(joinPath(ROOT_DIR, `${q_stem}.md`), 'utf-8'));
|
const { dedup, interval } = data;
|
||||||
data.video = '/' + q;
|
if (typeof interval === "number") {
|
||||||
|
for (let t = 0; t < duration; t += interval) {
|
||||||
|
const min = Math.floor(t / 60);
|
||||||
|
const sec = Math.floor(t - min * 60);
|
||||||
|
|
||||||
const interval = data.interval;
|
content += `\n${min}:${sec.toString().padStart(2, "0")}\n`;
|
||||||
if (typeof interval === 'number') {
|
}
|
||||||
for (let t = 0; t < duration; t += interval) {
|
}
|
||||||
const min = Math.floor(t / 60)
|
|
||||||
const sec = Math.floor(t - min * 60)
|
|
||||||
|
|
||||||
content += `\n${min}:${sec.toString().padStart(2, '0')}\n`;
|
const re = /^(?<min>\d{1,2}):(?<sec>\d{2})\s*$/;
|
||||||
|
const lines = content.split("\n");
|
||||||
|
content = "";
|
||||||
|
for (const line of lines) {
|
||||||
|
const m = re.exec(line);
|
||||||
|
if (m?.groups) {
|
||||||
|
const { min, sec } = m.groups;
|
||||||
|
const t = `${min}_${sec}`;
|
||||||
|
ts.push(t);
|
||||||
|
content += `\n\n\n`;
|
||||||
|
isChanged = true;
|
||||||
|
} else {
|
||||||
|
content += `${line}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isChanged) {
|
||||||
|
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 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
const re = /^(?<min>\d{1,2}):(?<sec>\d{2})\s*$/;
|
case "timed_q":
|
||||||
const lines = content.split('\n')
|
{
|
||||||
content = ''
|
const { q, t } = payload;
|
||||||
for (const line of lines) {
|
const q_stem = q
|
||||||
const m = re.exec(line);
|
.replace(/\.[a-z0-9]+$/, "")
|
||||||
if (m?.groups) {
|
.replace(/\[.+?\]/g, "")
|
||||||
const { min, sec } = m.groups;
|
.trim();
|
||||||
const t = `${min}_${sec}`;
|
filename = t ? joinPath(q_stem, `${t}.png`) : "";
|
||||||
ts.push(t);
|
|
||||||
content += `\n\n\n`;
|
|
||||||
isChanged = true;
|
|
||||||
} else {
|
|
||||||
content += `${line}\n`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
if (isChanged){
|
case "done":
|
||||||
writeFileSync(joinPath(ROOT_DIR, `${q_stem}.md`), matter.stringify(content, data));
|
{
|
||||||
ws.send(JSON.stringify({ name: "seek_q_ts", q, ts }));
|
const { q } = payload;
|
||||||
|
console.log(`Done ${q}`);
|
||||||
}
|
}
|
||||||
} break;
|
break;
|
||||||
case 'timed_q' : {
|
|
||||||
const { q, t } = payload;
|
|
||||||
const q_stem = q.replace(/\.[a-z0-9]+$/, "").replace(/\[.+?\]/g, '').trim();
|
|
||||||
filename = t ? joinPath(q_stem, `${t}.png`) : '';
|
|
||||||
} 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");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user