You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
3.4 KiB
122 lines
3.4 KiB
/** |
|
* Recognize well-known RSS / Atom feed URL shapes (Spotifeed, YouTube, FeedBurner, etc.) |
|
* for friendlier labels in the UI. Not exhaustive — unknown URLs return null. |
|
*/ |
|
|
|
export type StandardRssFeedIcon = 'music' | 'youtube' | 'feedburner' | 'reddit' | 'substack' | 'medium' | 'rss' |
|
|
|
export type StandardRssFeedProfile = { |
|
icon: StandardRssFeedIcon |
|
/** i18n key under translation */ |
|
labelKey: string |
|
/** English fallback if catalog missing */ |
|
defaultLabel: string |
|
/** Short secondary line (e.g. truncated id) */ |
|
detail?: string |
|
} |
|
|
|
function truncateId(id: string, max = 14): string { |
|
const t = id.trim() |
|
if (t.length <= max) return t |
|
return `${t.slice(0, max)}…` |
|
} |
|
|
|
/** |
|
* Returns a profile when `feedUrl` matches a known pattern; otherwise null. |
|
*/ |
|
export function getStandardRssFeedProfile(feedUrl: string): StandardRssFeedProfile | null { |
|
let u: URL |
|
try { |
|
u = new URL(feedUrl.trim()) |
|
} catch { |
|
return null |
|
} |
|
|
|
const host = u.hostname.replace(/^www\./, '').toLowerCase() |
|
|
|
if (host === 'spotifeed.timdorr.com') { |
|
const id = u.pathname.replace(/^\//, '').split('/').filter(Boolean)[0] ?? '' |
|
return { |
|
icon: 'music', |
|
labelKey: 'standardRssFeed_spotifeed', |
|
defaultLabel: 'Spotify playlist (Spotifeed)', |
|
detail: id ? truncateId(id, 22) : undefined |
|
} |
|
} |
|
|
|
if (host === 'youtube.com' || host === 'm.youtube.com' || host === 'music.youtube.com') { |
|
if (u.pathname.includes('/feeds/videos.xml')) { |
|
const ch = u.searchParams.get('channel_id') |
|
const pl = u.searchParams.get('playlist_id') |
|
if (ch) { |
|
return { |
|
icon: 'youtube', |
|
labelKey: 'standardRssFeed_youtubeChannel', |
|
defaultLabel: 'YouTube channel feed', |
|
detail: truncateId(ch, 18) |
|
} |
|
} |
|
if (pl) { |
|
return { |
|
icon: 'youtube', |
|
labelKey: 'standardRssFeed_youtubePlaylist', |
|
defaultLabel: 'YouTube playlist feed', |
|
detail: truncateId(pl, 18) |
|
} |
|
} |
|
return { |
|
icon: 'youtube', |
|
labelKey: 'standardRssFeed_youtube', |
|
defaultLabel: 'YouTube feed' |
|
} |
|
} |
|
} |
|
|
|
if (host.endsWith('feedburner.com') || host.endsWith('feedburner.google.com')) { |
|
return { |
|
icon: 'feedburner', |
|
labelKey: 'standardRssFeed_feedburner', |
|
defaultLabel: 'FeedBurner' |
|
} |
|
} |
|
|
|
if (host === 'reddit.com' || host.endsWith('.reddit.com')) { |
|
const p = u.pathname |
|
if (p.endsWith('.rss') || p.includes('/.rss') || p.endsWith('/rss') || p.includes('/rss/')) { |
|
return { |
|
icon: 'reddit', |
|
labelKey: 'standardRssFeed_reddit', |
|
defaultLabel: 'Reddit RSS' |
|
} |
|
} |
|
} |
|
|
|
if (host.endsWith('.substack.com') && u.pathname.startsWith('/feed')) { |
|
return { |
|
icon: 'substack', |
|
labelKey: 'standardRssFeed_substack', |
|
defaultLabel: 'Substack' |
|
} |
|
} |
|
|
|
if (host === 'medium.com' || host.endsWith('.medium.com')) { |
|
if (u.pathname.startsWith('/feed') || u.pathname.startsWith('/@')) { |
|
return { |
|
icon: 'medium', |
|
labelKey: 'standardRssFeed_medium', |
|
defaultLabel: 'Medium' |
|
} |
|
} |
|
} |
|
|
|
return null |
|
} |
|
|
|
/** Hostname for display when there is no known profile. */ |
|
export function getRssFeedUrlHostname(feedUrl: string): string { |
|
try { |
|
return new URL(feedUrl.trim()).hostname.replace(/^www\./, '') |
|
} catch { |
|
return feedUrl.trim().slice(0, 80) |
|
} |
|
}
|
|
|