Fix proxied video poster loading for blocked booru CDNs - #146
Fix proxied video poster loading for blocked booru CDNs#146AlejandroAkbal wants to merge 7 commits into
Conversation
Route video preview thumbnails through imgproxy under the same rules as feed images, add a premium CORS-proxy fallback when posters still fail, and keep PocketBase auth state in sync so premium retries run reliably.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📜 Recent review details🧰 Additional context used📓 Path-based instructions (6)app/**/*.{vue,ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{vue,ts,tsx,js,jsx,json,css,scss}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{ts,tsx,js,jsx,vue}📄 CodeRabbit inference engine (AGENTS.md)
Files:
app/components/**/*.vue📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{ts,tsx,vue}📄 CodeRabbit inference engine (AGENTS.md)
Files:
app/**/*.vue📄 CodeRabbit inference engine (AGENTS.md)
Files:
🔇 Additional comments (1)
📝 WalkthroughWalkthroughAdds a ChangesVideo poster proxy via imgproxy
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/components/pages/posts/post/PostMedia.vue`:
- Around line 465-473: The onVideoPlaybackError function has early return
conditions that exit without setting hasError.value to true, causing broken
video playback to display with no error UI. When the early return conditions are
triggered (for non-premium users, when hasError is already true, when the event
target is invalid, or when a proxy retry has already been attempted), the
function returns silently without indicating an error state. Set hasError.value
= true before each early return statement in the onVideoPlaybackError function
to ensure that failed video playback properly triggers the error UI fallback for
users who cannot retry with a proxy or are not premium members.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1dfcf435-a45c-441f-8a9f-d5e346b42820
📒 Files selected for processing (10)
app/assets/js/nuxt-image/get-imgproxy-url.tsapp/components/pages/posts/post/PostMedia.vueapp/composables/usePocketbase.tsapp/composables/useUserData.tsapp/plugins/030.pocketbase.tstest/assets/get-imgproxy-url.test.tstest/pages/posts-video-proxy.test.tstest/pages/posts.mock-data.tstest/pages/posts.test.tstest/server-mocks/plugin.ts
💤 Files with no reviewable changes (1)
- app/composables/usePocketbase.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (AGENTS.md)
Use Nuxt 4 with Vue 3 and TypeScript as the primary framework
Files:
app/composables/useUserData.tstest/assets/get-imgproxy-url.test.tstest/pages/posts-video-proxy.test.tstest/server-mocks/plugin.tstest/pages/posts.test.tsapp/assets/js/nuxt-image/get-imgproxy-url.tstest/pages/posts.mock-data.tsapp/plugins/030.pocketbase.tsapp/components/pages/posts/post/PostMedia.vue
**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,vue}: Use Prettier formatting with 120-char print width, no semicolons, single quotes, trailing commas removed, and single attribute per line in Vue templates
Use Nuxt flat ESLint (@nuxt/eslint) for linting
Files:
app/composables/useUserData.tstest/assets/get-imgproxy-url.test.tstest/pages/posts-video-proxy.test.tstest/server-mocks/plugin.tstest/pages/posts.test.tsapp/assets/js/nuxt-image/get-imgproxy-url.tstest/pages/posts.mock-data.tsapp/plugins/030.pocketbase.tsapp/components/pages/posts/post/PostMedia.vue
{app/composables/**/*.ts,app/components/**/*.vue}
📄 CodeRabbit inference engine (AGENTS.md)
{app/composables/**/*.ts,app/components/**/*.vue}: Use useLazyToast() to lazy-load vue-sonner and render ClientToaster; wait for ClientToaster to mount before calling toast.* (use plain nextTick() after lazy loading)
Use PocketBase batch writes for multi-record replacement/reorder operations; reordering positioned records should not emit one HTTP write per changed row
VueUse moveArrayElement() applies move on nextTick; for immediately persisted state, build reordered array synchronously instead of reading before VueUse applies move
Files:
app/composables/useUserData.tsapp/components/pages/posts/post/PostMedia.vue
{app/components/**/*.vue,app/composables/**/*.ts}
📄 CodeRabbit inference engine (AGENTS.md)
{app/components/**/*.vue,app/composables/**/*.ts}: Preserve interaction-gated loading for post UI features using Nuxt Lazy* components, dynamic imports, or deferred boundaries instead of entering first-load route chunk
PocketBase realtime subscriptions echo local writes; when debugging sync performance, inspect real network traces and separate write requests from realtime-triggered refreshes
Files:
app/composables/useUserData.tsapp/components/pages/posts/post/PostMedia.vue
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
For URL validation/parsing, prefer URL.canParse() or URL.parse() over constructor try/catch; use URL.parse() when parsed object is needed
Files:
app/composables/useUserData.tstest/assets/get-imgproxy-url.test.tstest/pages/posts-video-proxy.test.tstest/server-mocks/plugin.tstest/pages/posts.test.tsapp/assets/js/nuxt-image/get-imgproxy-url.tstest/pages/posts.mock-data.tsapp/plugins/030.pocketbase.ts
{app/components/**/*.vue,app/composables/**/*.ts,app/pages/**/premium/**/*.vue}
📄 CodeRabbit inference engine (AGENTS.md)
Empty cloud state means no user-authored cloud override; do not seed PocketBase from local defaults during initial load, only write premium cloud records after explicit user edits
Files:
app/composables/useUserData.tsapp/components/pages/posts/post/PostMedia.vue
test/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
test/**/*.{ts,tsx}: Use Vitest +@nuxt/test-utilswith Playwright browser mode for testing
Tests use@nuxt/test-utilswith Playwright inside describe blocks calling await setup({ browser: true })
Import debugBrowserOptions from test/helper.ts for headful playback with slowMo in debug mode
Plain Vitest suites importing app modules directly do not get Nuxt runtime alias resolution; keep repository/pure modules importable through relative paths or import directly from app path
@nuxt/test-utils$fetch has no .raw method; use fetch from@nuxt/test-utilswith { redirect: 'manual' } for redirect status and Location headers
Locale-related tests should import localeCodes, prefixedLocaleCodes, and removedLocaleCodes from config/i18n instead of hardcoding locale lists
Files:
test/assets/get-imgproxy-url.test.tstest/pages/posts-video-proxy.test.tstest/server-mocks/plugin.tstest/pages/posts.test.tstest/pages/posts.mock-data.ts
{nuxt.config.ts,test/server-mocks/plugin.ts}
📄 CodeRabbit inference engine (AGENTS.md)
Mock server-side API calls via test-only Nitro plugin at test/server-mocks/plugin.ts, injected through nuxt.config.ts → $test.nitro.plugins
Files:
test/server-mocks/plugin.ts
app/plugins/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Keep global TanStack Vue Query plugin unless a larger measured payoff appears
Files:
app/plugins/030.pocketbase.ts
app/components/**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
app/components/**/*.vue: Register components without path prefix using pathPrefix: false in nuxt.config.ts; import as not <Input/DomainSelector>
Keep@formkit/auto-animateroute-scoped unless used broadly; local vAutoAnimate imports on premium CSR pages saved about 3 KB gzip
Files:
app/components/pages/posts/post/PostMedia.vue
{app/pages/**/*.vue,app/components/**/*.vue}
📄 CodeRabbit inference engine (AGENTS.md)
{app/pages/**/*.vue,app/components/**/*.vue}: Keep schema.org breadcrumb source item URLs as local/locale-relative; do not convert to project.urls.production because nuxt-schema-org resolves JSON-LD against site.url
Use flat bracket keys (filter[sort], filter[rating], etc.) for query filters with Vue Router's default query handling; do not re-add qs unless URL contract changes
Files:
app/components/pages/posts/post/PostMedia.vue
{nuxt.config.ts,app/components/**/*.vue,app/pages/**/*.vue}
📄 CodeRabbit inference engine (AGENTS.md)
Use
@nuxt/imagev2 module API with preload: { fetchPriority: 'high' } for image preload priority instead of patching rendered HTML in Nitro
Files:
app/components/pages/posts/post/PostMedia.vue
{server/api/**/*.ts,app/components/**/*.vue}
📄 CodeRabbit inference engine (AGENTS.md)
Route Gelbooru media through Cloudflare Worker media proxies before imgproxy with clean upstream headers (no CF-, X-Forwarded-, cookies, auth)
Files:
app/components/pages/posts/post/PostMedia.vue
{app/components/**/*.vue,app/pages/**/*.vue}
📄 CodeRabbit inference engine (AGENTS.md)
When a premium prompt is triggered from inside a bottom sheet/dialog, let the sheet/dialog owner close local UI, open the premium prompt, and restore UI after prompt closes
Files:
app/components/pages/posts/post/PostMedia.vue
🧠 Learnings (1)
📚 Learning: 2026-06-22T07:50:54.935Z
Learnt from: AlejandroAkbal
Repo: Rule-34/App PR: 144
File: test/server/fix-canonical-queries.test.ts:174-178
Timestamp: 2026-06-22T07:50:54.935Z
Learning: In this Nuxt 3/4 project using `nuxt-schema-org` (v6.2.1), breadcrumb `source`/`itemListElement` URL values may be provided via `localePath()` (locale-relative in source code), but `nuxt-schema-org` renders JSON-LD with production-absolute URLs using `site.url` during schema rendering. When asserting JSON-LD breadcrumb `itemListElement` URLs in tests, expect production-absolute URLs (e.g., prefix with `project.urls.production.origin`), not the locale-relative `localePath()` forms. Do not flag production-absolute URL strings in JSON-LD breadcrumb tests as incorrect.
Applied to files:
test/assets/get-imgproxy-url.test.tstest/pages/posts-video-proxy.test.tstest/pages/posts.test.ts
🔇 Additional comments (10)
test/pages/posts.mock-data.ts (1)
3837-3879: LGTM!test/server-mocks/plugin.ts (1)
10-10: LGTM!Also applies to: 76-79
test/pages/posts.test.ts (2)
3-7: LGTM!
199-200: LGTM!test/pages/posts-video-proxy.test.ts (1)
1-110: 📐 Maintainability & Code QualityWell-structured E2E tests for video poster proxying.
The test coverage is comprehensive:
- Premium user fallback to CORS proxy on imgproxy failure
- SSR imgproxy poster routing for blocked CDNs
The test helpers (
decodeImgproxySourceUrl,expectPosterSrcToReference,getPostVideoPoster) follow the patterns established inposts.test.tsand provide clear, reusable logic.app/plugins/030.pocketbase.ts (1)
53-77: LGTM!app/composables/useUserData.ts (1)
6-16: LGTM!app/assets/js/nuxt-image/get-imgproxy-url.ts (1)
1-12: LGTM!test/assets/get-imgproxy-url.test.ts (1)
1-30: LGTM!app/components/pages/posts/post/PostMedia.vue (1)
4-67: LGTM!Also applies to: 456-463, 483-483, 787-796
Revert PocketBase auth sync and premium browser tests that depended on it; keep imgproxy poster coverage.
Restore the non-premium and post-retry failure path that was lost when video errors moved to onVideoPlaybackError.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Derive poster URLs from a computed instead of a watcher, fold video proxy retries back into onMediaError, and keep GIF poster state separate from video posters.
Distinguish imgproxy poster routing from CORS-proxy retries, and separate GIF poster state from video poster URLs.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/components/pages/posts/post/PostMedia.vue`:
- Around line 426-437: The poster probe path in PostMedia.vue is incorrectly
letting hidden poster image failures set error state and show the error UI for
an otherwise playable video. Update the load/error handling around the poster
proxy logic in the video poster branch and the shared error path near the video
media handlers so poster-only failures are ignored or retried without assigning
error.value. Use the existing symbols isVideo.value, isPremium.value,
triedToLoadPosterWithProxy.value, videoPosterProxyOverride.value, and
props.mediaPosterSrc to keep the fix scoped to poster probing.
- Around line 56-57: The SSR imgproxy condition in PostMedia should only apply
to the actual LCP poster instead of every SSR video in the first eight slots.
Update the logic around the poster selection in PostMedia.vue so it uses the
existing isLikelyLcpMedia check (postIndex === 0) rather than the broader
wasCurrentPageSSR && props.postIndex < 8 fallback, and keep the premium path
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: a27714fb-bfeb-4831-a5ab-054ffb89c7e3
📒 Files selected for processing (2)
app/components/pages/posts/post/PostMedia.vuetest/pages/posts-video-proxy.test.ts
💤 Files with no reviewable changes (1)
- test/pages/posts-video-proxy.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
app/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
app/**/*.{vue,ts,tsx}: Use Nuxt 4 with Vue 3 and TypeScript for the frontend application
Use useLazyToast() to lazy-load vue-sonner; wait for ClientToaster to mount before calling toast.* using a proper async wait instead of plain nextTick()
Build reordered array synchronously instead of reading VueUse moveArrayElement() result before nextTick for state immediately persisted
Files:
app/components/pages/posts/post/PostMedia.vue
**/*.{vue,ts,tsx,js,jsx,json,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
Use Prettier for formatting with 120-char print width, no semicolons, single quotes, trailing commas removed, and single attribute per line in Vue templates
Files:
app/components/pages/posts/post/PostMedia.vue
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (AGENTS.md)
Use Nuxt flat ESLint via
@nuxt/eslintfor linting
Files:
app/components/pages/posts/post/PostMedia.vue
app/components/**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
app/components/**/*.vue: Register Vue components without path prefix using pathPrefix: false in nuxt.config.ts; import them as not <Input/DomainSelector>
Auto-import components from app/components/ flat structure without folder prefix
Use custom imgproxy provider for images; generate images at 1x density only in webp format to reduce bandwidth
Use@nuxt/imagev2 preload API (preload: { fetchPriority: 'high' }) for image preload priority instead of patching rendered HTML in Nitro
Use Nuxt Lazy* components, dynamic imports, or similarly deferred boundaries for components and composables only needed after user interaction (menu, sheet, dialog)
Files:
app/components/pages/posts/post/PostMedia.vue
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,vue}: Define locales in config/i18n.ts as the single source of truth; do not hardcode locale lists in application code
Use PocketBase batch writes for multi-record replacement/reorder operations instead of one HTTP write per changed row
Files:
app/components/pages/posts/post/PostMedia.vue
app/**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
app/**/*.vue: Use project.urls.production for canonical URLs to point to production (https://r34.app/...) even when served from clone domains
Keep breadcrumb Schema.org source item URLs local/locale-relative; do not convert to project.urls.production since nuxt-schema-org resolves against site.url
When triggering a premium prompt from a nested dialog/bottom sheet, let the sheet owner close local UI, open the prompt, and restore UI after prompt closes
Premium auth transitions are reload-backed in dashboard/sign-in flow; rely on page reload to clear memory state instead of tracking PocketBase user ids in useState
Files:
app/components/pages/posts/post/PostMedia.vue
🔇 Additional comments (1)
app/components/pages/posts/post/PostMedia.vue (1)
4-4: LGTM!Also applies to: 39-43, 85-98, 480-485, 805-805, 814-814
Keep onMediaError as a thin dispatcher while grouping video and GIF retry logic into dedicated functions.
Limit SSR imgproxy to the LCP poster only and treat hidden poster probe failures as non-fatal so playable videos keep their UI.
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
preview_file) through imgproxy for Premium users and SSR LCP posts, matching the existing static-image proxy rules.<video poster>alone does not reliably emit load errors.Out of scope (follow-up)
authStore.onChange→useStatesync (needed for reliable Premium fallback browser tests, but unrelated to the core imgproxy fix).Test plan
pnpm vitest run test/assets/get-imgproxy-url.test.ts test/pages/posts-video-proxy.test.tsSummary by CodeRabbit
New Features
Bug Fixes
Tests