stormcloud-video-player 0.1.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/LICENSE +21 -0
- package/README.md +202 -0
- package/lib/index.cjs +1748 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +157 -0
- package/lib/index.d.ts +157 -0
- package/lib/index.js +1706 -0
- package/lib/index.js.map +1 -0
- package/package.json +38 -0
- package/src/index.ts +20 -0
- package/src/player/StormcloudVideoPlayer.ts +1124 -0
- package/src/sdk/ima.ts +369 -0
- package/src/types.ts +124 -0
- package/src/ui/StormcloudVideoPlayer.tsx +258 -0
- package/src/utils/tracking.ts +251 -0
- package/tsconfig.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [2025] [Showfer Media LLC]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
## stormcloud-video-player
|
|
2
|
+
|
|
3
|
+
Ad-first HLS player module for the web with tight ad break alignment. It uses the HTML5 video element with hls.js, parses SCTE-35 signals from HLS tags and ID3 timed metadata, and integrates Google IMA for VAST/VPAID ad playback.
|
|
4
|
+
|
|
5
|
+
### Key goals
|
|
6
|
+
|
|
7
|
+
- Precise ad start alignment on SCTE-35 CUE-OUT
|
|
8
|
+
- Mid-ad join synchronization (play remaining portion if you tune in late)
|
|
9
|
+
- Flexible fallback for feeds without SCTE-35 via external schedules
|
|
10
|
+
|
|
11
|
+
### Features (current)
|
|
12
|
+
|
|
13
|
+
- HLS playback via hls.js with native HLS fallback
|
|
14
|
+
- SCTE-35 parsing from HLS tags: `#EXT-X-CUE-OUT`, `#EXT-X-CUE-OUT-CONT`, `#EXT-X-CUE-IN`, `#EXT-X-DATERANGE`
|
|
15
|
+
- SCTE-35/markers parsing from ID3 text payloads
|
|
16
|
+
- IMA SDK integration with pause/resume/error handling
|
|
17
|
+
- Remaining-duration countdown and auto-stop when CUE-IN is absent
|
|
18
|
+
- External schedule ingestion helpers and default VAST tag support
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install stormcloud-video-player hls.js
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This library targets modern browsers (ES2020) and expects a DOM environment.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
### React Usage
|
|
35
|
+
|
|
36
|
+
```jsx
|
|
37
|
+
import React from "react";
|
|
38
|
+
import { StormcloudVideoPlayerComponent } from "stormcloud-video-player";
|
|
39
|
+
|
|
40
|
+
function MyApp() {
|
|
41
|
+
return (
|
|
42
|
+
<StormcloudVideoPlayerComponent
|
|
43
|
+
src="https://example.com/live/playlist.m3u8"
|
|
44
|
+
autoplay={true}
|
|
45
|
+
muted={true}
|
|
46
|
+
allowNativeHls={false}
|
|
47
|
+
controls={true}
|
|
48
|
+
style={{ width: "640px", height: "360px" }}
|
|
49
|
+
adSchedule={{
|
|
50
|
+
lateJoinPolicy: "play_remaining",
|
|
51
|
+
breaks: [
|
|
52
|
+
{
|
|
53
|
+
startTimeMs: 60_000,
|
|
54
|
+
durationMs: 120_000,
|
|
55
|
+
vastTagUrl: "https://...",
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
}}
|
|
59
|
+
onReady={(player) => {
|
|
60
|
+
console.log("Player ready:", player);
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Vanilla JavaScript Usage
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<div id="player" style="position:relative;width:640px;height:360px;">
|
|
71
|
+
<video
|
|
72
|
+
id="video"
|
|
73
|
+
playsinline
|
|
74
|
+
muted
|
|
75
|
+
controls
|
|
76
|
+
style="width:100%;height:100%;"
|
|
77
|
+
></video>
|
|
78
|
+
</div>
|
|
79
|
+
<script type="module">
|
|
80
|
+
import { StormcloudVideoPlayer } from "stormcloud-video-player";
|
|
81
|
+
|
|
82
|
+
const video = document.getElementById("video");
|
|
83
|
+
|
|
84
|
+
const player = new StormcloudVideoPlayer({
|
|
85
|
+
videoElement: video,
|
|
86
|
+
src: "https://example.com/live/playlist.m3u8",
|
|
87
|
+
autoplay: true,
|
|
88
|
+
muted: true,
|
|
89
|
+
allowNativeHls: false,
|
|
90
|
+
// Optional external ad schedule
|
|
91
|
+
adSchedule: {
|
|
92
|
+
lateJoinPolicy: "play_remaining",
|
|
93
|
+
breaks: [
|
|
94
|
+
{ startTimeMs: 60_000, durationMs: 120_000, vastTagUrl: "https://..." },
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
player.load();
|
|
100
|
+
</script>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## API
|
|
106
|
+
|
|
107
|
+
### Class: `StormcloudVideoPlayer`
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
new StormcloudVideoPlayer(config: StormcloudVideoPlayerConfig)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- `load(): Promise<void>`: Attach and start playback for the configured source
|
|
114
|
+
- `destroy(): void`: Cleanup player and ad resources
|
|
115
|
+
- `setAdSchedule(schedule?: AdSchedule): void`: Provide/replace ad schedule
|
|
116
|
+
- `loadAdScheduleFromUrl(url: string): Promise<void>`: Fetch and set JSON schedule
|
|
117
|
+
- `loadDefaultVastFromAiry(url: string, params?: Record<string, string>): Promise<void>`: Fetch ad tag from Airy-like API and set `defaultVastTagUrl`
|
|
118
|
+
|
|
119
|
+
### Types
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
type LateJoinPolicy = "play_remaining" | "skip_to_content";
|
|
123
|
+
|
|
124
|
+
interface AdBreak {
|
|
125
|
+
id?: string;
|
|
126
|
+
startTimeMs: number;
|
|
127
|
+
durationMs?: number;
|
|
128
|
+
vastTagUrl?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface AdSchedule {
|
|
132
|
+
breaks: AdBreak[];
|
|
133
|
+
lateJoinPolicy?: LateJoinPolicy;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
interface StormcloudVideoPlayerConfig {
|
|
137
|
+
videoElement: HTMLVideoElement;
|
|
138
|
+
src: string;
|
|
139
|
+
autoplay?: boolean;
|
|
140
|
+
muted?: boolean;
|
|
141
|
+
allowNativeHls?: boolean;
|
|
142
|
+
adSchedule?: AdSchedule;
|
|
143
|
+
defaultVastTagUrl?: string;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## How ad alignment works
|
|
150
|
+
|
|
151
|
+
- On `CUE-OUT`/`DATERANGE` start: request IMA ads and start playback immediately; if duration is known, a countdown is scheduled to auto-stop if `CUE-IN` is missing.
|
|
152
|
+
- On `CUE-OUT-CONT` (progress): if not already playing, and policy is `play_remaining`, request ads and start; countdown is updated based on elapsed/duration.
|
|
153
|
+
- On `CUE-IN`: stop ads and resume content immediately.
|
|
154
|
+
- Without markers: provide `adSchedule` or a `defaultVastTagUrl` to still serve ads based on wall-clock content time.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## External schedules and Airy API
|
|
159
|
+
|
|
160
|
+
You can set an `AdSchedule` directly via `setAdSchedule` or load a JSON schedule from a URL with `loadAdScheduleFromUrl`. For Airy-like APIs (`https://api.airy.tv/api/v2.1.7/ads/web`), use `loadDefaultVastFromAiry(airyApiUrl, params)` to set the default VAST tag returned by the service.
|
|
161
|
+
|
|
162
|
+
Note: Real-world APIs may require authentication or custom mapping from their JSON to `AdSchedule`. Add an adapter as needed before calling `setAdSchedule`.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npm run build # build ESM + CJS with types
|
|
170
|
+
npm run dev # watch mode
|
|
171
|
+
npm run clean # remove dist
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Limitations
|
|
177
|
+
|
|
178
|
+
- This module targets browsers (DOM required). SSR/Node-only not supported.
|
|
179
|
+
- SCTE-35 parsing relies on HLS tags and ID3 text; binary splice parsing is not implemented yet.
|
|
180
|
+
- Multi-ad pod stitching and competitive separation are left to the ad server/IMA.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Roadmap / Remaining tasks
|
|
185
|
+
|
|
186
|
+
- Robust SCTE-35 binary parsing (splice_info_section) and richer marker mapping
|
|
187
|
+
- Stronger late-join logic: handle partial pods, skip-to-content policy
|
|
188
|
+
- Low-Latency HLS (LL-HLS) tuning to reduce ad-start drift further
|
|
189
|
+
- Better drift correction between PTS and wall-clock; tolerance config
|
|
190
|
+
- Ad pod management: sequencing multiple ads per break if provided
|
|
191
|
+
- UI overlays: optional countdown/"Ad" badge and simple debug HUD
|
|
192
|
+
- Retry/backoff and error telemetry for both HLS and IMA flows
|
|
193
|
+
- Analytics events (start/firstQuartile/midpoint/thirdQuartile/complete)
|
|
194
|
+
- Example app and demo page with real streams and test tags
|
|
195
|
+
- Type definitions for IMA surface (remove `any`) and stronger public types
|
|
196
|
+
- CI, unit tests (ID3/daterange parsers), and integration tests
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|