From 047bafd2c6e5cfa62098daa9fff92dc3da2b0a58 Mon Sep 17 00:00:00 2001 From: Andrew Polk Date: Tue, 8 Aug 2023 16:39:46 -0700 Subject: [PATCH] fix: Use video player only for embedded videos (#60) --- src/config/default.docunotion.config.ts | 11 +--- src/plugins/VideoTransformer.spec.ts | 87 +++++++++++++++++++++++++ src/plugins/VideoTransformer.ts | 29 +++++++++ src/plugins/embedTweaks.spec.ts | 60 +---------------- src/plugins/embedTweaks.ts | 23 ------- src/plugins/externalLinks.spec.ts | 19 +++++- src/plugins/pluginTypes.ts | 9 ++- src/transform.ts | 17 +++-- 8 files changed, 153 insertions(+), 102 deletions(-) create mode 100644 src/plugins/VideoTransformer.spec.ts create mode 100644 src/plugins/VideoTransformer.ts diff --git a/src/config/default.docunotion.config.ts b/src/config/default.docunotion.config.ts index ee3dad4..6029250 100644 --- a/src/config/default.docunotion.config.ts +++ b/src/config/default.docunotion.config.ts @@ -1,9 +1,4 @@ -import { - gifEmbed, - imgurGifEmbed, - vimeoEmbed, - youtubeEmbed, -} from "../plugins/embedTweaks"; +import { gifEmbed, imgurGifEmbed } from "../plugins/embedTweaks"; import { standardImageTransformer } from "../images"; import { standardInternalLinkConversion } from "../plugins/internalLinks"; import { standardCalloutTransformer } from "../plugins/CalloutTransformer"; @@ -13,6 +8,7 @@ import { standardEscapeHtmlBlockModifier } from "../plugins/EscapeHtmlBlockModif import { standardHeadingTransformer } from "../plugins/HeadingTransformer"; import { standardNumberedListTransformer } from "../plugins/NumberedListTransformer"; import { standardTableTransformer } from "../plugins/TableTransformer"; +import { standardVideoTransformer } from "../plugins/VideoTransformer"; import { standardExternalLinkConversion } from "../plugins/externalLinks"; import { IDocuNotionConfig } from "./configuration"; @@ -30,6 +26,7 @@ const defaultConfig: IDocuNotionConfig = { standardCalloutTransformer, standardTableTransformer, standardNumberedListTransformer, + standardVideoTransformer, // Link modifiers, which are special because they can read metadata from all the pages in order to figure out the correct url standardInternalLinkConversion, @@ -38,8 +35,6 @@ const defaultConfig: IDocuNotionConfig = { // Regexps plus javascript `import`s that operate on the Markdown output imgurGifEmbed, gifEmbed, - youtubeEmbed, - vimeoEmbed, ], }; diff --git a/src/plugins/VideoTransformer.spec.ts b/src/plugins/VideoTransformer.spec.ts new file mode 100644 index 0000000..4433af8 --- /dev/null +++ b/src/plugins/VideoTransformer.spec.ts @@ -0,0 +1,87 @@ +import { setLogLevel } from "../log"; +import { NotionBlock } from "../types"; +import { standardVideoTransformer } from "./VideoTransformer"; +import { blocksToMarkdown } from "./pluginTestRun"; + +test("youtube embedded", async () => { + const config = { plugins: [standardVideoTransformer] }; + const result = await blocksToMarkdown(config, [ + { + object: "block", + type: "video", + video: { + caption: [ + { + type: "text", + text: { + content: "A video about editing in Notion", + link: null, + }, + plain_text: "A video about editing in Notion", + href: null, + }, + ], + type: "external", + external: { url: "https://www.youtube.com/watch?v=FXIrojSK3Jo" }, + }, + } as unknown as NotionBlock, + ]); + expect(result).toContain(`import ReactPlayer from "react-player";`); + expect(result).toContain( + `` + ); +}); + +test("vimeo embedded", async () => { + setLogLevel("verbose"); + const config = { plugins: [standardVideoTransformer] }; + const result = await blocksToMarkdown(config, [ + { + object: "block", + type: "video", + video: { + caption: [], + type: "external", + external: { url: "https://vimeo.com/4613611xx" }, + }, + } as unknown as NotionBlock, + ]); + expect(result).toContain(`import ReactPlayer from "react-player";`); + expect(result).toContain( + `` + ); +}); + +test("video link, not embedded", async () => { + setLogLevel("verbose"); + const config = { plugins: [standardVideoTransformer] }; + const result = await blocksToMarkdown(config, [ + { + object: "block", + type: "paragraph", + paragraph: { + rich_text: [ + { + type: "text", + text: { + content: "https://vimeo.com/4613611xx", + link: { + url: "https://vimeo.com/4613611xx", + }, + }, + annotations: { + code: false, + }, + plain_text: "https://vimeo.com/4613611xx", + href: "https://vimeo.com/4613611xx", + }, + ], + color: "default", + }, + } as unknown as NotionBlock, + ]); + expect(result).toContain( + "[https://vimeo.com/4613611xx](https://vimeo.com/4613611xx)" + ); + expect(result).not.toContain(`import`); +}); diff --git a/src/plugins/VideoTransformer.ts b/src/plugins/VideoTransformer.ts new file mode 100644 index 0000000..3151956 --- /dev/null +++ b/src/plugins/VideoTransformer.ts @@ -0,0 +1,29 @@ +import { VideoBlockObjectResponse } from "@notionhq/client/build/src/api-endpoints"; +import { IDocuNotionContext, IPlugin } from "./pluginTypes"; +import { warning } from "../log"; +import { NotionBlock } from "../types"; + +export const standardVideoTransformer: IPlugin = { + name: "video", + notionToMarkdownTransforms: [ + { + type: "video", + getStringFromBlock: ( + context: IDocuNotionContext, + block: NotionBlock + ): string => { + const video = (block as VideoBlockObjectResponse).video; + if (video.type === "external") { + if (!context.imports) context.imports = []; + context.imports.push(`import ReactPlayer from "react-player";`); + return ``; + } else { + warning( + `[standardVideoTransformer] Found Notion "video" block with type ${video.type}. The best docu-notion can do for now is ignore it.` + ); + } + return ""; + }, + }, + ], +}; diff --git a/src/plugins/embedTweaks.spec.ts b/src/plugins/embedTweaks.spec.ts index 2491f21..1084a0c 100644 --- a/src/plugins/embedTweaks.spec.ts +++ b/src/plugins/embedTweaks.spec.ts @@ -2,65 +2,7 @@ import { NotionBlock } from "../types"; import { IPlugin } from "./pluginTypes"; import { setLogLevel } from "../log"; import { blocksToMarkdown } from "./pluginTestRun"; -import { - gifEmbed, - imgurGifEmbed, - vimeoEmbed, - youtubeEmbed, -} from "./embedTweaks"; - -test("youtube", async () => { - const config = { plugins: [youtubeEmbed] }; - const result = await blocksToMarkdown(config, [ - { - object: "block", - id: "e6ddd1d4-36d4-4925-94c1-5dff4662c1f3", - has_children: false, - archived: false, - type: "video", - video: { - caption: [ - { - type: "text", - text: { - content: "A video about editing in Notion", - link: null, - }, - plain_text: "A video about editing in Notion", - href: null, - }, - ], - type: "external", - external: { url: "https://www.youtube.com/watch?v=FXIrojSK3Jo" }, - }, - } as unknown as NotionBlock, - ]); - expect(result).toContain(`import ReactPlayer from "react-player";`); - expect(result).toContain( - `` - ); -}); - -test("vimeo", async () => { - setLogLevel("verbose"); - const config = { plugins: [vimeoEmbed] }; - const result = await blocksToMarkdown(config, [ - { - object: "block", - id: "39ff83a3-2fb5-4411-a715-960656a177ff", - type: "video", - video: { - caption: [], - type: "external", - external: { url: "https://vimeo.com/4613611xx" }, - }, - } as unknown as NotionBlock, - ]); - expect(result).toContain(`import ReactPlayer from "react-player";`); - expect(result).toContain( - `` - ); -}); +import { gifEmbed, imgurGifEmbed } from "./embedTweaks"; test("imgur", async () => { setLogLevel("verbose"); diff --git a/src/plugins/embedTweaks.ts b/src/plugins/embedTweaks.ts index 4111cdb..455abde 100644 --- a/src/plugins/embedTweaks.ts +++ b/src/plugins/embedTweaks.ts @@ -22,26 +22,3 @@ export const imgurGifEmbed: IPlugin = { }, ], }; -export const youtubeEmbed: IPlugin = { - name: "youtube", - regexMarkdownModifications: [ - { - regex: /\[.*\]\((.*youtube\.com\/watch.*)\)/, //youtube.com/watch - imports: [`import ReactPlayer from "react-player";`], - replacementPattern: ``, - }, - ], -}; -export const vimeoEmbed: IPlugin = { - name: "vimeo", - regexMarkdownModifications: [ - { - regex: /\[.*\]\((https:\/\/.*vimeo.*)\)/, - // we use to have the following, but the above should handle both the player an not-player urls. - //regex: /\[.*\]\((.*player\.vimeo.*)\)/gm, // player.vimeo - - imports: [`import ReactPlayer from "react-player";`], - replacementPattern: ``, - }, - ], -}; diff --git a/src/plugins/externalLinks.spec.ts b/src/plugins/externalLinks.spec.ts index 578d8f6..9ba7298 100644 --- a/src/plugins/externalLinks.spec.ts +++ b/src/plugins/externalLinks.spec.ts @@ -1,5 +1,4 @@ -import { setLogLevel, verbose } from "../log"; -import { NotionPage } from "../NotionPage"; +import { setLogLevel } from "../log"; import { oneBlockToMarkdown } from "./pluginTestRun"; import { standardExternalLinkConversion } from "./externalLinks"; @@ -15,6 +14,22 @@ test("links turned into bookmarks", async () => { expect(results.trim()).toBe("[https://github.com](https://github.com)"); }); +test("video links turned into bookmarks", async () => { + setLogLevel("debug"); + const results = await getMarkdown({ + object: "block", + type: "bookmark", + bookmark: { + caption: [], + url: "https://vimeo.com/4613611xx", + }, + }); + expect(results).toContain( + "[https://vimeo.com/4613611xx](https://vimeo.com/4613611xx)" + ); + expect(results).not.toContain(`import`); +}); + test("external link inside callout", async () => { const results = await getMarkdown({ type: "callout", diff --git a/src/plugins/pluginTypes.ts b/src/plugins/pluginTypes.ts index 7c3eb5c..065d690 100644 --- a/src/plugins/pluginTypes.ts +++ b/src/plugins/pluginTypes.ts @@ -53,7 +53,9 @@ export type IRegexMarkdownModification = { // normally, anything in code blocks is will be ignored. If you want to make changes inside of code blocks, set this to true. includeCodeBlocks?: boolean; - // If the output is creating things like react elements, you can import their definitions here + // If the output is creating things like react elements, you can append their import definitions + // to this array so they get added to the page. + // e.g. mod.imports.push(`import ReactPlayer from "react-player";`); imports?: string[]; }; @@ -74,4 +76,9 @@ export type IDocuNotionContext = { convertNotionLinkToLocalDocusaurusLink: (url: string) => string | undefined; pages: NotionPage[]; counts: ICounts; + + // If the output is creating things like react elements, you can append their import definitions + // to this array so they get added to the page. + // e.g. context.imports.push(`import ReactPlayer from "react-player";`); + imports?: string[]; }; diff --git a/src/transform.ts b/src/transform.ts index 94cb21a..20d7281 100644 --- a/src/transform.ts +++ b/src/transform.ts @@ -56,15 +56,14 @@ export async function getMarkdownFromNotionBlocks( //console.log("markdown after link fixes", markdown); // simple regex-based tweaks. These are usually related to docusaurus - const { imports, body } = await doTransformsOnMarkdown( - context, - config, - markdown - ); + const body = await doTransformsOnMarkdown(context, config, markdown); // console.log("markdown after regex fixes", markdown); // console.log("body after regex", body); + const uniqueImports = [...new Set(context.imports)]; + const imports = uniqueImports.join("\n"); + context.imports = []; // reset for next page return `${imports}\n${body}`; } @@ -106,7 +105,6 @@ async function doTransformsOnMarkdown( let body = input; //console.log("body before regex: " + body); let match; - const imports = new Set(); // eslint-disable-next-line @typescript-eslint/no-unused-vars for (const mod of regexMods) { @@ -143,15 +141,16 @@ async function doTransformsOnMarkdown( body = precedingPart + partStartingFromThisMatch.replace(original, replacement); + // add any library imports - mod.imports?.forEach(imp => imports.add(imp)); + if (!context.imports) context.imports = []; + context.imports.push(...(mod.imports || [])); } } } } logDebug("doTransformsOnMarkdown", "body after regex: " + body); - const uniqueImports = [...new Set(imports)]; - return { body, imports: [...uniqueImports].join("\n") }; + return body; } async function doNotionToMarkdown(