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 }; } /**