aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2024-11-03 19:49:04 +0000
committerMohamed Bassem <me@mbassem.com>2024-11-03 19:49:04 +0000
commit7ec5746192ac59b38f7666643bad5e68ef4b46d7 (patch)
tree3ff09565bb688c1b389af6dadbcc778560763de5 /apps/web
parent7042f26b3d0af33ef86451b68b74683da78f1552 (diff)
downloadkarakeep-7ec5746192ac59b38f7666643bad5e68ef4b46d7.tar.zst
feature: Add support for importing bookmarks from Omnivore. Fixes #602
Diffstat (limited to 'apps/web')
-rw-r--r--apps/web/components/settings/ImportExport.tsx17
-rw-r--r--apps/web/lib/importBookmarkParser.ts31
2 files changed, 47 insertions, 1 deletions
diff --git a/apps/web/components/settings/ImportExport.tsx b/apps/web/components/settings/ImportExport.tsx
index b6fa6e03..7889b4d8 100644
--- a/apps/web/components/settings/ImportExport.tsx
+++ b/apps/web/components/settings/ImportExport.tsx
@@ -11,6 +11,7 @@ import {
ParsedBookmark,
parseHoarderBookmarkFile,
parseNetscapeBookmarkFile,
+ parseOmnivoreBookmarkFile,
parsePocketBookmarkFile,
} from "@/lib/importBookmarkParser";
import { cn } from "@/lib/utils";
@@ -128,7 +129,7 @@ export function ImportExportRow() {
source,
}: {
file: File;
- source: "html" | "pocket" | "hoarder";
+ source: "html" | "pocket" | "omnivore" | "hoarder";
}) => {
if (source === "html") {
return await parseNetscapeBookmarkFile(file);
@@ -136,6 +137,8 @@ export function ImportExportRow() {
return await parsePocketBookmarkFile(file);
} else if (source === "hoarder") {
return await parseHoarderBookmarkFile(file);
+ } else if (source === "omnivore") {
+ return await parseOmnivoreBookmarkFile(file);
} else {
throw new Error("Unknown source");
}
@@ -229,6 +232,18 @@ export function ImportExportRow() {
multiple={false}
className="flex items-center gap-2"
onFileSelect={(file) =>
+ runUploadBookmarkFile({ file, source: "omnivore" })
+ }
+ >
+ <Upload />
+ <p>Import Bookmarks from Omnivore export</p>
+ </FilePickerButton>
+ <FilePickerButton
+ loading={false}
+ accept=".json"
+ multiple={false}
+ className="flex items-center gap-2"
+ onFileSelect={(file) =>
runUploadBookmarkFile({ file, source: "hoarder" })
}
>
diff --git a/apps/web/lib/importBookmarkParser.ts b/apps/web/lib/importBookmarkParser.ts
index 3262b170..f3819e79 100644
--- a/apps/web/lib/importBookmarkParser.ts
+++ b/apps/web/lib/importBookmarkParser.ts
@@ -1,6 +1,7 @@
// Copied from https://gist.github.com/devster31/4e8c6548fd16ffb75c02e6f24e27f9b9
import * as cheerio from "cheerio";
import { parse } from "csv-parse/sync";
+import { z } from "zod";
import { BookmarkTypes } from "@hoarder/shared/types/bookmarks";
@@ -109,3 +110,33 @@ export async function parseHoarderBookmarkFile(
};
});
}
+
+export async function parseOmnivoreBookmarkFile(
+ file: File,
+): Promise<ParsedBookmark[]> {
+ const textContent = await file.text();
+ const zOmnivoreExportSchema = z.array(
+ z.object({
+ title: z.string(),
+ url: z.string(),
+ labels: z.array(z.string()),
+ savedAt: z.coerce.date(),
+ }),
+ );
+
+ const parsed = zOmnivoreExportSchema.safeParse(JSON.parse(textContent));
+ if (!parsed.success) {
+ throw new Error(
+ `The uploaded JSON file contains an invalid omnivore bookmark file: ${parsed.error.toString()}`,
+ );
+ }
+
+ return parsed.data.map((bookmark) => {
+ return {
+ title: bookmark.title ?? "",
+ content: { type: BookmarkTypes.LINK as const, url: bookmark.url },
+ tags: bookmark.labels,
+ addDate: bookmark.savedAt.getTime() / 1000,
+ };
+ });
+}