forked from niekcandaele/obsidian-local-images
-
Notifications
You must be signed in to change notification settings - Fork 42
/
contentProcessor.ts
128 lines (109 loc) · 3.17 KB
/
contentProcessor.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { URL } from "url";
import path from "path";
import { App, DataAdapter } from "obsidian";
import {
isUrl,
downloadImage,
fileExtByContent,
cleanFileName,
pathJoin,
} from "./utils";
import {
FILENAME_TEMPLATE,
MAX_FILENAME_INDEX,
FILENAME_ATTEMPTS,
} from "./config";
import { linkHashes } from "./linksHash";
export function imageTagProcessor(app: App, mediaDir: string) {
async function processImageTag(match: string, anchor: string, link: string) {
if (!isUrl(link)) {
return match;
}
try {
const fileData = await downloadImage(link);
// when several images refer to the same file they can be partly
// failed to download because file already exists, so try to resuggest filename several times
let attempt = 0;
while (attempt < FILENAME_ATTEMPTS) {
try {
const { fileName, needWrite } = await chooseFileName(
app.vault.adapter,
mediaDir,
anchor,
link,
fileData
);
if (needWrite && fileName) {
await app.vault.createBinary(fileName, fileData);
}
if (fileName) {
return `![${anchor}](${fileName})`;
} else {
return match;
}
} catch (error) {
if (error.message === "File already exists.") {
attempt++;
} else {
throw error;
}
}
}
return match;
} catch (error) {
console.warn("Image processing failed: ", error);
return match;
}
}
return processImageTag;
}
async function chooseFileName(
adapter: DataAdapter,
dir: string,
baseName: string,
link: string,
contentData: ArrayBuffer
): Promise<{ fileName: string; needWrite: boolean }> {
const fileExt = await fileExtByContent(contentData);
if (!fileExt) {
return { fileName: "", needWrite: false };
}
// if there is no anchor try get file name from url
if (!baseName) {
const parsedUrl = new URL(link);
baseName = path.basename(parsedUrl.pathname);
}
// if there is no part for file name from url use name template
if (!baseName) {
baseName = FILENAME_TEMPLATE;
}
// if filename already ends with correct extension, remove it to work with base name
if (baseName.endsWith(`.${fileExt}`)) {
baseName = baseName.slice(0, -1 * (fileExt.length + 1));
}
baseName = cleanFileName(baseName);
let fileName = "";
let needWrite = true;
let index = 0;
while (!fileName && index < MAX_FILENAME_INDEX) {
const suggestedName = index
? pathJoin(dir, `${baseName}-${index}.${fileExt}`)
: pathJoin(dir, `${baseName}.${fileExt}`);
if (await adapter.exists(suggestedName, false)) {
linkHashes.ensureHashGenerated(link, contentData);
const fileData = await adapter.readBinary(suggestedName);
if (linkHashes.isSame(link, fileData)) {
fileName = suggestedName;
needWrite = false;
}
} else {
fileName = suggestedName;
}
index++;
}
if (!fileName) {
throw new Error("Failed to generate file name for media file.");
}
linkHashes.ensureHashGenerated(link, contentData);
return { fileName, needWrite };
}