itube-modern-player 0.7.1 → 0.8.0
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 +128 -4
- package/dist/core.cjs +3 -3
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +571 -530
- package/dist/core.js.map +1 -1
- package/dist/index.js +4 -4
- 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 -51
- 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 +12 -0
- package/dist/types.d.ts +19 -2
- package/dist/vue.cjs +1 -1
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.ts +68 -5
- package/dist/vue.js +131 -38
- package/dist/vue.js.map +1 -1
- package/package.json +7 -2
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
|
|
@@ -620,13 +702,38 @@ player.off('timeupdate', fn)
|
|
|
620
702
|
|
|
621
703
|
## Vue 3: `<ITubePlayer>`
|
|
622
704
|
|
|
705
|
+
Ядро подтягивается **динамическим импортом** — сам компонент весит ~2 KB gzip и не тащит плеер в главный бандл приложения.
|
|
706
|
+
|
|
623
707
|
| Что | API |
|
|
624
708
|
| --- | --- |
|
|
625
709
|
| Пропсы | `source: VideoSource \| VideoSource[]` (реактивный — смена вызывает `load()`), `options: Omit<PlayerOptions, 'source'>` |
|
|
710
|
+
| `lazy` | **poster-first, player-on-intent**: компонент рендерит декларативную SSR-заглушку (постер уходит в первый HTML и становится LCP-кандидатом — `<ClientOnly>` не нужен), по триггеру качает чанк ядра + CSS и свапает через реактивное состояние. Клик по заглушке = загрузить и играть; фоновые триггеры не автоплеят |
|
|
711
|
+
| `loadOn` | триггер при `lazy`: `'placeholder'` (по умолчанию — любой интерактив в зоне заглушки), `'interaction'`, `'visible'`, `'immediate'` |
|
|
712
|
+
| `adConfig` | `AdsOptions` или **async-фабрика** `() => Promise<AdsOptions \| undefined>` (например, запрос VAST-тега) — выполняется параллельно загрузке чанка, плеер конструируется с готовым конфигом. Приоритетнее `options.adConfig` |
|
|
626
713
|
| События | Все события `PlayerEventMap` ретранслируются 1:1 (`@timeupdate`, `@aderror`, `@customaction`, …) |
|
|
627
714
|
| Слоты | `#pauseScreen="{ player, close }"` — контент экрана паузы (scoped: доступ к плееру и `close()`) |
|
|
628
715
|
| Expose | `player: ShallowRef<Player \| null>` — доступ к ядру: `playerRef.value.player.seek(0)` |
|
|
629
716
|
|
|
717
|
+
```vue
|
|
718
|
+
<!-- Nuxt 3: без ClientOnly, без ручного постера, без createLazyPlayer -->
|
|
719
|
+
<ITubePlayer lazy :source="video" :options="{ muted: true }" :ad-config="fetchVast" @ended="onEnded" />
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
CSS компонент подгружает сам (self-reference `itube-modern-player/style.css` через exports пакета); ручной импорт `style.css` в приложении продолжает работать и дедуплицируется бандлером — это фолбэк для экзотических сборок.
|
|
723
|
+
|
|
724
|
+
## Изоморфный шаблон заглушки: `itube-modern-player/placeholder`
|
|
725
|
+
|
|
726
|
+
`renderPlaceholder(options, flags?) → string` — чистая строка без DOM-зависимостей, единый источник правды по разметке «постер + кнопка play». Используется внутри `createLazyPlayer` и Vue-`lazy`; экспортируется для SSR любого фреймворка:
|
|
727
|
+
|
|
728
|
+
```ts
|
|
729
|
+
import { renderPlaceholder } from 'itube-modern-player/placeholder'
|
|
730
|
+
// options: { poster?, icon?, styling?, className?, playLabel? }
|
|
731
|
+
// flags: { inlineStyles?: true } — самодостаточные инлайн-стили (SSR без style.css)
|
|
732
|
+
const html = renderPlaceholder({ poster: { src: '/p.jpg', width: 1280, height: 720 } }, { inlineStyles: true })
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
Там же `posterSrc(poster)` — нормализация `PosterSource` в URL.
|
|
736
|
+
|
|
630
737
|
## Экспортируемые типы
|
|
631
738
|
|
|
632
739
|
`PlayerOptions, VideoSource, ChannelInfo, SubtitleTrack, Chapter, QualityLevel, PlaylistOptions, ControlsOptions, ActionsOptions, CustomAction, BuiltinActionId, RelatedOptions, RelatedItem, AdsOptions, AdRoll, ResolvedAd, ThumbnailCue, PlayerLabels, IconName, PlayerEvent, PlayerEventMap, Level, SourceController, NormalizedChapter`
|
|
@@ -739,13 +846,16 @@ lazy.destroy() // работает на любой стадии
|
|
|
739
846
|
</div>
|
|
740
847
|
```
|
|
741
848
|
|
|
742
|
-
2. **Инициализируйте плеер лениво** —
|
|
849
|
+
2. **Инициализируйте плеер лениво** — `createLazyPlayer` **подхватит серверную разметку** (adopt): если в mount-ноде уже есть содержимое, свой плейсхолдер не создаётся — вешаются только триггеры, а при загрузке бандла содержимое заменяется плеером. CSS плеера для постера не нужен (разметка ваша).
|
|
743
850
|
|
|
744
851
|
```ts
|
|
745
852
|
import { createLazyPlayer } from 'itube-modern-player/lazy'
|
|
746
|
-
|
|
853
|
+
// #player уже содержит SSR-постер → adopt, ничего не перерисовывается
|
|
854
|
+
createLazyPlayer('#player', { source: { src: '/stream.m3u8', poster: '/poster-1280.jpg' }, loadOn: 'placeholder' })
|
|
747
855
|
```
|
|
748
856
|
|
|
857
|
+
**Nuxt 3**: постер — обычная разметка компонента (SSR/SEO-friendly, никакого `<ClientOnly>` вокруг него), а `createLazyPlayer(mountRef.value, …)` — в `onMounted`. `import 'itube-modern-player/style.css'` можно грузить вместе с ленивым чанком, а не в главном бандле — постеру он не нужен.
|
|
858
|
+
|
|
749
859
|
Итог: LCP — это ваш серверный `<img>`-постер (приходит в первом HTML-ответе, приоритизируется браузером), а вес плеера и стрима подключается только когда пользователь реально собрался смотреть. Так делает и тестовый стенд проекта (постер `<picture><img>` в шаблоне страницы, плеер поверх по интерактиву).
|
|
750
860
|
|
|
751
861
|
> Если задать `source.poster`, плеер отрисует **свой** постер тоже (реальным `<img fetchpriority="high">`). Он сработает как LCP в чисто клиентских сценариях, но для лучшего LCP всё равно предпочтительнее серверный постер из пункта 1 — он есть в HTML сразу, без ожидания JS.
|
|
@@ -766,7 +876,8 @@ npm publish # prepublishOnly прогонит typecheck + build
|
|
|
766
876
|
| Импорт | Что это |
|
|
767
877
|
| --- | --- |
|
|
768
878
|
| `itube-modern-player` | ядро (`Player`, все типы, утилиты) |
|
|
769
|
-
| `itube-modern-player/vue` | Vue 3-компонент `ITubePlayer` |
|
|
879
|
+
| `itube-modern-player/vue` | Vue 3-компонент `ITubePlayer` (ядро — динамическим импортом; проп `lazy`) |
|
|
880
|
+
| `itube-modern-player/placeholder` | изоморфный `renderPlaceholder()` + `posterSrc()` (без DOM — можно на сервере) |
|
|
770
881
|
| `itube-modern-player/style.css` | стили (подключить один раз) |
|
|
771
882
|
|
|
772
883
|
---
|
|
@@ -775,6 +886,19 @@ npm publish # prepublishOnly прогонит typecheck + build
|
|
|
775
886
|
|
|
776
887
|
Версионирование по [SemVer](https://semver.org/lang/ru/): `major.minor.patch`.
|
|
777
888
|
|
|
889
|
+
### 0.8.0
|
|
890
|
+
|
|
891
|
+
- **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).
|
|
892
|
+
- **`adConfig` как async-фабрика** в lazy-слоях (`createLazyPlayer` и Vue-компонент): `adConfig: () => Promise<AdsOptions | undefined>` выполняется параллельно загрузке чанка — интегратору не нужно самому синхронизировать VAST-запрос с инициализацией.
|
|
893
|
+
- **Изоморфный шаблон заглушки** — новый entry `itube-modern-player/placeholder`: `renderPlaceholder(options, { inlineStyles? }) → string` без DOM-зависимостей (SSR любого фреймворка) + `posterSrc()`. Единый источник правды по разметке: `createLazyPlayer` и Vue-`lazy` рендерят её же — кастомная иконка `bigPlay`, `styling`, постер с полными `<img>`-атрибутами.
|
|
894
|
+
- **`poster` как объект.** `VideoSource.poster: string | { src, srcset?, sizes?, width?, height?, alt? }` — LCP-качество главного постера (responsive srcset, intrinsic-размеры против layout shift); тумбы используют `src`. Строка работает как раньше.
|
|
895
|
+
|
|
896
|
+
### 0.7.2
|
|
897
|
+
|
|
898
|
+
- **Скип битых роликов в «следующем видео».** Если при подготовке превью следующего ролика (ховер-карточка / end-оверлей) его постер не загрузился (4xx/5xx/сеть) — видео тоже не загрузится: источник помечается мёртвым и все вычисления «следующего» (превью, `next()`, автопереход, `hasNext`) пропускают его, подставляя следующий живой из списка. Карточка перерисовывается на лету; если живых впереди нет — прячется, next-кнопка дизейблится. Публичный метод `player.markSourceDead(source)` — пометить источник вручную. Панель плейлиста по-прежнему показывает все элементы.
|
|
899
|
+
- **lazy: плейсхолдер теперь пиксель-в-пиксель с плеером.** Кнопка play на заглушке использует кастомную иконку `options.icons.bigPlay` (раньше — всегда дефолтный треугольник, «менялась» после загрузки), плюс к заглушке применяются `styling.themeColor` / `borderRadius` / `playButtonStyle: 'inverted'` и `className`.
|
|
900
|
+
- **lazy: SSR-adopt — плейсхолдер можно рендерить на сервере.** Если mount-нода уже содержит разметку (SSR-постер любого вида, свои классы), `createLazyPlayer` **не создаёт** свой плейсхолдер: подхватывает существующий, вешает триггеры и заменяет содержимое плеером при загрузке. SSR-`<img>` остаётся LCP-кандидатом страницы — JS для первой отрисовки не нужен вовсе (лечит просадку Lighthouse/LCP при `ClientOnly`-обёртках в Nuxt/Next).
|
|
901
|
+
|
|
778
902
|
### 0.7.1
|
|
779
903
|
|
|
780
904
|
- **`options.autoplay` переименован в `playOnInit`** — «стартовать воспроизведение сразу после инициализации». Новое имя не путается с тоглером Autoplay в шестерёнке (тот — про автопереход, `playlist.autoAdvance`). Старый `autoplay` работает как deprecated-алиас (`playOnInit` приоритетнее); существующие интеграции не ломаются.
|