Compare commits

...
Author SHA1 Message Date
bvandeusenandClaude Opus 5.5 0928d2f914 chore: the poster-picker comments name their task #4491
CI and images / lint (push) Successful in 3s
CI and images / extension-version (push) Successful in 3s
CI and images / extension-test (push) Successful in 19s
CI and images / frontend-build (push) Successful in 21s
CI and images / backend-lint-and-test (push) Successful in 32s
CI and images / integration (push) Successful in 2m23s
CI and images / build-agent (push) Successful in 6s
CI and images / sign-extension (push) Successful in 6m2s
CI and images / build-web (push) Successful in 1m40s
CI and images / smoke-web (push) Successful in 55s
CI and images / promote (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:03:06 -04:00
10 changed files with 17 additions and 17 deletions
+2 -2
View File
@@ -123,7 +123,7 @@ async def quick_add_source():
# Patreon is canon: adding a Patreon source to an existing artist can take # Patreon is canon: adding a Patreon source to an existing artist can take
# the creator's Patreon display name (name only; the slug never moves). # the creator's Patreon display name (name only; the slug never moves).
use_platform_name = body.get("use_platform_name") is True use_platform_name = body.get("use_platform_name") is True
# #4488: a Discord add can carry the posters picked in the panel. # #4491: a Discord add can carry the posters picked in the panel.
discord_authors = body.get("discord_authors") discord_authors = body.get("discord_authors")
if discord_authors is not None and not _valid_authors(discord_authors): if discord_authors is not None and not _valid_authors(discord_authors):
return _bad("invalid_body", detail="discord_authors must be a list of {id, name}") return _bad("invalid_body", detail="discord_authors must be a list of {id, name}")
@@ -167,7 +167,7 @@ def _valid_authors(value) -> bool:
@extension_bp.route("/discord/posters", methods=["GET"]) @extension_bp.route("/discord/posters", methods=["GET"])
async def discord_posters(): async def discord_posters():
"""Who posts in the Discord channel `url` shows, lately, with the likely """Who posts in the Discord channel `url` shows, lately, with the likely
creator marked (#4488). Reads the channel with the stored token; writes creator marked (#4491). Reads the channel with the stored token; writes
nothing.""" nothing."""
url = (request.args.get("url") or "").strip() url = (request.args.get("url") or "").strip()
if not url: if not url:
+2 -2
View File
@@ -76,7 +76,7 @@ _THREADS_BATCH = 25
_MAX_429_RETRIES = 4 _MAX_429_RETRIES = 4
_429_WAIT_SECONDS = 60.0 _429_WAIT_SECONDS = 60.0
# How far back the poster picker looks: two pages. Enough to see who posts in # How far back the poster picker looks: two pages. Enough to see who posts in
# a channel, few enough to answer while the operator waits (#4488). # a channel, few enough to answer while the operator waits (#4491).
_POSTER_SCAN = 200 _POSTER_SCAN = 200
# https://discord.com/developers/docs/resources/message#message-object-message-types # https://discord.com/developers/docs/resources/message#message-object-message-types
@@ -667,7 +667,7 @@ class DiscordClient:
self, server_id: str | None, channel_id: str, *, max_messages: int = _POSTER_SCAN, self, server_id: str | None, channel_id: str, *, max_messages: int = _POSTER_SCAN,
) -> dict: ) -> dict:
"""Who has posted lately in a channel, for picking a source's poster """Who has posted lately in a channel, for picking a source's poster
list (#4488): the newest `max_messages` messages, tallied per author. list (#4491): the newest `max_messages` messages, tallied per author.
Deliberately shallow — it answers "who posts here", not "who ever did". Deliberately shallow — it answers "who posts here", not "who ever did".
Every poster comes back with their stable id, which is what the list Every poster comes back with their stable id, which is what the list
stores: a username can change on a whim, an id can't.""" stores: a username can change on a whim, an id can't."""
+3 -3
View File
@@ -23,7 +23,7 @@ log = logging.getLogger(__name__)
# The probe runs while the chip is drawing; names that take longer are skipped. # The probe runs while the chip is drawing; names that take longer are skipped.
_NAME_LOOKUP_SECONDS = 6.0 _NAME_LOOKUP_SECONDS = 6.0
# The poster picker waits on two pages of a channel's messages (#4488). # The poster picker waits on two pages of a channel's messages (#4491).
_POSTER_LOOKUP_SECONDS = 20.0 _POSTER_LOOKUP_SECONDS = 20.0
# A source's poster list (discord_ingester.AUTHORS_KEY) holds user ids; this # A source's poster list (discord_ingester.AUTHORS_KEY) holds user ids; this
@@ -137,7 +137,7 @@ class ExtensionService:
slug, and every path keyed off it, never moves (#130). Ignored on every slug, and every path keyed off it, never moves (#130). Ignored on every
other platform, and when the name can't be read. other platform, and when the name can't be read.
`discord_authors` ([{id, name}], #4488) sets a Discord source's poster `discord_authors` ([{id, name}], #4491) sets a Discord source's poster
list in the same step, on a new source or one already there.""" list in the same step, on a new source or one already there."""
platform, raw_slug = self._derive(url) platform, raw_slug = self._derive(url)
url = canonical_source_url(platform, url, raw_slug) url = canonical_source_url(platform, url, raw_slug)
@@ -182,7 +182,7 @@ class ExtensionService:
shaped["renamed_from"] = renamed_from shaped["renamed_from"] = renamed_from
return shaped return shaped
# -- Discord poster picker (#4488) ------------------------------------------ # -- Discord poster picker (#4491) ------------------------------------------
def _discord_page(self, url: str) -> tuple[str, str]: def _discord_page(self, url: str) -> tuple[str, str]:
platform, raw_slug = self._derive(url) platform, raw_slug = self._derive(url)
+1 -1
View File
@@ -100,7 +100,7 @@
/* The panel's own [hidden] — its rows are display:flex, which beats the UA's. */ /* The panel's own [hidden] — its rows are display:flex, which beats the UA's. */
.fc-panel [hidden] { display: none !important; } .fc-panel [hidden] { display: none !important; }
.fc-panel__rename { margin-top: 8px; font-size: 13px; } .fc-panel__rename { margin-top: 8px; font-size: 13px; }
/* Poster picker (#4488): who posts in the channel, the likely creator ticked. */ /* Poster picker (#4491): who posts in the channel, the likely creator ticked. */
.fc-panel__posters { max-height: 220px; overflow-y: auto; } .fc-panel__posters { max-height: 220px; overflow-y: auto; }
.fc-panel__poster { flex-wrap: wrap; } .fc-panel__poster { flex-wrap: wrap; }
.fc-panel__poster-meta { flex-basis: 100%; padding-left: 21px; font-size: 11px; color: rgb(170, 166, 156); } .fc-panel__poster-meta { flex-basis: 100%; padding-left: 21px; font-size: 11px; color: rgb(170, 166, 156); }
+3 -3
View File
@@ -81,7 +81,7 @@
const probe = currentProbe; const probe = currentProbe;
if (probe?.state === 'source_match') { if (probe?.state === 'source_match') {
// A followed Discord channel opens its poster list (#4488); the artist // A followed Discord channel opens its poster list (#4491); the artist
// page is one click further, in that panel. // page is one click further, in that panel.
if (probe.platform === 'discord' && probe.discord?.channel_id) { if (probe.platform === 'discord' && probe.discord?.channel_id) {
if (document.getElementById('fc-add-panel')) closePanel(); if (document.getElementById('fc-add-panel')) closePanel();
@@ -165,7 +165,7 @@
document.getElementById('fc-add-panel')?.remove(); document.getElementById('fc-add-panel')?.remove();
} }
// ---- Poster picker (#4488) ---- // ---- Poster picker (#4491) ----
// Who posted in this channel lately, read by FabledCurator with its Discord // Who posted in this channel lately, read by FabledCurator with its Discord
// token, the creator the server is named for (or owned by) ticked. The // token, the creator the server is named for (or owned by) ticked. The
// source keeps their ids: a username can change on a whim, an id can't, so // source keeps their ids: a username can change on a whim, an id can't, so
@@ -305,7 +305,7 @@
const d = probe.discord || {}; const d = probe.discord || {};
const discord = probe.platform === 'discord'; const discord = probe.platform === 'discord';
const choice = panelDefaults(probe, window.location.href); const choice = panelDefaults(probe, window.location.href);
// #4488: on a channel page, pick whose posts the new source takes. // #4491: on a channel page, pick whose posts the new source takes.
const posters = discord && d.channel_id ? posterSection() : null; const posters = discord && d.channel_id ? posterSection() : null;
const scopeRow = (value, label, disabled) => { const scopeRow = (value, label, disabled) => {
+2 -2
View File
@@ -106,7 +106,7 @@ class FabledCuratorAPI {
// artist from the URL. A Discord channel always sends one. // artist from the URL. A Discord channel always sends one.
// usePlatformName: a Patreon source joining an existing artist renames it // usePlatformName: a Patreon source joining an existing artist renames it
// to the Patreon display name (Patreon is canon; name only, never the slug). // to the Patreon display name (Patreon is canon; name only, never the slug).
// discordAuthors ([{id, name}], #4488): a Discord source's poster list, set // discordAuthors ([{id, name}], #4491): a Discord source's poster list, set
// in the same step. // in the same step.
quickAddSource(url, { artistId = null, artistName = null, usePlatformName = false, discordAuthors = null } = {}) { quickAddSource(url, { artistId = null, artistName = null, usePlatformName = false, discordAuthors = null } = {}) {
const body = { url }; const body = { url };
@@ -126,7 +126,7 @@ class FabledCuratorAPI {
const qs = new URLSearchParams(params).toString(); const qs = new URLSearchParams(params).toString();
return this.request('GET', `/extension/probe?${qs}`); return this.request('GET', `/extension/probe?${qs}`);
} }
// #4488: who posts in the Discord channel `url` shows, lately, with the // #4491: who posts in the Discord channel `url` shows, lately, with the
// likely creator marked; and setting the list on the source that follows it. // likely creator marked; and setting the list on the source that follows it.
getDiscordPosters(url) { getDiscordPosters(url) {
const qs = new URLSearchParams({ url }).toString(); const qs = new URLSearchParams({ url }).toString();
+1 -1
View File
@@ -145,7 +145,7 @@ function inlineCompletion(typed, results) {
return (results || []).find((a) => a.name.length > t.length && a.name.toLowerCase().startsWith(t)) || null; return (results || []).find((a) => a.name.length > t.length && a.name.toLowerCase().startsWith(t)) || null;
} }
// ---- Discord poster picker (#4488) ---- // ---- Discord poster picker (#4491) ----
// /api/extension/discord/posters lists who posted in the channel lately, with // /api/extension/discord/posters lists who posted in the channel lately, with
// the creator the server is named for (or owned by) marked `suggested`. The // the creator the server is named for (or owned by) marked `suggested`. The
// picker ticks posters; the source keeps their ids, since a username can // picker ticks posters; the source keeps their ids, since a username can
+1 -1
View File
@@ -216,7 +216,7 @@ describe('the poster checklist rows', () => {
}) })
}) })
describe('the Discord poster picker (#4488)', () => { describe('the Discord poster picker (#4491)', () => {
const posters = [ const posters = [
{ id: '7', username: 'todding', global_name: 'Todding', suggested: true }, { id: '7', username: 'todding', global_name: 'Todding', suggested: true },
{ id: '8', username: 'jakeboii', global_name: 'Jake Boii', suggested: false }, { id: '8', username: 'jakeboii', global_name: 'Jake Boii', suggested: false },
@@ -115,7 +115,7 @@ const structuredAuthors = ref('')
const otherConfig = ref({}) const otherConfig = ref({})
const STRUCTURED_KEYS = ['videos', 'since', 'discord_authors'] const STRUCTURED_KEYS = ['videos', 'since', 'discord_authors']
// The extension's poster picker stores ids (#4488) and the name each was // The extension's poster picker stores ids (#4491) and the name each was
// picked under, so a list of numbers still says who it means. // picked under, so a list of numbers still says who it means.
const authorNames = computed(() => { const authorNames = computed(() => {
const labels = otherConfig.value.discord_author_labels || {} const labels = otherConfig.value.discord_author_labels || {}
+1 -1
View File
@@ -426,7 +426,7 @@ def test_the_adapter_authenticates_with_the_token_and_keys_by_identity(tmp_path)
assert _ledger_key(media) == "9:300" assert _ledger_key(media) == "9:300"
# -- the poster picker (#4488) ------------------------------------------------- # -- the poster picker (#4491) -------------------------------------------------
def _person(pid, username, global_name=None, **extra): def _person(pid, username, global_name=None, **extra):
return {"id": pid, "username": username, "global_name": global_name, return {"id": pid, "username": username, "global_name": global_name,