Skip to content

Fixed YouTube bookmark metadata#29114

Open
cyphercodes wants to merge 1 commit into
TryGhost:mainfrom
cyphercodes:fix/youtube-bookmark-metadata
Open

Fixed YouTube bookmark metadata#29114
cyphercodes wants to merge 1 commit into
TryGhost:mainfrom
cyphercodes:fix/youtube-bookmark-metadata

Conversation

@cyphercodes

Copy link
Copy Markdown

Fixes #24741

Why:
YouTube bookmark requests can receive generic page metadata (and no thumbnail) from the fetched page, even though YouTube's allowlisted oEmbed endpoint has the video title, author, provider, and thumbnail. Missing image URLs were also still sent through bookmark image processing, which produced Failed to parse URL from null logs.

What:

  • Builds explicit bookmark cards from allowlisted known-provider oEmbed metadata without exposing provider-supplied embed HTML.
  • Skips absent bookmark icon/thumbnail URLs before image processing while keeping the default icon fallback.
  • Adds unit coverage for YouTube bookmark metadata and missing image URLs.

Why Ghost users or developers need this:
YouTube bookmark cards now use video metadata instead of a generic YouTube card and avoid noisy null-image URL errors.

Tests:

  • pnpm test:single test/unit/server/services/oembed/oembed-service.test.js
  • pnpm exec eslint core/server/services/oembed/oembed-service.js test/unit/server/services/oembed/oembed-service.test.js
  • git diff --check

Please check your PR against these items:

  • I've read and followed the Contributor Guide
  • I've explained my change
  • I've written an automated test to prove my change works

Fixes TryGhost#24741

Why: YouTube pages can return generic localized metadata to server-side bookmark requests even when the allowlisted oEmbed endpoint has the video details.

What: Use known-provider oEmbed metadata for explicit bookmark cards and skip missing image URLs before processing them.
@cursor

cursor Bot commented Jul 5, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

This change modifies the oEmbed service's bookmark card metadata handling. A DEFAULT_BOOKMARK_ICON constant is introduced and used as a fallback icon across bookmark flows. processImageFromUrl now handles missing/undefined URLs by returning null early. A new fetchBookmarkDataFromKnownProvider method leverages known-provider URL detection to build bookmark metadata (e.g., for YouTube), with conditional thumbnail processing. fetchBookmarkData's icon/thumbnail logic is reworked to conditionally process images and fall back to the default icon on failure or absence. fetchOembedDataFromUrl now attempts the known-provider path first for type === 'bookmark' requests. Corresponding unit tests are added covering these new behaviors.

Sequence Diagram(s)

(See hidden review stack artifact for diagram.)

Related issues

Possibly related PRs

  • TryGhost/Ghost#28768: Both PRs modify fetchBookmarkData's bookmark icon/fallback selection logic in oembed-service.js.
  • TryGhost/Ghost#28939: Both PRs touch bookmark-card icon/thumbnail handling and default icon fallback logic in fetchOembedDataFromUrl.
  • TryGhost/Ghost#28961: Both PRs modify the bookmark metadata flow in fetchBookmarkData and extend the same oembed-service unit tests.

Suggested reviewers: 9larsons

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: YouTube bookmark metadata handling was fixed.
Description check ✅ Passed The description is directly related to the changeset and accurately explains the bookmark metadata fix and tests.
Linked Issues check ✅ Passed The code addresses #24741 by using YouTube oEmbed metadata for bookmark cards and adding tests for missing image URLs.
Out of Scope Changes check ✅ Passed The changes stay focused on YouTube bookmark metadata and null image handling, with no clear unrelated additions.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
ghost/core/test/unit/server/services/oembed/oembed-service.test.js (1)

315-329: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a test for the known-provider failure/fallback path.

