/** * Platform definitions — mirrored from backend/app/services/platforms.py. * Two copies of these regexes (here and in backend extension_service.py) * is cheaper than building a shared definition file for two short * tables in different runtimes. Keep in sync by hand; reviewers catch * drift. */ const PLATFORMS = { patreon: { name: 'Patreon', domains: ['.patreon.com', 'www.patreon.com', 'patreon.com'], authType: 'cookies', color: '#FF424D', urlPattern: /^https?:\/\/(www\.)?patreon\.com/, }, subscribestar: { name: 'SubscribeStar', domains: [ '.subscribestar.com', '.subscribestar.adult', 'www.subscribestar.com', 'subscribestar.com', 'subscribestar.adult', ], authType: 'cookies', color: '#FFD700', urlPattern: /^https?:\/\/(www\.)?subscribestar\.(com|adult)/, }, hentaifoundry: { name: 'Hentai Foundry', domains: ['.hentai-foundry.com', 'www.hentai-foundry.com', 'hentai-foundry.com'], authType: 'cookies', color: '#9C27B0', urlPattern: /^https?:\/\/(www\.)?hentai-foundry\.com/, }, discord: { name: 'Discord', domains: ['.discord.com', 'discord.com'], authType: 'token', color: '#5865F2', urlPattern: /^https?:\/\/(www\.)?discord\.com/, note: 'Open Discord in browser to capture token', }, pixiv: { name: 'Pixiv', domains: ['.pixiv.net', 'www.pixiv.net', 'pixiv.net'], authType: 'token', color: '#0096FA', urlPattern: /^https?:\/\/(www\.)?pixiv\.net/, note: 'Click to authenticate via OAuth', }, deviantart: { name: 'DeviantArt', domains: ['.deviantart.com', 'www.deviantart.com', 'deviantart.com'], authType: 'cookies', color: '#05CC47', urlPattern: /^https?:\/\/(www\.)?deviantart\.com/, }, }; /** * Per-platform artist-page URL patterns — these are STRICTER than * urlPattern (which matches any URL on the domain). Used by the content * script to decide whether to show the floating "Add as source" button. */ const PLATFORM_ARTIST_PATTERNS = { patreon: /^https?:\/\/(www\.)?patreon\.com\/(?!home$|search\b|messages\b|notifications\b|library\b|settings\b|posts\b|c\/)[^/?#]+\/?$/i, subscribestar: /^https?:\/\/(www\.)?subscribestar\.(com|adult)\/(?!feed$|messages$|library$)[^/?#]+\/?$/i, hentaifoundry: /^https?:\/\/(www\.)?hentai-foundry\.com\/user\/[^/?#]+/i, deviantart: /^https?:\/\/(www\.)?deviantart\.com\/(?!home$|watch\b|tag\b|browse\b)[^/?#]+\/?$/i, pixiv: /^https?:\/\/(www\.)?pixiv\.net\/(en\/)?users\/\d+/i, }; function getPlatformFromUrl(url) { for (const [key, platform] of Object.entries(PLATFORMS)) { if (platform.urlPattern.test(url)) return key; } return null; } function isArtistPage(url, platformKey) { const pattern = PLATFORM_ARTIST_PATTERNS[platformKey]; return pattern ? pattern.test(url) : false; }