Skip to content

Commit daa60c3

Browse files
committed
Add a command to import metadata only from arxiv
1 parent 021433e commit daa60c3

File tree

6 files changed

+115
-53
lines changed

6 files changed

+115
-53
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ plugins in Obsidian setting and that's it.
1616

1717
## How to Use
1818

19-
Press `Ctrl+P` in Obsidian, and select `Paper Importer: Import PDF from arXiv`. In the popup,
20-
enter the arXiv ID or URL of the paper (e.g., `1703.06870` or `https://arxiv.org/abs/1703.06870`)
21-
you would like to import. Press the enter key to confirm. The PDF and its metadata will then
22-
be saved to selected folders. Those save destinations can be configured via the setting panel.
19+
Press `Ctrl+P` in Obsidian to open the command palette, then choose one of the following options:
20+
21+
- **`Paper Importer: Import metadata and PDF from arXiv`** - Downloads both the paper's metadata and PDF file
22+
- **`Paper Importer: Import metadata only from arXiv`** - Imports only the paper's metadata without downloading the PDF
23+
24+
In the popup, enter the arXiv ID or URL of the paper (e.g., `1703.06870` or `https://arxiv.org/abs/1703.06870`) you would like to import. Press the enter key to confirm. The metadata (and PDF if selected) will then be saved to your configured folders. Save destinations can be configured via the plugin settings panel.
2325

2426
## Template Customization
2527

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "paper_importer",
33
"name": "Paper Importer",
4-
"version": "1.3.0",
4+
"version": "1.4.0",
55
"minAppVersion": "1.7.6",
66
"description": "Import papers from arXiv with one click.",
77
"author": "Zhe Chen",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "obsidian_paper_importer",
3-
"version": "1.3.0",
3+
"version": "1.4.0",
44
"description": "A plugin to help you import papers from arXiv",
55
"main": "main.js",
66
"scripts": {

src/component/ImportDialog.svelte

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
interface Props {
33
onkeypress?: (event: KeyboardEvent, paperUri: string) => void;
44
states: Record<string, any>;
5+
downloadPdf?: boolean;
56
}
6-
let { onkeypress, states }: Props = $props();
7+
let { onkeypress, states, downloadPdf = true }: Props = $props();
78
89
let paperUri: string = $state("");
910
let logContainer: HTMLDivElement | null = $state(null);
@@ -62,10 +63,15 @@
6263
}
6364
</script>
6465

65-
<h4>Import Paper from arXiv</h4>
66+
<h4>
67+
{downloadPdf ? "Import Paper from arXiv" : "Import Metadata from arXiv"}
68+
</h4>
6669
<p style="margin-bottom: 20px; color: grey;">
67-
Enter the arXiv ID or URL of the paper you want to import. Press Enter to
68-
confirm.
70+
Enter the arXiv ID or URL of the paper you want to import.
71+
{downloadPdf
72+
? "The PDF will be downloaded and metadata will be created."
73+
: "Only metadata will be imported, no PDF download."}
74+
Press Enter to confirm.
6975
</p>
7076

7177
<input
@@ -79,7 +85,7 @@
7985
/>
8086