Coverage is good for the success case and the missing-image case, but there's no test exercising fetchBookmarkDataFromKnownProvider when knownProvider throws (or returns no title) to confirm fetchOembedDataFromUrl correctly falls back to page scraping for a bookmark request.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ghost/core/test/unit/server/services/oembed/oembed-service.test.js` around
lines 315 - 329, Add a unit test for the bookmark fallback path in
oembed-service.test.js that exercises fetchOembedDataFromUrl when
fetchBookmarkDataFromKnownProvider fails or returns no title, so it falls back
to page scraping for bookmark requests. Use the existing oembedService and stub
fetchBookmarkDataFromKnownProvider to throw or return an incomplete result, then
assert the fallback result comes from page scraping and that bookmark metadata
is still populated correctly. Keep the test alongside the existing
fetchBookmarkData coverage and reference fetchOembedDataFromUrl and
fetchBookmarkDataFromKnownProvider to locate the behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ghost/core/core/server/services/oembed/oembed-service.js`:
- Around line 192-244: The thumbnail image handling in
fetchBookmarkDataFromKnownProvider and fetchBookmarkData is inconsistent with
the icon fallback and can leave raw provider URLs exposed when
processImageFromUrl fails. Update the thumbnail processing path so failures
clear or replace metadata.thumbnail with a safe fallback instead of keeping the
original URL, matching the DEFAULT_BOOKMARK_ICON behavior used for icons. While
touching fetchBookmarkData and fetchBookmarkDataFromKnownProvider, consider
extracting the repeated “process image with fallback” logic into a shared helper
to remove duplication and keep the thumbnail/icon behavior consistent.

---

Nitpick comments:
In `@ghost/core/test/unit/server/services/oembed/oembed-service.test.js`:
- Around line 315-329: Add a unit test for the bookmark fallback path in
oembed-service.test.js that exercises fetchOembedDataFromUrl when
fetchBookmarkDataFromKnownProvider fails or returns no title, so it falls back
to page scraping for bookmark requests. Use the existing oembedService and stub
fetchBookmarkDataFromKnownProvider to throw or return an incomplete result, then
assert the fallback result comes from page scraping and that bookmark metadata
is still populated correctly. Keep the test alongside the existing
fetchBookmarkData coverage and reference fetchOembedDataFromUrl and
fetchBookmarkDataFromKnownProvider to locate the behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 11974741-5297-4601-918a-7c0105cc664a

📥 Commits

Reviewing files that changed from the base of the PR and between 7e35129 and 715b168.

📒 Files selected for processing (2)
  • ghost/core/core/server/services/oembed/oembed-service.js
  • ghost/core/test/unit/server/services/oembed/oembed-service.test.js

Comment on lines +192 to +244
/**
* Build bookmark metadata from a known oEmbed provider without exposing the
* provider-supplied HTML that embed cards use.
*
* @param {string} url
* @returns {Promise<Object|undefined>}
*/
async fetchBookmarkDataFromKnownProvider(url) {
const {url: providerUrl, provider} = findUrlWithProvider(url);
if (!provider) {
return;
}

let oembed;
try {
oembed = await this.knownProvider(providerUrl);
} catch {
// Known-provider metadata is an enhancement for explicit bookmark
// cards. If it fails, fall back to scraping the page as before.
return;
}

if (!oembed.title) {
return;
}

const metadata = {
url,
title: oembed.title,
description: oembed.description || null,
author: oembed.author_name || null,
publisher: oembed.provider_name || null,
thumbnail: oembed.thumbnail_url || (oembed.type === 'photo' ? oembed.url : null),
icon: DEFAULT_BOOKMARK_ICON
};

if (metadata.thumbnail) {
await this.processImageFromUrl(metadata.thumbnail, 'thumbnail')
.then((processedImageUrl) => {
metadata.thumbnail = processedImageUrl;
}).catch((err) => {
logging.error(err);
});
}

return {
version: '1.0',
type: 'bookmark',
url,
metadata
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Thumbnail fallback inconsistency + duplicated image-processing pattern.

When processImageFromUrl fails for the thumbnail (Line 228-235 here, and the equivalent Line 469-476 in fetchBookmarkData), the .catch only logs the error and leaves metadata.thumbnail pointing at the raw, unprocessed provider/third-party URL. This is inconsistent with the icon handling, which explicitly falls back to DEFAULT_BOOKMARK_ICON on failure, and it partially undercuts this PR's stated goal of not exposing provider-supplied content directly — a bookmark card can end up hot-linking an untrusted external image URL instead of a locally-stored copy.

Additionally, the "process image, fall back on missing/failure" pattern is now duplicated three times (icon in fetchBookmarkData, thumbnail in fetchBookmarkData, thumbnail in fetchBookmarkDataFromKnownProvider). Consider extracting a small shared helper.

♻️ Suggested helper + fallback fix
+    async _processBookmarkImage(imageUrl, imageType, fallback = null) {
+        if (!imageUrl) {
+            return fallback;
+        }
+        try {
+            return await this.processImageFromUrl(imageUrl, imageType);
+        } catch (err) {
+            logging.error(err);
+            return fallback;
+        }
+    }
+
     async fetchBookmarkDataFromKnownProvider(url) {
         ...
-        if (metadata.thumbnail) {
-            await this.processImageFromUrl(metadata.thumbnail, 'thumbnail')
-                .then((processedImageUrl) => {
-                    metadata.thumbnail = processedImageUrl;
-                }).catch((err) => {
-                    logging.error(err);
-                });
-        }
+        metadata.thumbnail = await this._processBookmarkImage(metadata.thumbnail, 'thumbnail');

Based on the PR's own rationale of not exposing provider-supplied content raw, applying the same null-fallback used for icons to thumbnails would close this gap.

Also applies to: 228-235, 469-476

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ghost/core/core/server/services/oembed/oembed-service.js` around lines 192 -
244, The thumbnail image handling in fetchBookmarkDataFromKnownProvider and
fetchBookmarkData is inconsistent with the icon fallback and can leave raw
provider URLs exposed when processImageFromUrl fails. Update the thumbnail
processing path so failures clear or replace metadata.thumbnail with a safe
fallback instead of keeping the original URL, matching the DEFAULT_BOOKMARK_ICON
behavior used for icons. While touching fetchBookmarkData and
fetchBookmarkDataFromKnownProvider, consider extracting the repeated “process
image with fallback” logic into a shared helper to remove duplication and keep
the thumbnail/icon behavior consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bookmark for YouTube links not populated with video metadata

1 participant