인코딩 문제 해결

This commit is contained in:
kim365my 2024-04-08 15:18:31 +09:00
parent d1afcdea7a
commit b943ba02c9
5 changed files with 58 additions and 26 deletions

23
package-lock.json generated
View File

@ -1,13 +1,16 @@
{ {
"name": "obsidian-sample-plugin", "name": "link-thumbnail-plugin",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "obsidian-sample-plugin", "name": "link-thumbnail-plugin",
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": {
"iconv-lite": "^0.6.3"
},
"devDependencies": { "devDependencies": {
"@codemirror/commands": "6.0.0", "@codemirror/commands": "6.0.0",
"@codemirror/language": "https://github.com/lishid/cm-language", "@codemirror/language": "https://github.com/lishid/cm-language",
@ -1587,6 +1590,17 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@ -2087,6 +2101,11 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/semver": { "node_modules/semver": {
"version": "7.6.0", "version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",

View File

@ -12,18 +12,21 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@codemirror/commands": "6.0.0",
"@codemirror/language": "https://github.com/lishid/cm-language",
"@codemirror/search": "6.0.0",
"@codemirror/view": "6.0.0",
"@types/node": "^16.11.6", "@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0", "@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0", "@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0", "builtin-modules": "3.3.0",
"compare-versions": "^4.1.3",
"esbuild": "0.17.3", "esbuild": "0.17.3",
"obsidian": "latest", "obsidian": "latest",
"tslib": "2.4.0", "tslib": "2.4.0",
"typescript": "4.7.4", "typescript": "4.7.4"
"compare-versions": "^4.1.3", },
"@codemirror/search": "6.0.0", "dependencies": {
"@codemirror/view": "6.0.0", "iconv-lite": "^0.6.3"
"@codemirror/commands": "6.0.0",
"@codemirror/language": "https://github.com/lishid/cm-language"
} }
} }

View File

@ -59,7 +59,7 @@ class StatefulDecorationSet {
linkEl.addEventListener("click", (e) => e.stopPropagation()); linkEl.addEventListener("click", (e) => e.stopPropagation());
} }
}); });
deco = this.decoCache[token.value] = Decoration.replace({widget: new EmojiWidget(div), block: true}); deco = this.decoCache[token.value] = Decoration.replace({widget: new ogLinkWidget(div), block: true});
} }
decorations.push(deco.range(token.from, token.to)); decorations.push(deco.range(token.from, token.to));
} }
@ -147,7 +147,7 @@ function defineStatefulDecoration(): {
return {update, field}; return {update, field};
} }
class EmojiWidget extends WidgetType { class ogLinkWidget extends WidgetType {
private readonly source: HTMLDivElement; private readonly source: HTMLDivElement;
constructor(source: HTMLDivElement) { constructor(source: HTMLDivElement) {
@ -155,7 +155,7 @@ class EmojiWidget extends WidgetType {
this.source = source; this.source = source;
} }
eq(other: EmojiWidget) { eq(other: ogLinkWidget) {
return other == this; return other == this;
} }

View File

@ -1,4 +1,5 @@
import { requestUrl } from "obsidian"; import { requestUrl } from "obsidian";
import { decode } from 'iconv-lite';
// url 정규식 // url 정규식
const urlRegex = new RegExp("^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"); const urlRegex = new RegExp("^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$");
@ -6,17 +7,30 @@ const urlRegex = new RegExp("^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|
export async function LinkThumbnailWidgetParams(url: string) { export async function LinkThumbnailWidgetParams(url: string) {
const urlArr = urlRegex.exec(url); const urlArr = urlRegex.exec(url);
if (urlArr) { if (urlArr) {
try { try {
const response = await requestUrl(url);
const contextType = response.headers["content-type"]; const response = await requestUrl({
if (contextType.toLocaleLowerCase().trim() === "text/html;charset=ms949") { url:url,
return null; throw: true
});
if (response && response.headers && response.headers['content-type'] && !response.headers['content-type']?.includes('text/')) {
throw new Error('Page must return a header content-type with text/');
} }
const htmlString = response.text; // 인코딩 문제 해결
const bodyArrayBuffer = response.arrayBuffer;
const charset = response.headers["content-type"].replace("text/html;charset=","").toLowerCase();
let body;
if (charset.trim() === "utf-8") {
body = Buffer.from(bodyArrayBuffer).toString('utf-8');
} else {
body = decode(Buffer.from(bodyArrayBuffer), charset);
}
const parser = new DOMParser(); const parser = new DOMParser();
const document = parser.parseFromString(htmlString, 'text/html'); const document = parser.parseFromString(body, 'text/html');
const ogTitle = document.querySelector("meta[property='og:title']")?.getAttribute("content") || document.querySelector("title")?.textContent || ""; const ogTitle = document.querySelector("meta[property='og:title']")?.getAttribute("content") || document.querySelector("title")?.textContent || "";
const ogDescription = document.querySelector("meta[property='og:description']")?.getAttribute("content") || ""; const ogDescription = document.querySelector("meta[property='og:description']")?.getAttribute("content") || "";
let ogImage = document.querySelector("meta[property='og:image']")?.getAttribute("content") || ""; let ogImage = document.querySelector("meta[property='og:image']")?.getAttribute("content") || "";
@ -39,9 +53,8 @@ export async function LinkThumbnailWidgetParams(url: string) {
` `
return result; return result;
} catch (error) { } catch (error) {
console.error(error); throw new Error(error);
return null;
} }
} }
return null return null
} }

View File

@ -16,10 +16,7 @@ export class PostProcessor {
// 링크 변환 // 링크 변환
const linkEls:Element[] = element.findAll("a.external-link:not(.cm-formatting, .markdown-rendered)"); const linkEls:Element[] = element.findAll("a.external-link:not(.cm-formatting, .markdown-rendered)");
for (const linkEl of linkEls) { for (const linkEl of linkEls) {
// dataview 클래스를 가진 부모 요소를 확인합니다.
if (linkEl.closest(".dataview") !== null) {
continue;
}
const url = linkEl.innerHTML; const url = linkEl.innerHTML;
const params = await LinkThumbnailWidgetParams(url); const params = await LinkThumbnailWidgetParams(url);
if (params != null) { if (params != null) {