From f73dd547e54dcdf2ef9b98c9aad3baca644b9cf0 Mon Sep 17 00:00:00 2001 From: svemagie <869694+svemagie@users.noreply.github.com> Date: Sat, 4 Apr 2026 10:16:03 +0200 Subject: [PATCH] feat: skip bookmark import when no feed is found at the URL Runs feed discovery before creating a subscription so article URLs without a detectable feed are silently skipped instead of being imported as dead feeds. Uses the discovered feed URL (which may differ from the bookmarked URL) and passes the feed title from discovery to avoid waiting for the first poll. Co-Authored-By: Claude Sonnet 4.6 --- lib/bookmark-import.js | 43 ++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/bookmark-import.js b/lib/bookmark-import.js index 73c76e1..9c2d924 100644 --- a/lib/bookmark-import.js +++ b/lib/bookmark-import.js @@ -11,6 +11,10 @@ */ import { detectCapabilities } from "./feeds/capabilities.js"; +import { + discoverAndValidateFeeds, + getBestFeed, +} from "./feeds/discovery.js"; import { refreshFeedNow } from "./polling/scheduler.js"; import { createChannel, getChannels } from "./storage/channels.js"; import { @@ -98,6 +102,17 @@ export async function importBookmarkAsFollow( return { error: "microsub not initialised" }; } + // Discover the actual feed URL — skip import if no valid feed exists + const discoveredFeeds = await discoverAndValidateFeeds(url).catch(() => []); + const bestFeed = getBestFeed(discoveredFeeds); + if (!bestFeed) { + console.log( + `[Microsub] bookmark-import: no feed found at ${url}, skipping`, + ); + return { skipped: true, reason: "no feed found", url }; + } + const feedUrl = bestFeed.url; + // Normalise tags to an array of trimmed, non-empty strings const tagList = (Array.isArray(tags) ? tags : [tags]) .map((t) => String(t).trim()) @@ -121,7 +136,7 @@ export async function importBookmarkAsFollow( } // Check if already followed in any channel - const existing = await findFeedAcrossChannels(application, url); + const existing = await findFeedAcrossChannels(application, feedUrl); if (existing) { const existingFeed = existing.feed; @@ -138,14 +153,14 @@ export async function importBookmarkAsFollow( }); } console.log( - `[Microsub] bookmark-import: ${url} already followed in "${existingChannelName}" (correct channel)`, + `[Microsub] bookmark-import: ${feedUrl} already followed in "${existingChannelName}" (correct channel)`, ); - return { alreadyExists: true, url, channel: existingChannelName }; + return { alreadyExists: true, url: feedUrl, channel: existingChannelName }; } // Wrong channel — move the feed: delete from old channel, create in new one. console.log( - `[Microsub] bookmark-import: moving ${url} from "${existingChannelName}" → "${targetChannel.name}"`, + `[Microsub] bookmark-import: moving ${feedUrl} from "${existingChannelName}" → "${targetChannel.name}"`, ); await deleteFeedById(application, existingFeed._id); // Fall through to create below @@ -156,17 +171,17 @@ export async function importBookmarkAsFollow( try { feed = await createFeed(application, { channelId: targetChannel._id, - url, - title: undefined, + url: feedUrl, + title: bestFeed.title || undefined, photo: undefined, micropubPostUrl: postUrl || undefined, }); } catch (error) { if (error.code === "DUPLICATE_FEED") { console.log( - `[Microsub] bookmark-import: duplicate feed detected for ${url}`, + `[Microsub] bookmark-import: duplicate feed detected for ${feedUrl}`, ); - return { alreadyExists: true, url }; + return { alreadyExists: true, url: feedUrl }; } throw error; } @@ -174,20 +189,20 @@ export async function importBookmarkAsFollow( // Fire-and-forget: fetch and detect capabilities refreshFeedNow(application, feed._id).catch((error) => { console.error( - `[Microsub] bookmark-import: error fetching ${url}:`, + `[Microsub] bookmark-import: error fetching ${feedUrl}:`, error.message, ); }); - detectCapabilities(url).catch((error) => { + detectCapabilities(feedUrl).catch((error) => { console.error( - `[Microsub] bookmark-import: capability detection error for ${url}:`, + `[Microsub] bookmark-import: capability detection error for ${feedUrl}:`, error.message, ); }); // Notify blogroll (it gets its entries from microsub, not independently) notifyBlogroll(application, "follow", { - url, + url: feedUrl, title: feed.title, channelName: targetChannel.name, feedId: feed._id.toString(), @@ -197,9 +212,9 @@ export async function importBookmarkAsFollow( }); console.log( - `[Microsub] bookmark-import: added ${url} to channel "${targetChannel.name}"`, + `[Microsub] bookmark-import: added ${feedUrl} (discovered from ${url}) to channel "${targetChannel.name}"`, ); - return { added: 1, url, channel: targetChannel.name }; + return { added: 1, url: feedUrl, channel: targetChannel.name }; } /**