Autoplay video when opening the video page
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.
This commit is contained in:
parent
a89f972afa
commit
b1faeb3729
2 changed files with 64 additions and 2 deletions
60
analytics/2026-09-25-video-autoplay.md
Normal file
60
analytics/2026-09-25-video-autoplay.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Автозапуск видео при переходе на страницу видео (best-effort, без muted)
|
||||
|
||||
## Задача
|
||||
|
||||
При переходе на страницу видео (YouTube-ветка или локальная копия) плеер должен пытаться начать воспроизведение сразу, без ручного нажатия play. Реализация — best-effort, без костылей: звук не мутируем, поэтому там, где браузер разрешает автозапуск со звуком, видео стартует само; где запрещает — остаётся обычная кнопка play, поведение не хуже текущего.
|
||||
|
||||
Три точки изменения в `frontend/src/components/Player.tsx`:
|
||||
1. Локальная ветка `<video controls>`: добавить атрибут `autoPlay` (+ `playsInline` для iOS — проверить, что его ещё нет).
|
||||
2. YouTube-ветка (IFrame Player API): добавить `autoplay: 1` в `playerVars` (существующий `playsinline: 1` сохранить).
|
||||
3. Фолбэк 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, менять не нужно).
|
||||
- `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 — без изменений.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
1. Автозапуск настроен на обеих ветках и в фолбэке: `autoPlay`/`playsInline` на `<video>`, `autoplay: 1` в `playerVars`, `?autoplay=1` в `src` фолбэка.
|
||||
2. Звук не мутируется: `muted` не добавляется нигде (`<video>`, `playerVars`, параметры iframe); best-effort — где браузер разрешит автозапуск со звуком, видео стартует, где нет — остаётся кнопка play.
|
||||
3. Существующая логика не сломана: кнопки «−10 сек»/«+10 сек» (обе ветки, disabled до `onReady` в YouTube-ветке), двойной тап локального плеера, `onReady`-логика (сброс таймаута, `setYtApiStatus('ready')`), фолбэк plain-iframe по таймауту, `destroy()` в cleanup — работают как раньше.
|
||||
4. `npm run lint` и `npm run build` в `frontend/` чистые.
|
||||
5. Backend не тронут: изменения только в `frontend/src/components/Player.tsx` и `analytics/`; `pytest` не требуется.
|
||||
6. Деплой по правилу: `docker compose up -d --build`, `curl http://localhost:8080/api/health` → OK.
|
||||
|
||||
## План
|
||||
|
||||
1. `Player.tsx`, локальная ветка (строки 251–259): к `<video>` добавить атрибуты `autoPlay` и `playsInline` (React camelCase) — рядом с `controls`, до `src`. Больше ничего в этой ветке не менять.
|
||||
2. `Player.tsx`, YouTube-ветка (строка 181): `playerVars: { playsinline: 1 }` → `playerVars: { playsinline: 1, autoplay: 1 }` (тип `Record<string, string | number>` — число допустимо). `onReady`, таймеры, `host: youtube-nocookie.com` не трогать.
|
||||
3. `Player.tsx`, фолбэк (строка 279): `src` → `` `https://www.youtube-nocookie.com/embed/${video.youtube_video_id}?autoplay=1` ``. `title`/`allow`/`allowFullScreen` не менять (`allow` уже содержит `autoplay`).
|
||||
4. Проверки: `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); фолбэк-iframe `src` без параметров (строка 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 не трогаем.
|
||||
Loading…
Add table
Add a link
Reference in a new issue