Skip to content

Retry expired Instagram media URLs via instaById before giving up - #41

Open
rejas wants to merge 1 commit into
prereleasefrom
feat/media-proxy-refresh-expired-instagram-url
Open

Retry expired Instagram media URLs via instaById before giving up#41
rejas wants to merge 1 commit into
prereleasefrom
feat/media-proxy-refresh-expired-instagram-url

Conversation

@rejas

@rejas rejas commented Aug 31, 2026

Copy link
Copy Markdown
Member

Summary

  • Instagram issues some (typically older) media noticeably shorter-lived signed CDN URLs than others fetched in the same batch, so a media_url embedded on the WordPress side can already be expired by the time a visitor loads it — even though our own cache entry for that batch is still well within its 12h TTL. resolve_media_url previously just returned a plain 400 with no way to recover. (Root cause originally diagnosed while fixing a stuck Instagram carousel on the frontpage — see the paired frontend fallback fix in vvp_divi5_extensions.)
  • Thread the Instagram post id (and, for a carousel slide, its child id) and account through to the proxy URL, bound into the HMAC signature via a new generate_token_with_context() alongside the existing url-only generate_token().
  • On a failed primary fetch, resolve_media_url now does one retry: re-fetch that single post from Instagram (fetch_fresh_media_url, new) to get a freshly signed URL, then fetch that.
  • Backward compatible: links generated before this deploy (or from a post missing an id, which shouldn't happen but is technically optional in the API response) carry no post_id and are verified/served exactly as before — generate_token(url) is unchanged, replace_media_urls() is unchanged and still directly tested. replace_media_urls_for_posts() is the new, additive, Instagram-feed-specific counterpart that instaFeed now uses instead.
  • Only adds a live Instagram API call on the request path when the first fetch fails, not on every image load, so this shouldn't meaningfully increase Graph API call volume.

Test plan

  • python manage.py test proxycache — 68 tests pass (11 new, all existing ones untouched/still green)
  • ruff check / ruff format --check clean on all touched files
  • bandit -c pyproject.toml — no new findings (2 pre-existing low-severity findings in insta_feed.py's ACCOUNTS dict are unrelated env-var-name string matches, not part of this diff)
  • Spot check on production once deployed: confirm a previously-broken Instagram carousel slide recovers without a full cache cycle

🤖 Generated with Claude Code

Instagram issues some (typically older) media noticeably shorter-lived
signed CDN URLs than others fetched in the same batch, so a media_url
embedded on the WordPress side can already be expired by the time a
visitor loads it — even though our own cache entry for that batch is
still well within its 12h TTL. The proxy would just return a plain 400,
leaving the frontend with no way to recover (see the paired frontend
fix in vvp_divi5_extensions for the "Bild nicht verfügbar" fallback
this produces).

Thread the Instagram post id (and, for a carousel slide, its child id)
and account through to the proxy URL, bound into the HMAC signature via
a new generate_token_with_context() alongside the existing url-only
generate_token(). On a failed primary fetch, resolve_media_url now
does one retry: re-fetch that single post from Instagram
(fetch_fresh_media_url) to get a freshly signed URL, then fetch that.

Backward compatible: links generated before this deploy (or by a post
missing an 'id', which shouldn't happen but is technically optional in
the API response) carry no post_id and are verified and served exactly
as before — generate_token(url) unchanged, replace_media_urls()
unchanged and still used/tested as-is. replace_media_urls_for_posts()
is the new, additive, Instagram-feed-specific counterpart that
instaFeed now uses instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves resilience of the Instagram media proxy by allowing a single, signed, context-aware retry when an embedded Instagram CDN media_url has expired before the proxy cache TTL.

Changes:

  • Add context-bound HMAC tokens (generate_token_with_context) and Instagram-feed-specific URL rewriting (replace_media_urls_for_posts) so proxy URLs carry post_id/child_id/account.
  • Update resolve_media_url to retry once by calling the Instagram Graph API for a fresh media_url when the initial fetch fails (context links only).
  • Extend test coverage to validate context binding, fallback behavior, and backward compatibility for legacy (url-only) tokens.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
proxycache/helper.py Adds context-bound token generation and an Instagram-feed-specific media URL rewriter that includes post/account context.
proxycache/services/resolve_media_url.py Adds a fetch helper and a one-shot retry path that re-fetches a fresh Instagram media_url on failed primary fetch (context links only).
proxycache/services/insta_feed.py Switches instaFeed to the new post-aware URL rewriting and adds fetch_fresh_media_url used by the proxy retry.
proxycache/tests.py Adds tests for context binding, carousel child handling, retry behavior, and legacy behavior preservation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +119 to +123
url = f"https://graph.instagram.com/v15.0/{post_id}"
params = {
"fields": "media_url,thumbnail_url,children{id,media_url}",
"access_token": getToken(account),
}
Comment on lines 35 to +51
media_url = request.GET.get("url")
# Present only on links generated by replace_media_urls_for_posts() —
# older/plain links (generate_token(url) alone) have none of these, and
# are verified and served exactly as before.
post_id = request.GET.get("post_id", "")
child_id = request.GET.get("child_id", "")
account = request.GET.get("account", "")

if not provided_hash or not media_url:
return HttpResponseBadRequest("Missing required parameters.")

expected_hash = generate_token(media_url)
if post_id:
expected_hash = generate_token_with_context(
media_url, post_id, child_id, account
)
else:
expected_hash = generate_token(media_url)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants