Skip to content

Commit 539dfa6

Browse files
committed
fix: video element parsing error
1 parent 8a8c5c5 commit 539dfa6

File tree

6 files changed

+77
-2
lines changed

6 files changed

+77
-2
lines changed

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
"remark-rehype": "^11.1.1",
110110
"remark-stringify": "^11.0.0",
111111
"unified": "^11.0.5",
112+
"unist-util-visit": "^5.0.0",
112113
"uuid": "^8.3.2",
113114
"y-prosemirror": "^1.3.4",
114115
"y-protocols": "^1.0.6",

packages/core/src/api/exporters/markdown/markdownExporter.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import {
1111
initializeESMDependencies,
1212
} from "../../../util/esmDependencies.js";
1313
import { createExternalHTMLExporter } from "../html/externalHTMLExporter.js";
14-
import { removeUnderlines } from "./removeUnderlinesRehypePlugin.js";
14+
import { removeUnderlines } from "./util/removeUnderlinesRehypePlugin.js";
1515
import { addSpacesToCheckboxes } from "./util/addSpacesToCheckboxesRehypePlugin.js";
16+
import { convertVideoToMarkdown } from "./util/convertVideoToMarkdownRehypePlugin.js";
1617

1718
// Needs to be sync because it's used in drag handler event (SideMenuPlugin)
1819
// Ideally, call `await initializeESMDependencies()` before calling this function
@@ -28,12 +29,15 @@ export function cleanHTMLToMarkdown(cleanHTMLString: string) {
2829
const markdownString = deps.unified
2930
.unified()
3031
.use(deps.rehypeParse.default, { fragment: true })
32+
.use(convertVideoToMarkdown)
3133
.use(removeUnderlines)
3234
.use(addSpacesToCheckboxes)
3335
.use(deps.rehypeRemark.default)
3436
.use(deps.remarkGfm.default)
3537
.use(deps.remarkStringify.default, {
36-
handlers: { text: (node) => node.value },
38+
handlers: {
39+
text: (node) => node.value,
40+
},
3741
})
3842
.processSync(cleanHTMLString);
3943

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { visit } from "unist-util-visit";
2+
3+
// Originally, rehypeParse parses videos as links, which is incorrect.
4+
export function convertVideoToMarkdown() {
5+
return (tree: any) => {
6+
visit(tree, "element", (node, index, parent) => {
7+
if (node.tagName === "video") {
8+
const src = node.properties?.src || node.properties?.["data-url"] || "";
9+
const name =
10+
node.properties?.title || node.properties?.["data-name"] || "";
11+
parent.children[index!] = {
12+
type: "text",
13+
value: `![${name}](${src})`,
14+
};
15+
}
16+
});
17+
};
18+
}

packages/core/src/api/parsers/markdown/parseMarkdown.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from "../../../schema/index.js";
99
import { initializeESMDependencies } from "../../../util/esmDependencies.js";
1010
import { HTMLToBlocks } from "../html/parseHTML.js";
11+
import { isVideoUrl } from "../../../util/string.js";
1112

1213
// modified version of https://github.com/syntax-tree/mdast-util-to-hast/blob/main/lib/handlers/code.js
1314
// that outputs a data-language attribute instead of a CSS class (e.g.: language-typescript)
@@ -48,6 +49,27 @@ function code(state: any, node: any) {
4849
return result;
4950
}
5051

52+
function video(state: any, node: any) {
53+
const url = String(node?.url || "");
54+
const title = node?.title ? String(node.title) : undefined;
55+
56+
let result: any = {
57+
type: "element",
58+
tagName: "video",
59+
properties: {
60+
src: url,
61+
"data-name": title,
62+
"data-url": url,
63+
controls: true,
64+
},
65+
children: [],
66+
};
67+
state.patch?.(node, result);
68+
result = state.applyData ? state.applyData(node, result) : result;
69+
70+
return result;
71+
}
72+
5173
export async function markdownToHTML(markdown: string): Promise<string> {
5274
const deps = await initializeESMDependencies();
5375

@@ -58,6 +80,15 @@ export async function markdownToHTML(markdown: string): Promise<string> {
5880
.use(deps.remarkRehype.default, {
5981
handlers: {
6082
...(deps.remarkRehype.defaultHandlers as any),
83+
image: (state: any, node: any) => {
84+
const url = String(node?.url || "");
85+
86+
if (isVideoUrl(url)) {
87+
return video(state, node);
88+
} else {
89+
return deps.remarkRehype.defaultHandlers.image(state, node);
90+
}
91+
},
6192
code,
6293
},
6394
})

packages/core/src/util/string.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,24 @@ export function filenameFromURL(url: string): string {
1313
}
1414
return parts[parts.length - 1];
1515
}
16+
17+
export function isVideoUrl(url: string) {
18+
const videoExtensions = [
19+
"mp4",
20+
"webm",
21+
"ogg",
22+
"mov",
23+
"mkv",
24+
"flv",
25+
"avi",
26+
"wmv",
27+
"m4v",
28+
];
29+
try {
30+
const pathname = new URL(url).pathname;
31+
const ext = pathname.split(".").pop()?.toLowerCase() || "";
32+
return videoExtensions.includes(ext);
33+
} catch (_) {
34+
return false;
35+
}
36+
}

0 commit comments

Comments
 (0)