Merge pull request 'fix(extension): Add-to-FC button on subscribed Patreon creators + v1.0.8 (#1485)' (#230) from dev into main
CI / lint (push) Successful in 3s
Build images / build-agent (push) Successful in 8s
Build images / build-ml (push) Successful in 8s
extension / lint (push) Successful in 8s
CI / frontend-build (push) Successful in 20s
CI / backend-lint-and-test (push) Successful in 30s
CI / integration (push) Successful in 3m46s
Build images / sign-extension (push) Successful in 5m8s
Build images / build-web (push) Successful in 10s
CI / lint (push) Successful in 3s
Build images / build-agent (push) Successful in 8s
Build images / build-ml (push) Successful in 8s
extension / lint (push) Successful in 8s
CI / frontend-build (push) Successful in 20s
CI / backend-lint-and-test (push) Successful in 30s
CI / integration (push) Successful in 3m46s
Build images / sign-extension (push) Successful in 5m8s
Build images / build-web (push) Successful in 10s
This commit was merged in pull request #230.
This commit is contained in:
@@ -35,9 +35,14 @@ class InvalidUrlError(Exception):
|
|||||||
# reviewers catch drift.
|
# reviewers catch drift.
|
||||||
_PLATFORM_PATTERNS: list[tuple[str, re.Pattern[str]]] = [
|
_PLATFORM_PATTERNS: list[tuple[str, re.Pattern[str]]] = [
|
||||||
("patreon", re.compile(
|
("patreon", re.compile(
|
||||||
|
# Three creator URL shapes — bare (patreon.com/Atole), `c/`, and `cw/`
|
||||||
|
# (the "creator workspace" URL served once subscribed, see
|
||||||
|
# patreon_resolver._VANITY_RE). A trailing sub-path is allowed so a
|
||||||
|
# creator's inner page still derives the slug. Nav pages stay excluded.
|
||||||
r"^https?://(?:www\.)?patreon\.com/"
|
r"^https?://(?:www\.)?patreon\.com/"
|
||||||
r"(?!home$|search\b|messages\b|notifications\b|library\b|settings\b|posts\b|c/)"
|
r"(?:cw/|c/)?"
|
||||||
r"(?P<slug>[^/?#]+)/?$",
|
r"(?!(?:home|search|messages|notifications|library|settings|posts)(?:[/?#]|$))"
|
||||||
|
r"(?P<slug>[^/?#]+)",
|
||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
)),
|
)),
|
||||||
("subscribestar", re.compile(
|
("subscribestar", re.compile(
|
||||||
|
|||||||
@@ -86,7 +86,16 @@ const PLATFORMS = {
|
|||||||
* script to decide whether to show the floating "Add as source" button.
|
* script to decide whether to show the floating "Add as source" button.
|
||||||
*/
|
*/
|
||||||
const PLATFORM_ARTIST_PATTERNS = {
|
const PLATFORM_ARTIST_PATTERNS = {
|
||||||
patreon: /^https?:\/\/(www\.)?patreon\.com\/(?!home$|search\b|messages\b|notifications\b|library\b|settings\b|posts\b|c\/)[^/?#]+\/?$/i,
|
// Patreon serves the same creator under three URL shapes (see backend
|
||||||
|
// patreon_resolver._VANITY_RE): bare `patreon.com/Atole`, `c/` prefix, and
|
||||||
|
// `cw/` "creator workspace" — the last is the URL you land on once you're
|
||||||
|
// SUBSCRIBED, which is exactly when the button matters. Match all three, and
|
||||||
|
// drop the single-segment end-anchor so a creator's inner page
|
||||||
|
// (…/cw/Atole/posts, …/Atole/membership) also injects the button. Nav pages
|
||||||
|
// (home/search/…/posts permalink) stay excluded. Mirrors extension_service
|
||||||
|
// ._PLATFORM_PATTERNS — keep in sync (operator-flagged 2026-07-13: button
|
||||||
|
// vanished once subscribed because the old pattern only matched the bare root).
|
||||||
|
patreon: /^https?:\/\/(www\.)?patreon\.com\/(?:cw\/|c\/)?(?!(?:home|search|messages|notifications|library|settings|posts)(?:[\/?#]|$))[^/?#]+/i,
|
||||||
subscribestar: /^https?:\/\/(www\.)?subscribestar\.(com|adult)\/(?!feed$|messages$|library$)[^/?#]+\/?$/i,
|
subscribestar: /^https?:\/\/(www\.)?subscribestar\.(com|adult)\/(?!feed$|messages$|library$)[^/?#]+\/?$/i,
|
||||||
hentaifoundry: /^https?:\/\/(www\.)?hentai-foundry\.com\/user\/[^/?#]+/i,
|
hentaifoundry: /^https?:\/\/(www\.)?hentai-foundry\.com\/user\/[^/?#]+/i,
|
||||||
deviantart: /^https?:\/\/(www\.)?deviantart\.com\/(?!home$|watch\b|tag\b|browse\b)[^/?#]+\/?$/i,
|
deviantart: /^https?:\/\/(www\.)?deviantart\.com\/(?!home$|watch\b|tag\b|browse\b)[^/?#]+\/?$/i,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "FabledCurator",
|
"name": "FabledCurator",
|
||||||
"version": "1.0.7",
|
"version": "1.0.8",
|
||||||
"description": "Export cookies from supported platforms to FabledCurator and add creators as sources in one click.",
|
"description": "Export cookies from supported platforms to FabledCurator and add creators as sources in one click.",
|
||||||
|
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fabledcurator-extension",
|
"name": "fabledcurator-extension",
|
||||||
"version": "1.0.7",
|
"version": "1.0.8",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Firefox extension for FabledCurator",
|
"description": "Firefox extension for FabledCurator",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
"""Unit tests for ExtensionService._derive — the URL → (platform, slug)
|
||||||
|
parser that gates the browser extension's "Add as source" button and pulls
|
||||||
|
the creator slug on probe/add.
|
||||||
|
|
||||||
|
Regression cover for #1485: Patreon serves the same creator under three URL
|
||||||
|
shapes — bare `patreon.com/Atole`, `c/`, and `cw/` (the "creator workspace"
|
||||||
|
URL you land on once SUBSCRIBED). The button used to vanish while subscribed
|
||||||
|
because the pattern only matched the bare root and excluded `c/`.
|
||||||
|
|
||||||
|
_derive is pure URL parsing (no DB / no async), so a session-less instance is
|
||||||
|
fine to exercise directly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from backend.app.services.extension_service import (
|
||||||
|
ExtensionService,
|
||||||
|
InvalidUrlError,
|
||||||
|
UnknownPlatformError,
|
||||||
|
)
|
||||||
|
|
||||||
|
_svc = ExtensionService(None)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"url, slug",
|
||||||
|
[
|
||||||
|
# All three Patreon creator prefixes resolve to the same vanity slug.
|
||||||
|
("https://www.patreon.com/Atole", "Atole"),
|
||||||
|
("https://www.patreon.com/c/Atole", "Atole"),
|
||||||
|
("https://www.patreon.com/cw/Atole", "Atole"), # subscribed-view URL
|
||||||
|
# A creator's inner page still derives the slug (trailing sub-path).
|
||||||
|
("https://www.patreon.com/cw/Atole/posts", "Atole"),
|
||||||
|
("https://www.patreon.com/Atole/membership", "Atole"),
|
||||||
|
("https://patreon.com/c/Atole", "Atole"), # bare host, no www
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_derive_patreon_creator_urls(url, slug):
|
||||||
|
platform, got = _svc._derive(url)
|
||||||
|
assert platform == "patreon"
|
||||||
|
assert got == slug
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"url",
|
||||||
|
[
|
||||||
|
# Patreon's own nav pages must never read as a creator slug.
|
||||||
|
"https://www.patreon.com/home",
|
||||||
|
"https://www.patreon.com/settings",
|
||||||
|
"https://www.patreon.com/search",
|
||||||
|
"https://www.patreon.com/messages",
|
||||||
|
"https://www.patreon.com/library",
|
||||||
|
"https://www.patreon.com/notifications",
|
||||||
|
"https://www.patreon.com/posts/12345", # post permalink
|
||||||
|
"https://www.patreon.com/settings/billing", # nav sub-page
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_derive_patreon_nav_pages_rejected(url):
|
||||||
|
with pytest.raises(UnknownPlatformError):
|
||||||
|
_svc._derive(url)
|
||||||
|
|
||||||
|
|
||||||
|
def test_derive_rejects_missing_scheme():
|
||||||
|
with pytest.raises(InvalidUrlError):
|
||||||
|
_svc._derive("patreon.com/Atole")
|
||||||
Reference in New Issue
Block a user