itube-modern-player 0.7.2 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -5
- package/dist/core.cjs +4 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +696 -583
- package/dist/core.js.map +1 -1
- package/dist/gesture-4dfbCdW5.cjs +2 -0
- package/dist/gesture-4dfbCdW5.cjs.map +1 -0
- package/dist/gesture-SgY5sB4t.js +11 -0
- package/dist/gesture-SgY5sB4t.js.map +1 -0
- package/dist/gesture.d.ts +18 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +22 -23
- package/dist/index.js.map +1 -1
- package/dist/itube-modern-player.iife.js +3 -3
- package/dist/itube-modern-player.iife.js.map +1 -1
- package/dist/lazy.cjs +1 -1
- package/dist/lazy.cjs.map +1 -1
- package/dist/lazy.d.ts +10 -2
- package/dist/lazy.js +56 -58
- package/dist/lazy.js.map +1 -1
- package/dist/placeholder.cjs +2 -0
- package/dist/placeholder.cjs.map +1 -0
- package/dist/placeholder.d.ts +32 -0
- package/dist/placeholder.js +42 -0
- package/dist/placeholder.js.map +1 -0
- package/dist/player.d.ts +10 -0
- package/dist/types.d.ts +39 -5
- package/dist/vue.cjs +1 -1
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.ts +68 -5
- package/dist/vue.js +137 -38
- package/dist/vue.js.map +1 -1
- package/package.json +7 -2
- package/dist/dom-Bq7EQenh.cjs +0 -5
- package/dist/dom-Bq7EQenh.cjs.map +0 -1
- package/dist/dom-DrpWbY0y.js +0 -87
- package/dist/dom-DrpWbY0y.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Содержание
|
|
8
8
|
|
|
9
|
+
- [Подключить за 2 минуты (копипаст)](#подключить-за-2-минуты-копипаст)
|
|
9
10
|
- [Возможности](#возможности)
|
|
10
11
|
- [Установка](#установка)
|
|
11
12
|
- [Быстрый старт (vanilla JS / TS)](#быстрый-старт-vanilla-js--ts)
|
|
@@ -40,6 +41,87 @@
|
|
|
40
41
|
- [Сборка и публикация](#сборка-и-публикация)
|
|
41
42
|
- [История изменений](#история-изменений)
|
|
42
43
|
|
|
44
|
+
## Подключить за 2 минуты (копипаст)
|
|
45
|
+
|
|
46
|
+
Три готовых рецепта. Скопируй подходящий целиком, подставь свои URL — работает.
|
|
47
|
+
|
|
48
|
+
### Рецепт 1: просто HTML-страница (без npm и сборщика)
|
|
49
|
+
|
|
50
|
+
Сохрани как `index.html`, открой в браузере. Всё.
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<!doctype html>
|
|
54
|
+
<html>
|
|
55
|
+
<head>
|
|
56
|
+
<link rel="stylesheet" href="https://unpkg.com/itube-modern-player/dist/style.css">
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<div id="player"></div>
|
|
60
|
+
|
|
61
|
+
<!-- hls.js нужен ТОЛЬКО если видео — .m3u8 (стрим). Для .mp4 эту строку можно удалить. -->
|
|
62
|
+
<script src="https://unpkg.com/hls.js"></script>
|
|
63
|
+
<script src="https://unpkg.com/itube-modern-player/dist/itube-modern-player.iife.js"></script>
|
|
64
|
+
<script>
|
|
65
|
+
new ITubePlayer('#player', {
|
|
66
|
+
source: {
|
|
67
|
+
src: 'https://example.com/video.m3u8', // ← твоё видео (.m3u8 или .mp4)
|
|
68
|
+
poster: 'https://example.com/poster.jpg', // ← твоя обложка
|
|
69
|
+
title: 'Название ролика',
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
</script>
|
|
73
|
+
</body>
|
|
74
|
+
</html>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Что получится: обложка с кнопкой play; по клику — воспроизведение со всеми контролами.
|
|
78
|
+
|
|
79
|
+
### Рецепт 2: проект со сборщиком (Vite / webpack / любой)
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm i itube-modern-player
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { Player } from 'itube-modern-player'
|
|
87
|
+
import 'itube-modern-player/style.css'
|
|
88
|
+
|
|
89
|
+
new Player('#player', {
|
|
90
|
+
source: { src: '/video.m3u8', poster: '/poster.jpg', title: 'Название' },
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
hls.js для `.m3u8` подтянется сам, отдельным чанком — ставить и импортировать его не нужно.
|
|
95
|
+
|
|
96
|
+
### Рецепт 3: Nuxt 3 / Vue 3 (рекомендуемый — быстрый LCP из коробки)
|
|
97
|
+
|
|
98
|
+
```vue
|
|
99
|
+
<script setup lang="ts">
|
|
100
|
+
import { ITubePlayer } from 'itube-modern-player/vue'
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<template>
|
|
104
|
+
<!-- lazy = постер приходит в первом HTML (SSR), тяжёлый плеер качается
|
|
105
|
+
только когда пользователь потянулся к видео. Никаких ClientOnly. -->
|
|
106
|
+
<ITubePlayer
|
|
107
|
+
lazy
|
|
108
|
+
:source="{ src: '/video.m3u8', poster: '/poster.jpg', title: 'Название' }"
|
|
109
|
+
:options="{ muted: true }"
|
|
110
|
+
@ended="onEnded"
|
|
111
|
+
/>
|
|
112
|
+
</template>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Если что-то не так
|
|
116
|
+
|
|
117
|
+
| Симптом | Причина и лечение |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| Видео не стартует само | Браузеры блокируют автоплей со звуком. Нужен автостарт — `playOnInit: true` **плюс** `muted: true`. Иначе — старт только по клику, это норма |
|
|
120
|
+
| Чёрный прямоугольник вместо плеера | Не подключён `style.css` (рецепт 1 — `<link>`, рецепт 2 — `import`) |
|
|
121
|
+
| `.m3u8` не играет (в рецепте 1) | Удалил строку с hls.js — верни её |
|
|
122
|
+
| Контролы на английском | Добавь в опции `language: 'ru'` |
|
|
123
|
+
| Реклама не нужна | Просто не передавай `adConfig` — её и не будет |
|
|
124
|
+
|
|
43
125
|
## Возможности
|
|
44
126
|
|
|
45
127
|
- **Источники** — MP4/WebM, HLS-стримы (`.m3u8`, hls.js или нативно в Safari), live-потоки с бейджем LIVE, прогрессивные качества с переключением без потери позиции, HLS-уровни с пунктом Auto.
|
|
@@ -267,7 +349,7 @@ const source: VideoSource = {
|
|
|
267
349
|
|
|
268
350
|
Стандартный формат — WebVTT, каждый cue указывает картинку и регион спрайта:
|
|
269
351
|
|
|
270
|
-
```
|
|
352
|
+
```text
|
|
271
353
|
WEBVTT
|
|
272
354
|
|
|
273
355
|
00:00:00.000 --> 00:00:05.000
|
|
@@ -365,11 +447,15 @@ new Player('#mount', {
|
|
|
365
447
|
// перечисленные идут первыми, остальные сохраняют дефолтную позицию, ⋯ всегда последняя.
|
|
366
448
|
order: ['gear', 'pip', 'fullscreen'], // ControlBarItem[]: like|dislike|speed|quality|subtitles|gear|scenes|sceneTypes|playlist|pip|fullscreen|`custom:<id>`
|
|
367
449
|
seekPlacement: 'overlay', // 'overlay' (default) — ±N и play поверх видео на всех экранах
|
|
368
|
-
//
|
|
369
|
-
|
|
450
|
+
// | 'bar' — seek-кнопки в нижнем баре (поведение до 0.3)
|
|
451
|
+
mobileLayout: 'bar', // мобильный (≤767px) лейаут:
|
|
452
|
+
// 'bar' (default, с 0.8.1) — поверх видео только ±N и play (как на десктопе),
|
|
453
|
+
// prev/next живут в нижнем баре слева, рядом с play;
|
|
454
|
+
// 'center' — прежний лейаут: prev · −N · play · +N · next большим
|
|
455
|
+
// кластером по центру видео, play/next в баре скрыты
|
|
370
456
|
playlist: true, // prev/next/список — рендерятся ТОЛЬКО в режиме плейлиста
|
|
371
457
|
hidePrev: true, // скрыть кнопку «prev» в баре (бар = только next); false — вернуть.
|
|
372
|
-
//
|
|
458
|
+
// При mobileLayout: 'center' центр-кластер всё равно показывает prev
|
|
373
459
|
nextPreview: true, // ховер-превью следующего ролика над кнопкой next (десктоп).
|
|
374
460
|
// объект: { thumbnail?, title?, duration?, meta? } — какие поля показывать
|
|
375
461
|
// (иконки чанков задаются в самих source.previewMeta — см. { text, icon })
|
|
@@ -620,13 +706,38 @@ player.off('timeupdate', fn)
|
|
|
620
706
|
|
|
621
707
|
## Vue 3: `<ITubePlayer>`
|
|
622
708
|
|
|
709
|
+
Ядро подтягивается **динамическим импортом** — сам компонент весит ~2 KB gzip и не тащит плеер в главный бандл приложения.
|
|
710
|
+
|
|
623
711
|
| Что | API |
|
|
624
712
|
| --- | --- |
|
|
625
713
|
| Пропсы | `source: VideoSource \| VideoSource[]` (реактивный — смена вызывает `load()`), `options: Omit<PlayerOptions, 'source'>` |
|
|
714
|
+
| `lazy` | **poster-first, player-on-intent**: компонент рендерит декларативную SSR-заглушку (постер уходит в первый HTML и становится LCP-кандидатом — `<ClientOnly>` не нужен), по триггеру качает чанк ядра + CSS и свапает через реактивное состояние. Клик по заглушке = загрузить и играть; фоновые триггеры не автоплеят |
|
|
715
|
+
| `loadOn` | триггер при `lazy`: `'placeholder'` (по умолчанию — любой интерактив в зоне заглушки), `'interaction'`, `'visible'`, `'immediate'` |
|
|
716
|
+
| `adConfig` | `AdsOptions` или **async-фабрика** `() => Promise<AdsOptions \| undefined>` (например, запрос VAST-тега) — выполняется параллельно загрузке чанка, плеер конструируется с готовым конфигом. Приоритетнее `options.adConfig` |
|
|
626
717
|
| События | Все события `PlayerEventMap` ретранслируются 1:1 (`@timeupdate`, `@aderror`, `@customaction`, …) |
|
|
627
718
|
| Слоты | `#pauseScreen="{ player, close }"` — контент экрана паузы (scoped: доступ к плееру и `close()`) |
|
|
628
719
|
| Expose | `player: ShallowRef<Player \| null>` — доступ к ядру: `playerRef.value.player.seek(0)` |
|
|
629
720
|
|
|
721
|
+
```vue
|
|
722
|
+
<!-- Nuxt 3: без ClientOnly, без ручного постера, без createLazyPlayer -->
|
|
723
|
+
<ITubePlayer lazy :source="video" :options="{ muted: true }" :ad-config="fetchVast" @ended="onEnded" />
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
CSS компонент подгружает сам (self-reference `itube-modern-player/style.css` через exports пакета); ручной импорт `style.css` в приложении продолжает работать и дедуплицируется бандлером — это фолбэк для экзотических сборок.
|
|
727
|
+
|
|
728
|
+
## Изоморфный шаблон заглушки: `itube-modern-player/placeholder`
|
|
729
|
+
|
|
730
|
+
`renderPlaceholder(options, flags?) → string` — чистая строка без DOM-зависимостей, единый источник правды по разметке «постер + кнопка play». Используется внутри `createLazyPlayer` и Vue-`lazy`; экспортируется для SSR любого фреймворка:
|
|
731
|
+
|
|
732
|
+
```ts
|
|
733
|
+
import { renderPlaceholder } from 'itube-modern-player/placeholder'
|
|
734
|
+
// options: { poster?, icon?, styling?, className?, playLabel? }
|
|
735
|
+
// flags: { inlineStyles?: true } — самодостаточные инлайн-стили (SSR без style.css)
|
|
736
|
+
const html = renderPlaceholder({ poster: { src: '/p.jpg', width: 1280, height: 720 } }, { inlineStyles: true })
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
Там же `posterSrc(poster)` — нормализация `PosterSource` в URL.
|
|
740
|
+
|
|
630
741
|
## Экспортируемые типы
|
|
631
742
|
|
|
632
743
|
`PlayerOptions, VideoSource, ChannelInfo, SubtitleTrack, Chapter, QualityLevel, PlaylistOptions, ControlsOptions, ActionsOptions, CustomAction, BuiltinActionId, RelatedOptions, RelatedItem, AdsOptions, AdRoll, ResolvedAd, ThumbnailCue, PlayerLabels, IconName, PlayerEvent, PlayerEventMap, Level, SourceController, NormalizedChapter`
|
|
@@ -769,7 +880,8 @@ npm publish # prepublishOnly прогонит typecheck + build
|
|
|
769
880
|
| Импорт | Что это |
|
|
770
881
|
| --- | --- |
|
|
771
882
|
| `itube-modern-player` | ядро (`Player`, все типы, утилиты) |
|
|
772
|
-
| `itube-modern-player/vue` | Vue 3-компонент `ITubePlayer` |
|
|
883
|
+
| `itube-modern-player/vue` | Vue 3-компонент `ITubePlayer` (ядро — динамическим импортом; проп `lazy`) |
|
|
884
|
+
| `itube-modern-player/placeholder` | изоморфный `renderPlaceholder()` + `posterSrc()` (без DOM — можно на сервере) |
|
|
773
885
|
| `itube-modern-player/style.css` | стили (подключить один раз) |
|
|
774
886
|
|
|
775
887
|
---
|
|
@@ -778,6 +890,20 @@ npm publish # prepublishOnly прогонит typecheck + build
|
|
|
778
890
|
|
|
779
891
|
Версионирование по [SemVer](https://semver.org/lang/ru/): `major.minor.patch`.
|
|
780
892
|
|
|
893
|
+
### 0.8.1
|
|
894
|
+
|
|
895
|
+
- **Новый мобильный лейаут по умолчанию** — `controls.mobileLayout: 'bar' | 'center'`. `'bar'` (default): поверх видео остаются только ±N-перемотка и большая play (как на десктопе), а кнопка play и «следующее видео» доступны в нижнем баре слева — по аналогии с десктопом. `'center'` — прежний вариант (prev · −N · play · +N · next кластером по центру, play/next в баре скрыты); вернуть старое поведение: `controls: { mobileLayout: 'center' }`.
|
|
896
|
+
- **Фикс: двойной тап для старта видео на мобиле** (`lazy` + `loadOn` + `muted: false`). Автостарт после клика по заглушке проходил две async-границы (загрузка чанка + attach HLS) — жест «протухал», мобильные браузеры (особенно iOS Safari) реджектили `play()` со звуком, играло только со второго тапа. Теперь `<video playsinline>` создаётся и «благословляется» синхронно в обработчике клика (`play()` в рамках жеста; новый модуль `gesture.ts`) и передаётся плееру через новую опцию `PlayerOptions.videoElement` — ядро не создаёт свой элемент, и отложенный автостарт со звуком проходит с первого тапа. Работает и в `createLazyPlayer`, и во Vue-`lazy`. Заодно reject'ы `play()` больше не глушатся молча — причина логируется через `console.debug('[itube-player] play() rejected: …')`.
|
|
897
|
+
- **Фикс: кнопка выбора типа тайм-кодов появлялась с опозданием или «не появлялась вовсе»** при наличии `sceneGroups`. Две причины: (1) селектор и сегменты таймлайна ждали `loadedmetadata` — у ещё не запущенного стрима метаданные могут не прийти до старта; теперь группа применяется сразу (хватает явных `end` сцен и/или заявленной `source.duration`), а по приходу реальной длительности перенормируется; (2) на узком мобильном баре кнопка сваливалась в ⋯-меню одной из первых — приоритет поднят (26 → 68), теперь она уходит в overflow последней из фич-кнопок. Попутно: `normalizeChapters` больше не пропускает сцену с неизвестным `end` (NaN проходил сравнение и ломал сегмент).
|
|
898
|
+
- **Прогресс-бар интерактивен до старта видео.** Если у источника указана `source.duration`, полоса перемотки активна ещё до первого запуска: потянул/кликнул — плеер стартует сразу с нужного места (сиик применяется по `loadedmetadata`; пре-ролл, если настроен, отрабатывает как при обычном старте).
|
|
899
|
+
|
|
900
|
+
### 0.8.0
|
|
901
|
+
|
|
902
|
+
- **Vue-обёртка: ядро — динамическим импортом.** `itube-modern-player/vue` больше не тащит весь плеер в бандл приложения (~2 KB gzip вместо ~30): чанк ядра качается на mount, а с новым пропом **`lazy`** — по интенту. `lazy` = poster-first: декларативная SSR-заглушка (постер в первом HTML, LCP-кандидат, `<ClientOnly>` не нужен), триггер `loadOn` (`'placeholder'` по умолчанию / `'interaction'` / `'visible'` / `'immediate'`), своп через реактивное состояние — никакого чужого DOM. CSS подгружается самим компонентом (self-reference через exports; ручной импорт `style.css` остаётся рабочим фолбэком). NB: `import { Player } from '…/vue'` теперь только тип (рантайм-класс — из главного entry).
|
|
903
|
+
- **`adConfig` как async-фабрика** в lazy-слоях (`createLazyPlayer` и Vue-компонент): `adConfig: () => Promise<AdsOptions | undefined>` выполняется параллельно загрузке чанка — интегратору не нужно самому синхронизировать VAST-запрос с инициализацией.
|
|
904
|
+
- **Изоморфный шаблон заглушки** — новый entry `itube-modern-player/placeholder`: `renderPlaceholder(options, { inlineStyles? }) → string` без DOM-зависимостей (SSR любого фреймворка) + `posterSrc()`. Единый источник правды по разметке: `createLazyPlayer` и Vue-`lazy` рендерят её же — кастомная иконка `bigPlay`, `styling`, постер с полными `<img>`-атрибутами.
|
|
905
|
+
- **`poster` как объект.** `VideoSource.poster: string | { src, srcset?, sizes?, width?, height?, alt? }` — LCP-качество главного постера (responsive srcset, intrinsic-размеры против layout shift); тумбы используют `src`. Строка работает как раньше.
|
|
906
|
+
|
|
781
907
|
### 0.7.2
|
|
782
908
|
|
|
783
909
|
- **Скип битых роликов в «следующем видео».** Если при подготовке превью следующего ролика (ховер-карточка / end-оверлей) его постер не загрузился (4xx/5xx/сеть) — видео тоже не загрузится: источник помечается мёртвым и все вычисления «следующего» (превью, `next()`, автопереход, `hasNext`) пропускают его, подставляя следующий живой из списка. Карточка перерисовывается на лету; если живых впереди нет — прячется, next-кнопка дизейблится. Публичный метод `player.markSourceDead(source)` — пометить источник вручную. Панель плейлиста по-прежнему показывает все элементы.
|