The root-cause fix for empty post bodies. Patreon deprecated the flat content HTML field (returns null on feed + detail, all post types); the body now lives in content_json_string (a ProseMirror doc), returned only under the default post fieldset.
Verified against the live API (operator's browser): sparse → null, default fieldset → content_json_string present. No migrations. CI green on dev head 976107b (run 1124).
After merge: re-pull :latest, re-run Recapture on StickySpoodge — with_body should go 126 → 135.
The root-cause fix for empty post bodies. Patreon deprecated the flat `content` HTML field (returns null on feed + detail, all post types); the body now lives in `content_json_string` (a ProseMirror doc), returned only under the default post fieldset.
- `utils/prosemirror.py`: ProseMirror→HTML + `post_body_html(attrs)` resolver (legacy content else convert content_json_string).
- Feed `_FIELDS_POST` adds `content_json_string`; `fetch_post_detail_content` uses the default fieldset (no sparse) + the resolver.
- `_write_sidecar_data` resolves the body via the helper before the detail fetch.
- Inline `<img>` nodes carry the CDN filehash → feed Phase-2 localization.
Verified against the live API (operator's browser): sparse → null, default fieldset → content_json_string present. No migrations. CI green on dev head `976107b` (run 1124).
After merge: re-pull `:latest`, re-run Recapture on StickySpoodge — `with_body` should go 126 → 135.
THE empty-body root cause. Patreon deprecated the flat `content` HTML field —
it returns null on the feed AND the detail endpoint, for every post type
(confirmed against the live API: all 135 StickySpoodge posts, text_only/
image_file/poll alike). The real body now lives in `content_json_string` (a
ProseMirror/TipTap doc), returned only under the DEFAULT post fieldset — a sparse
fields[post]=content request omits it. Not credential, not post_type: a request
shape gone stale.
- NEW utils/prosemirror.py: ProseMirror doc -> HTML (paragraphs, marks
bold/italic/underline/strike/code/link, hardBreak, inline images, lists,
headings; unknown nodes degrade to children). post_body_html(attrs) = the one
resolver: legacy content HTML else convert content_json_string.
- patreon_client: add content_json_string to the feed _FIELDS_POST; rewrite
fetch_post_detail_content to use the DEFAULT fieldset (no sparse fields[post])
and resolve via post_body_html (replaces the wrong sparse req + full-fetch
fallback).
- patreon_downloader._write_sidecar_data: resolve body via post_body_html
(feed content_json_string) before the detail-fetch; memoize resolved HTML.
- tests: prosemirror converter unit tests; client legacy + content_json_string
paths; contract pins content_json_string.
Inline <img> nodes carry the CDN filehash → bodies now feed Phase-2 localization.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The root-cause fix for empty post bodies. Patreon deprecated the flat
contentHTML field (returns null on feed + detail, all post types); the body now lives incontent_json_string(a ProseMirror doc), returned only under the default post fieldset.utils/prosemirror.py: ProseMirror→HTML +post_body_html(attrs)resolver (legacy content else convert content_json_string)._FIELDS_POSTaddscontent_json_string;fetch_post_detail_contentuses the default fieldset (no sparse) + the resolver._write_sidecar_dataresolves the body via the helper before the detail fetch.<img>nodes carry the CDN filehash → feed Phase-2 localization.Verified against the live API (operator's browser): sparse → null, default fieldset → content_json_string present. No migrations. CI green on dev head
976107b(run 1124).After merge: re-pull
:latest, re-run Recapture on StickySpoodge —with_bodyshould go 126 → 135.