myYouTube/analytics/2026-09-25-video-autoplay.md
vrubelroman b1faeb3729 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.
2026-09-25 12:06:11 +00:00

60 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Автозапуск видео при переходе на страницу видео (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 не трогаем.