8187
<!-- Download Progress Bar -->
82-
{#if states.downloadProgress !== undefined && states.downloadProgress > 0}
88+
{#if downloadPdf && states.downloadProgress !== undefined && states.downloadProgress > 0}
8389
<div style="margin-top: 20px;">
8490
<div
8591
style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;"

src/import_modal.svelte.ts

Lines changed: 81 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,21 @@ import type { PaperImporterPluginSettings } from "./setting_tab";
99

1010
export class ImportModal extends Modal {
1111
settings: PaperImporterPluginSettings;
12+
downloadPdf: boolean;
1213
importDialog: ReturnType<typeof ImportDialog> | null = null;
1314
states: Record<string, any> = $state({
1415
logs: [],
1516
downloadProgress: 0,
1617
});
1718

18-
constructor(app: App, settings: PaperImporterPluginSettings) {
19+
constructor(
20+
app: App,
21+
settings: PaperImporterPluginSettings,
22+
downloadPdf: boolean = true
23+
) {
1924
super(app);
2025
this.settings = settings;
26+
this.downloadPdf = downloadPdf;
2127
}
2228

2329
onOpen() {
@@ -27,12 +33,18 @@ export class ImportModal extends Modal {
2733
target: contentEl,
2834
props: {
2935
states: this.states,
36+
downloadPdf: this.downloadPdf,
3037
onkeypress: async (e: KeyboardEvent, paperUri: string) => {
3138
if (e.key === "Enter") {
3239
// Reset progress and messages
3340
this.states.downloadProgress = 0;
3441
this.states.logs.length = 0;
35-
this.states.logs.push(["info", "Importing paper..."]);
42+
this.states.logs.push([
43+
"info",
44+
this.downloadPdf
45+
? "Importing paper..."
46+
: "Importing metadata...",
47+
]);
3648

3749
let arxivId: string;
3850
try {
@@ -56,11 +68,15 @@ export class ImportModal extends Modal {
5668
return;
5769
}
5870

59-
this.states.logs.push([
60-
"success",
61-
"Paper imported successfully!",
62-
]);
63-
new Notice("Paper imported successfully!");
71+
// Add a simple success message
72+
this.states.logs.push(["success", "Import completed!"]);
73+
74+
// Show a notice based on the last log entry
75+
const lastLog =
76+
this.states.logs[this.states.logs.length - 1];
77+
if (lastLog && lastLog[0] === "success") {
78+
new Notice(lastLog[1]);
79+
}
6480

6581
this.close();
6682
}
@@ -78,6 +94,19 @@ export class ImportModal extends Modal {
7894
async searchAndImportPaper(arxivId: string): Promise<[string, string]> {
7995
const paper = await searchPaper(arxivId);
8096

97+
let pdfPath = "";
98+
99+
if (this.downloadPdf) {
100+
pdfPath = await this.downloadPdfFile(paper);
101+
}
102+
103+
const notePath = await this.createNoteFromPaper(paper, pdfPath);
104+
105+
this.states.downloadProgress = 100;
106+
return [notePath, pdfPath];
107+
}
108+
109+
private async downloadPdfFile(paper: any): Promise<string> {
81110
const pdfFolder = normalizePath(this.settings.pdfFolder);
82111

83112
let pdfFolderPath = this.app.vault.getFolderByPath(pdfFolder)!;
@@ -93,8 +122,14 @@ export class ImportModal extends Modal {
93122
// Check if PDF already exists
94123
const pdfExists = await this.app.vault.adapter.exists(pdfPath);
95124
if (pdfExists) {
96-
this.states.logs.push(["error", `PDF already exists: ${pdfPath}`]);
97-
throw new Error(`PDF already exists: ${pdfPath}`);
125+
this.states.logs.push([
126+
"warn",
127+
`PDF already exists: ${pdfPath}. Skipping download.`,
128+
]);
129+
new Notice(
130+
`PDF already exists: ${pdfFilename}. Using existing file.`
131+
);
132+
return pdfPath; // Return the existing PDF path instead of throwing an error
98133
}
99134

100135
// Download PDF with progress tracking
@@ -151,7 +186,6 @@ export class ImportModal extends Modal {
151186
pdfPath,
152187
arrayBuffer.buffer
153188
);
154-
this.states.downloadProgress = 100;
155189
} catch (error) {
156190
this.states.downloadProgress = 0;
157191
this.states.logs.push([
@@ -162,7 +196,13 @@ export class ImportModal extends Modal {
162196
}
163197

164198
this.states.logs.push(["info", `PDF downloaded: ${pdfPath}`]);
199+
return pdfPath;
200+
}
165201

202+
private async createNoteFromPaper(
203+
paper: any,
204+
pdfPath: string
205+
): Promise<string> {
166206
const noteFolder = normalizePath(this.settings.noteFolder);
167207

168208
let noteFolderPath = this.app.vault.getFolderByPath(noteFolder)!;
@@ -180,16 +220,34 @@ export class ImportModal extends Modal {
180220
// Check if note already exists
181221
const noteExists = await this.app.vault.adapter.exists(notePath);
182222
if (noteExists) {
183-
this.states.logs.push([
184-
"error",
185-
`Note already exists: ${notePath}`,
186-
]);
187-
throw new Error(`Note already exists: ${notePath}`);
223+
this.states.logs.push(["warn", `Note already exists: ${notePath}`]);
224+
new Notice(
225+
`Note already exists: ${noteFilename}. Opening existing note.`
226+
);
227+
return notePath; // Return the existing note path instead of creating a new one
188228
}
189229

190-
// Load template: external file if specified, otherwise use default
191-
let template: string;
230+
const template = await this.loadTemplate();
231+
232+
// Determine PDF link format based on whether we downloaded the PDF
233+
const pdfLink = pdfPath ? `"[[${pdfPath}]]"` : `"${paper.pdfUrl}"`;
234+
235+
const noteContent = template
236+
.replace(/{{\s*paper_id\s*}}/g, paper.paperId)
237+
.replace(/{{\s*title\s*}}/g, `"${paper.title}"`)
238+
.replace(/{{\s*authors\s*}}/g, paper.authors.join(", "))
239+
.replace(/{{\s*date\s*}}/g, paper.date)
240+
.replace(/{{\s*abstract\s*}}/g, `"${paper.abstract}"`)
241+
.replace(/{{\s*comments\s*}}/g, `"${paper.comments}"`)
242+
.replace(/{{\s*pdf_link\s*}}/g, pdfLink);
243+
244+
await this.app.vault.adapter.write(notePath, noteContent);
245+
246+
this.states.logs.push(["info", `Note created: ${notePath}`]);
247+
return notePath;
248+
}
192249

250+
private async loadTemplate(): Promise<string> {
193251
if (
194252
this.settings.templateFilePath &&
195253
this.settings.templateFilePath.trim()
@@ -202,43 +260,30 @@ export class ImportModal extends Modal {
202260
templatePath
203261
);
204262
if (exists) {
205-
template = await this.app.vault.adapter.read(templatePath);
263+
const template = await this.app.vault.adapter.read(
264+
templatePath
265+
);
206266
this.states.logs.push([
207267
"info",
208268
`Using custom template: ${templatePath}`,
209269
]);
270+
return template;
210271
} else {
211272
this.states.logs.push([
212273
"warn",
213274
`Template file not found: ${templatePath}, using default template`,
214275
]);
215-
template = this.getDefaultTemplate();
216276
}
217277
} catch (error) {
218278
this.states.logs.push([
219279
"error",
220280
`Failed to read template file: ${error.message}, using default template`,
221281
]);
222-
template = this.getDefaultTemplate();
223282
}
224-
} else {
225-
template = this.getDefaultTemplate();
226-
this.states.logs.push(["info", "Using default template"]);
227283
}
228284

229-
const noteContent = template
230-
.replace(/{{\s*paper_id\s*}}/g, paper.paperId)
231-
.replace(/{{\s*title\s*}}/g, `"${paper.title}"`)
232-
.replace(/{{\s*authors\s*}}/g, paper.authors.join(", "))
233-
.replace(/{{\s*date\s*}}/g, paper.date)
234-
.replace(/{{\s*abstract\s*}}/g, `"${paper.abstract}"`)
235-
.replace(/{{\s*comments\s*}}/g, `"${paper.comments}"`)
236-
.replace(/{{\s*pdf_link\s*}}/g, `"[[${pdfPath}]]"`);
237-
await this.app.vault.adapter.write(notePath, noteContent);
238-
239-
this.states.logs.push(["info", `Note created: ${notePath}`]);
240-
241-
return [notePath, pdfPath];
285+
this.states.logs.push(["info", "Using default template"]);
286+
return this.getDefaultTemplate();
242287
}
243288

244289
extractArxivId(text: string): string {

src/main.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
1-
import { Editor, MarkdownView, Modal, Notice, Plugin } from "obsidian";
1+
import { Plugin } from "obsidian";
2+
import { ImportModal } from "./import_modal.svelte";
23
import {
34
DEFAULT_SETTINGS,
45
type PaperImporterPluginSettings,
56
PaperImporterSettingTab,
67
} from "./setting_tab";
7-
import { ImportModal } from "./import_modal.svelte";
88

99
export default class PaperImporterPlugin extends Plugin {
1010
settings: PaperImporterPluginSettings;
1111

1212
async onload() {
1313
await this.loadSettings();
1414

15-
// This adds an editor command that can perform some operation on the current editor instance
15+
// This adds a command to import metadata and download PDF from arXiv
1616
this.addCommand({
1717
id: "import_pdf_from_arxiv",
18-
name: "Import PDF from arXiv",
18+
name: "Import metadata and PDF from arXiv",
19+
callback: () => {
20+
new ImportModal(this.app, this.settings, true).open();
21+
},
22+
});
23+
24+
// This adds a command to import only metadata without downloading PDF
25+
this.addCommand({
26+
id: "import_metadata_from_arxiv",
27+
name: "Import metadata only from arXiv",
1928
callback: () => {
20-
new ImportModal(this.app, this.settings).open();
29+
new ImportModal(this.app, this.settings, false).open();
2130
},
2231
});
2332

@@ -31,7 +40,7 @@ export default class PaperImporterPlugin extends Plugin {
3140
this.settings = Object.assign(
3241
{},
3342
DEFAULT_SETTINGS,
34-
await this.loadData(),
43+
await this.loadData()
3544
);
3645
}
3746

0 commit comments

Comments
 (0)