# Багфикс: звук в ленте Shorts сбрасывается при листании ## Задача Устранить регрессию ленты Shorts `/shorts/:youtubeVideoId`: при листании к следующему ролику звук снова выключен, даже если пользователь включил его на текущем слайде. Требуется, чтобы выбор звука («вкл/выкл») сохранялся на все последующие слайды в рамках открытой ленты, не ломая autoplay. Это отдельный багфикс поверх механики ленты из `analytics/2026-09-27-shorts-split-and-feed.md` (документ исторический, его решения не переписываются). ## Контекст Баг воспроизводится и подтверждён по фактическому коду (ветка `master`, правки shorts в незакоммиченном worktree): - `frontend/src/components/ShortsPlayer.tsx:16` держит звук локально: `const [muted, setMuted] = useState(true)`. Это состояние живёт ровно столько, сколько смонтирован конкретный плеер. - `frontend/src/pages/ShortsFeed.tsx:162-163` монтирует `` **только для активного слайда**, а неактивные показывает постером. При смене активного слайда старый плеер размонтируется, новый создаётся заново — и снова с `muted = true`. - Для YouTube это усилено на двух уровнях: `playerVars: { ..., mute: 1 }` при создании (`ShortsPlayer.tsx:57`) и `player.mute?.()` в `onReady` (`ShortsPlayer.tsx:66`). - Итог: состояние звука не переносится между роликами, потому что единственный его владелец — плеер одного слайда. Это следствие осознанного решения «плеер монтируется только для активного слайда» из `2026-09-27-shorts-split-and-feed.md` (гарантированная остановка звука/воспроизведения при свайпе без ручного `pause`). Решение остаётся в силе; меняется только владелец флага звука. Потребителей у `ShortsPlayer` ровно один — `ShortsFeed.tsx` (подтверждено grep по `frontend/src`), поэтому изменение пропсов безопасно и локально. ## Затронутые подсистемы и файлы Frontend (единственная подсистема): - `frontend/src/pages/ShortsFeed.tsx` — новый источник правды для звука: `const [muted, setMuted] = useState(true)` на уровне ленты; передать `muted` и `onToggleMute` в `ShortsPlayer`. - `frontend/src/components/ShortsPlayer.tsx` — убрать локальный `useState(muted)`; принимать `muted: boolean` и `onToggleMute: () => void`; применять состояние к обоим типам плеера; кнопка звука управляет общим состоянием. - `frontend/src/utils/youtube.ts` — изменений не требуется (`YoutubePlayerApi` уже содержит опциональные `mute?`/`unMute?`). - `frontend/src/App.css` — изменений не требуется (класс `.shorts-mute` и иконка не меняются). Backend, миграции, env — не затрагиваются. Тестов на frontend в проекте нет; проверка — `npm run lint` и `npm run build`. ## Проектное решение (без костылей) ### Владелец состояния Единый источник правды — `ShortsFeed` (живёт, пока открыта лента, включая подгрузку следующих страниц). `ShortsPlayer` становится полностью контролируемым презентационным компонентом: пропсы `muted` + `onToggleMute`, без собственного `useState`. Почему не `sessionStorage`/`localStorage` и не глобальный store: это единичное UI-состояние одной страницы, лишняя инфраструктура не нужна; кроме того, персист между перезагрузками запрещён (см. ниже). ### Локальное видео `