aboutsummaryrefslogtreecommitdiffstats
path: root/packages/shared/import-export
diff options
context:
space:
mode:
authorDaniel Wieser <me@daniel-wieser.com>2026-02-09 01:06:54 +0100
committerGitHub <noreply@github.com>2026-02-09 00:06:54 +0000
commitfbe7e3a901862e5766662735a623a5a935c87c0b (patch)
treed5a9cb347b1368e2c25ec07924d4711a5d72388e /packages/shared/import-export
parent485e9948b1d6d40df44a781c5133f6698b1f872b (diff)
downloadkarakeep-fbe7e3a901862e5766662735a623a5a935c87c0b.tar.zst
feat: Added Import for Instapaper (#2434)
* Added Instapaper import * Fixes #1444 Added Instapaper import support
Diffstat (limited to 'packages/shared/import-export')
-rw-r--r--packages/shared/import-export/parsers.ts64
1 files changed, 63 insertions, 1 deletions
diff --git a/packages/shared/import-export/parsers.ts b/packages/shared/import-export/parsers.ts
index df3d2c45..24d85c80 100644
--- a/packages/shared/import-export/parsers.ts
+++ b/packages/shared/import-export/parsers.ts
@@ -16,7 +16,8 @@ export type ImportSource =
| "karakeep"
| "linkwarden"
| "tab-session-manager"
- | "mymind";
+ | "mymind"
+ | "instapaper";
export interface ParsedBookmark {
title: string;
@@ -357,6 +358,64 @@ function parseMymindBookmarkFile(textContent: string): ParsedBookmark[] {
});
}
+function parseInstapaperBookmarkFile(textContent: string): ParsedBookmark[] {
+ const zInstapaperRecordScheme = z.object({
+ URL: z.string(),
+ Title: z.string(),
+ Selection: z.string(),
+ Folder: z.string(),
+ Timestamp: z.string(),
+ Tags: z.string(),
+ });
+
+ const zInstapaperExportScheme = z.array(zInstapaperRecordScheme);
+
+ const record = parse(textContent, {
+ columns: true,
+ skip_empty_lines: true,
+ });
+
+ const parsed = zInstapaperExportScheme.safeParse(record);
+
+ if (!parsed.success) {
+ throw new Error(
+ `CSV file contains an invalid instapaper bookmark file: ${parsed.error.toString()}`,
+ );
+ }
+
+ return parsed.data.map((record) => {
+ let content: ParsedBookmark["content"];
+ if (record.URL && record.URL.trim().length > 0) {
+ content = { type: BookmarkTypes.LINK as const, url: record.URL.trim() };
+ } else if (record.Selection && record.Selection.trim().length > 0) {
+ content = {
+ type: BookmarkTypes.TEXT as const,
+ text: record.Selection.trim(),
+ };
+ }
+
+ const addDate = parseInt(record.Timestamp);
+
+ let tags: string[] = [];
+ try {
+ const parsedTags = JSON.parse(record.Tags);
+ if (Array.isArray(parsedTags)) {
+ tags = parsedTags.map((tag) => tag.toString().trim());
+ }
+ } catch {
+ tags = [];
+ }
+
+ return {
+ title: record.Title || "",
+ content,
+ addDate,
+ tags,
+ paths: [], // TODO
+ };
+ });
+}
+
function deduplicateBookmarks(bookmarks: ParsedBookmark[]): ParsedBookmark[] {
const deduplicatedBookmarksMap = new Map<string, ParsedBookmark>();
const textBookmarks: ParsedBookmark[] = [];
@@ -428,6 +487,9 @@ export function parseImportFile(
case "mymind":
result = parseMymindBookmarkFile(textContent);
break;
+ case "instapaper":
+ result = parseInstapaperBookmarkFile(textContent);
+ break;
}
return deduplicateBookmarks(result);
}