Checklist across security, performance/DOM, feeds/search/trending,
watch page, Shorts, channel/studio/live, and admin/a11y. Branch per
phase; backup tag backup/pre-audit-2026-10-21 at 5058eaf.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
6.6 KiB
6.6 KiB
YouTube-parity audit remediation plan
Branch per phase. Feature tests per fix. STOP for approval between phases.
Safety tag: backup/pre-audit-2026-10-21 at commit 5058eaf on full-screen-data.
Stashed WIP: stash@{0} — pre-audit WIP on full-screen-data (recover with git stash pop on that branch).
Phase 1 — Security & data integrity
Branch: fix/phase-1-security
- Wrap
sendEmailVerificationNotification+NewCommentNotificationmail in try/catch; move to a queued notification channel; log failures. Document.envfor Gmail app password + Mailgun/Postmark fallback. php artisan app:resend-verification {--user=}command.- Reserved-name blocklist (
admin,superadmin,takeone,support,root, …) viaApp\Rules\ReservedIdentifierwired intoRegisteredUserController. - Harden login throttling to
5,1with lockout message; log repeated failures and alert super admins. - Confirm
email_verified_atis not mass-assignable; only the signed-URL verification route flips it. - Comment dedupe: raise 5s → 60s window, add client idempotency key header, server reject on duplicate. Disable submit button on first click.
php artisan comments:dedupe --dry-runcleanup command.- Owner-view exclusion in
VideoController@trackProgress; dedupe bydevice_hashwithin 30 min. - Feature tests for each item.
Phase 2 — Performance & DOM
Branch: fix/phase-2-perf
- One shared
#globalEditModal+#globalCardMenuper page; populated viaGET /videos/{id}/edit-payload. Owner-only items gated server-side. - Lazy-render reply/edit boxes (template clone on open).
- Match highlights panel rendered once.
href="javascript:void(0)"→ real<button>elements. Hide "Not interested" / "Don't recommend" / "Report" on own videos.- Cursor pagination (
?cursor=) across Home,/videosfilters, channel Videos, search, history, liked, comments. - IntersectionObserver infinite scroll + "Load more" fallback.
loading="lazy", explicitwidth/height, skeleton placeholders on thumbnails.
Phase 3 — Feeds, search, trending
Branch: fix/phase-3-feeds
- Music + filter feeds switch to
latest(). - Trending windows: 24 / 168 / 720 h; rank by in-window views.
- Channel Popular: order by views, tiebreak by likes.
- Duration badge on card thumbnail. Scoreboard thumb →
<img>oraria-hidden. StripCLUBLOGO/FLAGplaceholders. - Filter tabs: fetch +
pushState, sticky,aria-current. - Normalize naming (DB stays
match, display label "Sports" everywhere). - "New to You" → "Recent" (or make personalized).
- New
SearchControllerwith/search?q=matching title/description/tags/language/channel; include channels + playlists./videos/searchbecomes 302 redirect. Keep query in input. Merge the two duplicate search forms. /feed/subscriptionsroute backed bysubscriptionstable.
Phase 4 — Watch page
Branch: fix/phase-4-watch
- Up Next: real
<a href>cards. Recommender by type/tags/language, cross-channel. Tabs All / From this channel / Same type. Show more. - Unified player wrapper for match/music/generic: theater (t), mini (i), PiP, volume slider, captions=lyrics (c), 2x speed, end screen, autoplay countdown. Autoplay-ON forces Loop-OFF.
- Like count visible, dislike button, Subscribe for guests, viewer
⋯→ Report. Channel row on match videos. /videos/{id}/download/{format}signed route; enforce per-video Downloads setting (Off/Everyone/Members/Subscribers) server-side for MP4 and MP3.- Comments:
?lc=permalink with scroll + highlight. Heart + pin (creator). "Read more" clamp. @handle prefill on nested reply. Singular/plural counts. Top-level vs reply counts separate. - Markdown rendered (or stripped) in descriptions.
- Dedupe audio Language menu.
- Hide "Generating lyrics…" overlay unless job active.
- Edit form prefilled with real values (fix "Generic" / "Loading…" defaults).
- Streaming: HLS exists (
GenerateHlsJob,/videos/{video}/hls/{file}). Switch player to hls.js whenhls_pathset. Verify-movflags +faststarton mp4 encode inCompressVideoJob. AddCache-Control+ Range on stream route.
Phase 5 — Shorts
Branch: fix/phase-5-shorts
- Upload flag + auto-detect (vertical, ≤60s).
/shorts/{id}route + branded 404 atresources/views/errors/404.blade.php./shortsfeed: 100dvh snap container, single reused<video>moved between slots, next 1–2 preloaded, URL viahistory.replaceState, wheel + arrows + swipe + ▲▼. Loop. Muted autoplay + "Tap to unmute"; unmuted state inlocalStorage.- Right action column (like/dislike/comments/share). Bottom overlay (avatar/@handle/Subscribe/title). Comments side panel desktop / bottom sheet mobile.
- Home Shorts shelf. Channel Shorts tab (fix "My Shorts").
Phase 6 — Channel / Studio / Live
Branch: fix/phase-6-channel-studio-live
users.handlemigration (unique, nullable → backfill → required)./@handleroutes. Tabs/@handle/{videos,shorts,playlists,posts,about}. "Preview as visitor" toggle. Header shows description + links. Age/gender/horoscope private by default. Pluralize "1 video"; drop stray "· 0.0" on playlist cards./studio/*: content table + bulk edit, per-video analytics, two-step upload (Details → Visibility: tags + playlist + schedule), file-size + duration + processing-status surfaced.- Live: ended state → "Stream ended" + duration + recording (never "Waiting"). Live chat. Like / share / subscribe on viewer page. Channel name link. Private option on
/live/newmatching upload wording.
Phase 7 — Admin, a11y, consistency
Branch: fix/phase-7-admin-a11y
- Rename "Live Videos" → "Ready". Storage counts NAS (via
NasSyncService). Fix KPI WoW %. Strip HTML in description excerpts. Full titles with tooltip. Next/Last pagination on Audit Logs, Users, Videos. Error log viewer groups by stack trace in natural order. - A11y:
aria-labelon every icon button (menu, search, voice, avatar, ⋯, Like-active, comment menus, seek slider with value text). Skip-to-content link. One<h1>per page.label[for]on all inputs. Remove duplicate "Prefer not to say". Birth year 13+ cap. - Title helper
{page} | Play. Brand: Play displayed;TAKEONEstays asconfig('app.name')per CLAUDE.md. - Cleanup commands (all
--dry-runfirst):comments:dedupe,live:cleanup-test,tracks:dedupe.
Final deliverables
- Checklist: done / partial / skipped (with reason).
.envkeys the user must set.- Deploy steps: migrations, queue restart, cache clear, asset build.