Skip to content

Commit 3b72c79

Browse files
committed
feat: enhance image handling by verifying image URLs and allowing logo images in headers
1 parent 1696f28 commit 3b72c79

File tree

4 files changed

+102
-32
lines changed

4 files changed

+102
-32
lines changed

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "obsidian-link-embed",
33
"name": "Link Embed",
4-
"version": "2.8.3",
4+
"version": "2.8.4",
55
"minAppVersion": "0.12.0",
66
"description": "This plugin auto-fetches page metadata to embed Notion-style link preview cards.",
77
"author": "SErAphLi",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "obsidian-link-embed",
3-
"version": "2.8.3",
3+
"version": "2.8.4",
44
"description": "This plugin auto-fetches page metadata to embed Notion-style link preview cards.",
55
"main": "main.js",
66
"scripts": {

src/parsers/LocalParser.ts

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { requestUrl } from 'obsidian';
22
import { Parser, ParsedLinkData } from './parser';
33
import { ConcurrencyLimiter } from '../utils/concurrencyLimiter';
4+
import { getImageDimensions } from './utils/imageUtils';
45

56
const electronPkg = require('electron');
67

@@ -46,33 +47,88 @@ export class LocalParser extends Parser {
4647
}
4748

4849
meetsCriteria(element: Element): boolean {
49-
//If inline - display:none
50+
// If inline - display:none
5051
if (/display:\s*none/.test(element.getAttribute('style'))) {
5152
return false;
5253
}
5354

54-
//hide images in navigation bar/header
55+
// Check for logo images which should be allowed even in headers
56+
if (element instanceof HTMLImageElement) {
57+
const src = element.getAttribute('src') || '';
58+
const alt = element.getAttribute('alt') || '';
59+
60+
// Allow images that are likely logos based on src or alt text
61+
if (
62+
src.toLowerCase().includes('logo') ||
63+
alt.toLowerCase().includes('logo') ||
64+
src.endsWith('.svg') // SVGs are often used for logos
65+
) {
66+
this.debugLog('[Link Embed] Image - Allowing logo image:', src);
67+
return true;
68+
}
69+
}
70+
71+
// Hide images in navigation bar/header, unless they're potential logos
5572
let contains_header = false;
5673
element.classList.forEach((val) => {
57-
if (val.toLowerCase().contains('header')) {
74+
if (val.toLowerCase().includes('header')) {
5875
contains_header = true;
5976
}
6077
});
61-
if (element.id.toLowerCase().contains('header') || contains_header) {
78+
79+
if (
80+
(element.id.toLowerCase().includes('header') || contains_header) &&
81+
!(element instanceof HTMLImageElement)
82+
) {
6283
return false;
6384
}
6485

65-
//recurse until <html>
86+
// Recurse until <html>
6687
if (element.parentElement != null) {
6788
return this.meetsCriteria(element.parentElement);
6889
}
6990

7091
return true;
7192
}
7293

73-
getImage(doc: Document, url: URL): string {
94+
// Method to verify if an image URL can be loaded
95+
private async verifyImageUrl(
96+
imgUrl: string,
97+
failedUrls: Set<string>,
98+
): Promise<string | null> {
99+
if (failedUrls.has(imgUrl)) return null;
100+
101+
try {
102+
// Try to get image dimensions - this will verify the image loads
103+
const dimensions = await getImageDimensions(imgUrl);
104+
if (dimensions) {
105+
this.debugLog(
106+
'[Link Embed] Image - Successfully verified image loads:',
107+
imgUrl,
108+
);
109+
return imgUrl;
110+
} else {
111+
this.debugLog(
112+
'[Link Embed] Image - Image failed to load properly:',
113+
imgUrl,
114+
);
115+
failedUrls.add(imgUrl);
116+
}
117+
} catch (error) {
118+
this.debugError(
119+
'[Link Embed] Image - Failed to load image:',
120+
imgUrl,
121+
error,
122+
);
123+
failedUrls.add(imgUrl);
124+
}
125+
return null;
126+
}
127+
128+
async getImage(doc: Document, url: URL): Promise<string> {
74129
const baseEl = doc.querySelector('base[href]') as HTMLBaseElement;
75130
const base = (baseEl && baseEl.href) || url.href;
131+
const failedUrls = new Set<string>(); // Track failed URLs
76132

77133
this.debugLog('[Link Embed] Image - Looking for image for:', url.href);
78134
this.debugLog('[Link Embed] Image - Base URL:', base);
@@ -81,26 +137,30 @@ export class LocalParser extends Parser {
81137
const og = doc.querySelector<HTMLMetaElement>(
82138
'head meta[property="og:image"]',
83139
);
84-
if (og) {
140+
if (og && og.content) {
85141
this.debugLog(
86142
'[Link Embed] Image - Found Open Graph image:',
87143
og.content,
88144
);
89-
if (og.content) {
90-
try {
91-
const resolvedUrl = new URL(og.content, base).href;
92-
this.debugLog(
93-
'[Link Embed] Image - Resolved OG image URL:',
94-
resolvedUrl,
95-
);
96-
return resolvedUrl;
97-
} catch (error) {
98-
this.debugError(
99-
'[Link Embed] Image - Error resolving OG image URL:',
100-
error,
101-
);
102-
return og.content;
103-
}
145+
try {
146+
const resolvedUrl = new URL(og.content, base).href;
147+
this.debugLog(
148+
'[Link Embed] Image - Resolved OG image URL:',
149+
resolvedUrl,
150+
);
151+
152+
// Verify OG image loads
153+
const verifiedUrl = await this.verifyImageUrl(
154+
resolvedUrl,
155+
failedUrls,
156+
);
157+
if (verifiedUrl) return verifiedUrl;
158+
} catch (error) {
159+
this.debugError(
160+
'[Link Embed] Image - Error resolving OG image URL:',
161+
error,
162+
);
163+
// No longer trying with original content if URL resolution fails
104164
}
105165
}
106166

@@ -124,10 +184,10 @@ export class LocalParser extends Parser {
124184
this.debugLog(
125185
`[Link Embed] Image - Found ${imgs.length} images for selector "${sel}"`,
126186
);
187+
127188
for (const img of imgs) {
128-
if (!this.meetsCriteria(img)) {
129-
continue;
130-
}
189+
if (!this.meetsCriteria(img)) continue;
190+
131191
const src = img.getAttribute('src');
132192
if (src) {
133193
this.debugLog(
@@ -140,19 +200,27 @@ export class LocalParser extends Parser {
140200
'[Link Embed] Image - Resolved image URL:',
141201
resolvedUrl,
142202
);
143-
return resolvedUrl;
203+
204+
// Verify image loads
205+
const verifiedUrl = await this.verifyImageUrl(
206+
resolvedUrl,
207+
failedUrls,
208+
);
209+
if (verifiedUrl) return verifiedUrl;
144210
} catch (error) {
145211
this.debugError(
146212
'[Link Embed] Image - Error resolving image URL:',
147213
error,
148214
);
149-
return src;
215+
// No longer trying with original src if URL resolution fails
150216
}
151217
}
152218
}
153219
}
154220

155-
this.debugLog('[Link Embed] Image - No suitable image found');
221+
this.debugLog(
222+
'[Link Embed] Image - No suitable image found or all images failed to load',
223+
);
156224
return '';
157225
}
158226

@@ -377,9 +445,10 @@ export class LocalParser extends Parser {
377445
let uRL = new URL(url);
378446
this.debugLog('[Link Embed] Doc:', doc);
379447
let title = this.getTitle(doc, uRL);
380-
let image = this.getImage(doc, uRL);
381448
let description = this.getDescription(doc);
382449
let favicon = this.getFavicon(doc, uRL);
450+
// Get image - now this is an async call
451+
let image = await this.getImage(doc, uRL);
383452

384453
// 1. First, process the raw data to extract basic information
385454
let processedData = this.process({

versions.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,6 @@
4848
"2.8.0": "0.12.0",
4949
"2.8.1": "0.12.0",
5050
"2.8.2": "0.12.0",
51-
"2.8.3": "0.12.0"
51+
"2.8.3": "0.12.0",
52+
"2.8.4": "0.12.0"
5253
}

0 commit comments

Comments
 (0)