Add autoPlay/playsInline to the local video element, autoplay: 1 to the YouTube IFrame API playerVars, and ?autoplay=1 to the plain-iframe fallback. Best-effort: browsers may block unmuted autoplay, so the play button stays as fallback.
10 KiB
10 KiB
Автозапуск видео при переходе на страницу видео (best-effort, без muted)
Задача
При переходе на страницу видео (YouTube-ветка или локальная копия) плеер должен пытаться начать воспроизведение сразу, без ручного нажатия play. Реализация — best-effort, без костылей: звук не мутируем, поэтому там, где браузер разрешает автозапуск со звуком, видео стартует само; где запрещает — остаётся обычная кнопка play, поведение не хуже текущего.
Три точки изменения в frontend/src/components/Player.tsx:
- Локальная ветка
<video controls>: добавить атрибутautoPlay(+playsInlineдля iOS — проверить, что его ещё нет). - YouTube-ветка (IFrame Player API): добавить
autoplay: 1вplayerVars(существующийplaysinline: 1сохранить). - Фолбэк plain-iframe: добавить параметр
?autoplay=1кsrc.
Контекст
- Текущее состояние
Player.tsx(298 строк, последний коммит «Add seek controls to the player»):- Локальная ветка (строки 247–272):
<video ref={videoRef} controls src={video.local.media_url!} onError={…} />— атрибутовautoPlay/playsInline/mutedнет (grep поfrontend/src:mutedне встречается,playsinline— только вplayerVars, строка 181). - YouTube-ветка (строки 178–192):
new YT.Player(host, { videoId, host: 'https://www.youtube-nocookie.com', playerVars: { playsinline: 1 }, events: { onReady } })—autoplayотсутствует. - Фолбэк при таймауте API (строки 277–283):
<iframe src="https://www.youtube-nocookie.com/embed/{id}" title allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />— вsrcпараметров нет;allowуже содержитautoplay(Permissions Policy для iframe, менять не нужно).
- Локальная ветка (строки 247–272):
Playerмонтируется только вVideoPage.tsx:24сkey={video.youtube_video_id}: при переходе на страницу видео компонент монтируется заново, смена видео — ремоунт. Отдельной логики «перезапуска при навигации» не требуется: автозапуск срабатывает на каждый свежий монтаж.- Локальная ветка выбирается по
video.local.available && !!video.local.media_url && !localFailed; приonErrorлокального видео — переход в YouTube-ветку. Автозапуск добавляется в обе ветки независимо, существующая логика выбора ветки не меняется. - Frontend без тестовой инфраструктуры; проверки —
npm run lint(oxlint) иnpm run build(tsc -b && vite build). Backend, миграции, README — не трогаем.
Затронутые подсистемы и файлы
Только frontend, один файл:
frontend/src/components/Player.tsx:- локальная ветка:
<video>— добавитьautoPlayиplaysInline; - YouTube-ветка:
playerVars: { playsinline: 1 }→playerVars: { playsinline: 1, autoplay: 1 }; - фолбэк-iframe:
src→https://www.youtube-nocookie.com/embed/{id}?autoplay=1.
- локальная ветка:
App.css, VideoPage.tsx, backend, тесты, миграции, README — без изменений.
Критерии приёмки
- Автозапуск настроен на обеих ветках и в фолбэке:
autoPlay/playsInlineна<video>,autoplay: 1вplayerVars,?autoplay=1вsrcфолбэка. - Звук не мутируется:
mutedне добавляется нигде (<video>,playerVars, параметры iframe); best-effort — где браузер разрешит автозапуск со звуком, видео стартует, где нет — остаётся кнопка play. - Существующая логика не сломана: кнопки «−10 сек»/«+10 сек» (обе ветки, disabled до
onReadyв YouTube-ветке), двойной тап локального плеера,onReady-логика (сброс таймаута,setYtApiStatus('ready')), фолбэк plain-iframe по таймауту,destroy()в cleanup — работают как раньше. npm run lintиnpm run buildвfrontend/чистые.- Backend не тронут: изменения только в
frontend/src/components/Player.tsxиanalytics/;pytestне требуется. - Деплой по правилу:
docker compose up -d --build,curl http://localhost:8080/api/health→ OK.
План
Player.tsx, локальная ветка (строки 251–259): к<video>добавить атрибутыautoPlayиplaysInline(React camelCase) — рядом сcontrols, доsrc. Больше ничего в этой ветке не менять.Player.tsx, YouTube-ветка (строка 181):playerVars: { playsinline: 1 }→playerVars: { playsinline: 1, autoplay: 1 }(типRecord<string, string | number>— число допустимо).onReady, таймеры,host: youtube-nocookie.comне трогать.Player.tsx, фолбэк (строка 279):src→`https://www.youtube-nocookie.com/embed/${video.youtube_video_id}?autoplay=1`.title/allow/allowFullScreenне менять (allowуже содержитautoplay).- Проверки:
npm run lint,npm run build; ручная — переход на страницу локального видео и YouTube-видео в браузерах с разной политикой автозапуска (Chrome с/без истории взаимодействия, Safari/iOS, Firefox): где разрешено — старт без нажатия, где запрещено — кнопка play; проверить кнопки ±10 сек, двойной тап, фолбэк (offline/блокировка youtube.com в DevTools); деплой + health.
Риски и ограничения
- Политика автозапуска браузеров (главный риск, принят осознанно): автозапуск со звуком блокируется по умолчанию в Chrome (требует Media Engagement Index / недавнее взаимодействие пользователя с сайтом), Safari и практически всегда на iOS/iPadOS, где автозапуск возможен только с
muted. Мы намеренно не мутим (пользователю нужен звук), поэтому результат — best-effort: где браузер разрешит — стартует, где нет — остаётся кнопка play. Это ожидаемое и принятое поведение, а не баг. - iOS: даже с
playsInlineавтозапуск со звуком будет заблокирован;playsInlineдобавляем, чтобы не было попытки fullscreen и чтобы воспроизведение после нажатия play шло инлайн, как сейчас у YouTube-ветки (playsinline: 1). - YouTube embed:
autoplay: 1безmute: 1часто блокируется без предшествующего взаимодействия с доменом YouTube; в фолбэке plain-iframe политика та же.mute: 1сознательно не добавляем (критерий 2). - Консоль: при заблокированном автозапуске браузер может выводить предупреждение (для
<video autoplay>— заметка о блокировке, иногда rejected play promise в DevTools) — это штатное поведение best-effort, не ошибка приложения; новых unhandled rejection в коде приложения нет (мы не вызываемplay()вручную). - Не трогать существующую логику:
playerVarsменяется одной строкой;onReady/таймаут/cleanup и рендер фолбэка не затрагиваются. При рефакторинге не добавлятьmutedи не менять порядок/содержимоеallow. - Ремоунт по
key: автозапуск происходит при каждом монтированииPlayer(переход на страницу, смена видео) — это ожидаемо; при возврате со страницы видео и повторном заходе видео начнётся заново с начала, как и сейчас при обычном play.
Журнал изменений
- 2026-09-25: документ создан перед реализацией. Зафиксированы факты по коду:
<video controls>безautoPlay/playsInline/muted(Player.tsx:251–259);playerVars: { playsinline: 1 }безautoplay(строка 181); фолбэк-iframesrcбез параметров (строка 279),allowуже содержитautoplay;Playerмонтируется только вVideoPage.tsx:24сkey. Решения:autoPlay+playsInlineна<video>;autoplay: 1вplayerVars(сохранитьplaysinline: 1);?autoplay=1вsrcфолбэка;mutedне добавлять нигде — best-effort со звуком, блокировка браузерами (особенно iOS) принята и описана в «Рисках». README не трогаем; backend не трогаем.