@dolby-optiview/ads-sdk-adapter-hlsjs 1.0.0-beta.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/dist/HlsJsAdapter.d.ts +251 -0
- package/dist/HlsJsAdapter.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import Hls from 'hls.js';
|
|
2
|
+
import type { PlayerAdapter, PlayerAdapterEvent, PlayerAdapterEventHandler, VideoQuality } from '@dolby-optiview/ads-sdk-core';
|
|
3
|
+
/**
|
|
4
|
+
* HLS.js adapter implementing the PlayerAdapter interface.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import Hls from 'hls.js';
|
|
9
|
+
* import { HlsJsAdapter } from '@dolby-optiview/ads-sdk-adapter-hlsjs';
|
|
10
|
+
*
|
|
11
|
+
* // Content player (pass existing video element)
|
|
12
|
+
* const video = document.getElementById('video') as HTMLVideoElement;
|
|
13
|
+
* const hls = new Hls();
|
|
14
|
+
* hls.loadSource('https://example.com/stream.m3u8');
|
|
15
|
+
* hls.attachMedia(video);
|
|
16
|
+
* const adapter = new HlsJsAdapter(hls, video);
|
|
17
|
+
*
|
|
18
|
+
* // Ad adapter factory (SDK creates a container div and passes it)
|
|
19
|
+
* createAdAdapter: (container) => {
|
|
20
|
+
* const adVideo = document.createElement('video');
|
|
21
|
+
* container.appendChild(adVideo);
|
|
22
|
+
* const adHls = new Hls();
|
|
23
|
+
* adHls.attachMedia(adVideo);
|
|
24
|
+
* return new HlsJsAdapter(adHls, adVideo);
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare class HlsJsAdapter implements PlayerAdapter {
|
|
29
|
+
private hls;
|
|
30
|
+
private video;
|
|
31
|
+
private eventListeners;
|
|
32
|
+
private boundHandlers;
|
|
33
|
+
private _programDateTime;
|
|
34
|
+
private deferredSeekTime;
|
|
35
|
+
/**
|
|
36
|
+
* The level selection in force before {@link pinLowestQuality}, or `null` when nothing is
|
|
37
|
+
* pinned. `-1` is hls.js's own value for automatic, so it is a meaningful snapshot rather
|
|
38
|
+
* than a sentinel — an application that had pinned a level itself gets that level back, not
|
|
39
|
+
* ABR.
|
|
40
|
+
*/
|
|
41
|
+
private levelBeforePin;
|
|
42
|
+
/**
|
|
43
|
+
* Cues already emitted as `parsed`, keyed by their pts, awaiting presentation.
|
|
44
|
+
*
|
|
45
|
+
* hls.js hands us a cue at segment PARSE — 20-30s before the playhead reaches it —
|
|
46
|
+
* so the cue and the instant it refers to are two separate events. The `presented`
|
|
47
|
+
* twin is timed off the metadata TextTrack (see attachId3CueEnterListener) but its
|
|
48
|
+
* CONTENT comes from here: the track's own cue payload is hls.js's DataCue shape,
|
|
49
|
+
* and re-deriving the Anvato fields from it would be a second, divergent parse of
|
|
50
|
+
* the same bytes. Keyed by pts because hls.js sets the track cue's `startTime` from
|
|
51
|
+
* `sample.pts` — the identical value this handler reads (id3-track-controller:
|
|
52
|
+
* `const startTime = samples[i].pts`), so the two line up exactly rather than
|
|
53
|
+
* approximately.
|
|
54
|
+
*
|
|
55
|
+
* An array per pts: one ID3 sample can carry several GEOB frames, and they all
|
|
56
|
+
* share a timestamp.
|
|
57
|
+
*/
|
|
58
|
+
private parsedCues;
|
|
59
|
+
/** pts already emitted as `presented`, so a re-entered cue fires once. */
|
|
60
|
+
private presentedPts;
|
|
61
|
+
/** The metadata track listener, kept for teardown. */
|
|
62
|
+
private id3TrackListener;
|
|
63
|
+
constructor(hls: Hls, videoElement: HTMLVideoElement);
|
|
64
|
+
/**
|
|
65
|
+
* Current playback time in seconds.
|
|
66
|
+
*/
|
|
67
|
+
get currentTime(): number;
|
|
68
|
+
/**
|
|
69
|
+
* Total duration of the content in seconds.
|
|
70
|
+
*/
|
|
71
|
+
get duration(): number;
|
|
72
|
+
/**
|
|
73
|
+
* Live per the PLAYLIST, not per the media duration.
|
|
74
|
+
*
|
|
75
|
+
* `video.duration` cannot answer this on hls.js: the engine sets it to Infinity for live
|
|
76
|
+
* only when `liveDurationInfinity` is configured, and that defaults to false — so a live
|
|
77
|
+
* stream normally reports a finite duration equal to the playlist edge. `latestLevelDetails`
|
|
78
|
+
* carries the real answer, taken straight from the absence of `#EXT-X-ENDLIST`.
|
|
79
|
+
*
|
|
80
|
+
* Cast and optional-chained because `latestLevelDetails` is newer than the hls.js this
|
|
81
|
+
* adapter must keep compiling against; an older build returns undefined and the core falls
|
|
82
|
+
* back to its duration proxy.
|
|
83
|
+
*/
|
|
84
|
+
get isLive(): boolean | null;
|
|
85
|
+
/**
|
|
86
|
+
* Whether the player is currently paused.
|
|
87
|
+
*/
|
|
88
|
+
get paused(): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Whether the player is muted.
|
|
91
|
+
*/
|
|
92
|
+
get muted(): boolean;
|
|
93
|
+
set muted(value: boolean);
|
|
94
|
+
/**
|
|
95
|
+
* Volume level in the range [0, 1].
|
|
96
|
+
*/
|
|
97
|
+
get volume(): number;
|
|
98
|
+
set volume(value: number);
|
|
99
|
+
/**
|
|
100
|
+
* Playback rate, `1` being normal speed.
|
|
101
|
+
*
|
|
102
|
+
* Proxies the element rather than remembering what was written. hls.js drives this property
|
|
103
|
+
* itself when `maxLiveSyncPlaybackRate` is configured, nudging the rate to hold live latency,
|
|
104
|
+
* and the SDK needs to see that so it can stop competing rather than fight it.
|
|
105
|
+
*/
|
|
106
|
+
get playbackRate(): number;
|
|
107
|
+
set playbackRate(value: number);
|
|
108
|
+
/**
|
|
109
|
+
* The underlying HTMLVideoElement. Exposed so the SDK can pass it to GAM.
|
|
110
|
+
*/
|
|
111
|
+
get videoElement(): HTMLVideoElement;
|
|
112
|
+
/**
|
|
113
|
+
* Whether the content and ad pipelines can buffer in parallel. True whenever
|
|
114
|
+
* HLS.js (MSE) is operational. On Web, `false` prevents continued content
|
|
115
|
+
* playout during a break because the two pipelines cannot remain buffered
|
|
116
|
+
* concurrently.
|
|
117
|
+
*/
|
|
118
|
+
get supportsParallelBuffering(): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Program Date Time from the HLS manifest (EXT-X-PROGRAM-DATE-TIME).
|
|
121
|
+
*/
|
|
122
|
+
get programDateTime(): Date | null;
|
|
123
|
+
/**
|
|
124
|
+
* Pause playback.
|
|
125
|
+
*/
|
|
126
|
+
pause(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Resume playback.
|
|
129
|
+
*/
|
|
130
|
+
play(): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Seek to a specific time in seconds.
|
|
133
|
+
*/
|
|
134
|
+
seek(time: number): void;
|
|
135
|
+
/**
|
|
136
|
+
* Load a media source without playing.
|
|
137
|
+
* Used for preloading ad content.
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* The manifest hls.js currently has, read off the engine (`hls.url`) so it is correct even
|
|
141
|
+
* when the application called `loadSource()` itself. The element's own `src` is a `blob:`
|
|
142
|
+
* MediaSource and cannot be reloaded.
|
|
143
|
+
*/
|
|
144
|
+
get sourceUrl(): string | null;
|
|
145
|
+
load(url: string): Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Detached prefetch for the single-decoder preload strategy.
|
|
148
|
+
*
|
|
149
|
+
* Spins up a throwaway HLS.js instance with `startFragPrefetch` that parses
|
|
150
|
+
* the manifest and fetches the first fragment WITHOUT attaching media — so no
|
|
151
|
+
* second MediaSource / decoder is engaged. The raw responses land in the
|
|
152
|
+
* browser HTTP cache; the instance is destroyed once the first fragment is
|
|
153
|
+
* loaded (or on error/timeout). Best-effort: never rejects.
|
|
154
|
+
*/
|
|
155
|
+
preload(url: string): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* The closest-to-live position hls.js will actually hold — `null` on VOD.
|
|
158
|
+
*
|
|
159
|
+
* hls.js is the one engine that pre-computes this, so there is no offset arithmetic
|
|
160
|
+
* here: its own `hls.liveSyncPosition` is already `liveEdge - targetLatency`, clamped
|
|
161
|
+
* into the seekable window by the latency controller itself.
|
|
162
|
+
*
|
|
163
|
+
* The explicit liveness gate is NOT redundant, despite what hls.js's own docstring
|
|
164
|
+
* suggests ("@returns null prior to loading live Playlist"). On VOD the getter returns
|
|
165
|
+
* a NUMBER, not null: `targetLatency` falls back to `liveSyncDurationCount * targetduration`
|
|
166
|
+
* (3 x segment duration by default) whenever `holdBack` is absent, which is exactly the
|
|
167
|
+
* VOD case, and `estimateLiveEdge()` needs only `levelDetails` — which VOD has. The
|
|
168
|
+
* result is a plausible-looking position roughly 3 segments from the end of the asset.
|
|
169
|
+
* Handing that to the resume clamp would drag every VOD resume backwards, breaking the
|
|
170
|
+
* one guarantee this feature is supposed to keep: VOD is untouched.
|
|
171
|
+
*
|
|
172
|
+
* `latestLevelDetails.live` is the authoritative flag and is public API.
|
|
173
|
+
*/
|
|
174
|
+
get maxLiveSeekPosition(): number | null;
|
|
175
|
+
/**
|
|
176
|
+
* Set the requested video quality, or restore the selection in force before the first set.
|
|
177
|
+
* Returns whether an override was applied or an outstanding override was restored.
|
|
178
|
+
*
|
|
179
|
+
* The lowest rendition is selected through `nextLevel`, which hls.js documents as taking
|
|
180
|
+
* effect without interrupting the fragment being decoded. `currentLevel` would flush the
|
|
181
|
+
* whole buffer and stall the content timeline. The argmin is computed because hls.js does
|
|
182
|
+
* not sort its level array by bitrate first.
|
|
183
|
+
*/
|
|
184
|
+
setVideoQuality(quality: VideoQuality | null): boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Subscribe to player events.
|
|
187
|
+
*/
|
|
188
|
+
on(event: PlayerAdapterEvent, handler: PlayerAdapterEventHandler): void;
|
|
189
|
+
/**
|
|
190
|
+
* Unsubscribe from player events.
|
|
191
|
+
*/
|
|
192
|
+
off(event: PlayerAdapterEvent, handler: PlayerAdapterEventHandler): void;
|
|
193
|
+
/**
|
|
194
|
+
* Clean up resources.
|
|
195
|
+
*/
|
|
196
|
+
destroy(): void;
|
|
197
|
+
/**
|
|
198
|
+
* Set up HLS.js event listeners for program date time.
|
|
199
|
+
*/
|
|
200
|
+
private setupHlsListeners;
|
|
201
|
+
/**
|
|
202
|
+
* Re-emit a held cue as `presented` when the playhead reaches it.
|
|
203
|
+
*
|
|
204
|
+
* The timing comes from the metadata TextTrack hls.js already maintains
|
|
205
|
+
* (`addTextTrack('metadata', 'id3')`, enabled by the default `enableID3MetadataCues`),
|
|
206
|
+
* via the standard `cuechange` event — the HTML "time marches on" algorithm, which is
|
|
207
|
+
* the same machinery THEOplayer's per-cue `enter` runs on. One listener on the track
|
|
208
|
+
* rather than one per cue: a metadata track accumulates a cue every few seconds for the
|
|
209
|
+
* life of a live stream, and per-cue listeners on that would be an unbounded leak.
|
|
210
|
+
*
|
|
211
|
+
* Note the precision of this path is the browser's, not ours. ADS-285 measures it; if
|
|
212
|
+
* it turns out to be the same ~250ms class as the scheduler's poll, this buys nothing
|
|
213
|
+
* on web and the native adapters carry the feature alone. It cannot make things worse:
|
|
214
|
+
* a cue enters when the playhead is genuinely at it, so this can only ever fire a break
|
|
215
|
+
* ON TIME or not at all, never early.
|
|
216
|
+
*/
|
|
217
|
+
private attachId3CueEnterListener;
|
|
218
|
+
/**
|
|
219
|
+
* Drop held cues the playhead has passed.
|
|
220
|
+
*
|
|
221
|
+
* Without this a live stream accumulates every ID3 cue it has ever seen — hours of
|
|
222
|
+
* them. The window is generous because a cue may be parsed well before it presents,
|
|
223
|
+
* and dropping one early costs a `presented` emission; it is bounded by wall time,
|
|
224
|
+
* not by count, so it stays correct if the parse lead grows.
|
|
225
|
+
*/
|
|
226
|
+
private pruneParsedCues;
|
|
227
|
+
/**
|
|
228
|
+
* Set up video element event listeners.
|
|
229
|
+
*/
|
|
230
|
+
private setupVideoListeners;
|
|
231
|
+
/**
|
|
232
|
+
* Update program date time based on current playback position.
|
|
233
|
+
*/
|
|
234
|
+
private updateProgramDateTime;
|
|
235
|
+
/**
|
|
236
|
+
* Set the reported PDT from a reference fragment, anchored to the playhead:
|
|
237
|
+
* `pdt = refFragPdt + (currentTime - refFragStart)`.
|
|
238
|
+
*
|
|
239
|
+
* All PDT update paths (timeupdate interpolation, FRAG_CHANGED,
|
|
240
|
+
* LEVEL_LOADED) MUST share this single affine mapping. On a live stream the
|
|
241
|
+
* playlist window slides (`firstFrag.start` grows), so any path that drops
|
|
242
|
+
* the `- refFragStart` term drifts ahead of the true PDT and then snaps
|
|
243
|
+
* back on the next fragment/playlist update — an oscillation that re-fired
|
|
244
|
+
* completed wallclock breaks and broke the countdown badge (PLAYG-280).
|
|
245
|
+
*/
|
|
246
|
+
private anchorProgramDateTime;
|
|
247
|
+
/**
|
|
248
|
+
* Emit an event to all registered handlers.
|
|
249
|
+
*/
|
|
250
|
+
private emit;
|
|
251
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"hls.js";import{decodeUtf8 as t,parseId3GeobFrames as s}from"@dolby-optiview/ads-sdk-core/id3";export class HlsJsAdapter{constructor(e,t){this.eventListeners=new Map,this.boundHandlers=new Map,this._programDateTime=null,this.deferredSeekTime=null,this.levelBeforePin=null,this.parsedCues=new Map,this.presentedPts=new Set,this.id3TrackListener=null,this.hls=e,this.video=t,this.setupHlsListeners(),this.setupVideoListeners()}get currentTime(){return this.video.currentTime}get duration(){return this.video.duration}get isLive(){const e=this.hls?.latestLevelDetails;return"boolean"==typeof e?.live?e.live:null}get paused(){return this.video.paused}get muted(){return this.video.muted}set muted(e){this.video.muted=e}get volume(){return this.video.volume}set volume(e){this.video.volume=e}get playbackRate(){return this.video.playbackRate}set playbackRate(e){this.video.playbackRate=e}get videoElement(){return this.video}get supportsParallelBuffering(){return e.isSupported()}get programDateTime(){return this._programDateTime}pause(){this.video.pause()}async play(){await this.video.play()}seek(e){this.video.seeking?this.deferredSeekTime=e:this.video.currentTime=e}get sourceUrl(){return this.hls.url??null}async load(t){return new Promise((s,i)=>{const r=()=>{this.hls.off(e.Events.MANIFEST_PARSED,r),this.hls.off(e.Events.ERROR,n),s()},n=(t,s)=>{s.fatal&&(this.hls.off(e.Events.MANIFEST_PARSED,r),this.hls.off(e.Events.ERROR,n),i(new Error(`HLS load error: ${s.details}`)))};this.hls.on(e.Events.MANIFEST_PARSED,r),this.hls.on(e.Events.ERROR,n),this.hls.loadSource(t),this.hls.attachMedia(this.video)})}async preload(t){if(e.isSupported())return new Promise(s=>{const i=new e({startFragPrefetch:!0,autoStartLoad:!0});let r=!1;const n=()=>{if(!r){r=!0,clearTimeout(d),i.off(e.Events.FRAG_LOADED,a),i.off(e.Events.ERROR,o);try{i.destroy()}catch{}s()}},a=()=>n(),o=(e,t)=>{t.fatal&&n()},d=setTimeout(n,8e3);i.on(e.Events.FRAG_LOADED,a),i.on(e.Events.ERROR,o),i.loadSource(t)})}get maxLiveSeekPosition(){const e=this.hls.latestLevelDetails;if(!e?.live)return null;const t=this.hls.liveSyncPosition;return"number"==typeof t&&Number.isFinite(t)?t:null}setVideoQuality(e){if(null===e){if(null===this.levelBeforePin)return!1;const e=this.levelBeforePin;try{return this.hls.nextLevel=e,this.hls.nextLevel!==e?!1:(this.levelBeforePin=null,!0)}catch{return!1}}const t=this.hls.levels;if(!t||t.length<2)return!1;let s=0;for(let e=1;e<t.length;e++)t[e].bitrate<t[s].bitrate&&(s=e);const i=t[s].bitrate;if("number"!=typeof i||!Number.isFinite(i)||i<=0)return!1;const r=null===this.levelBeforePin;let n;r&&(this.levelBeforePin=this.hls.manualLevel);try{this.hls.nextLevel=s,n=this.hls.nextLevel===s}catch{n=!1}return!!n||(r&&(this.levelBeforePin=null),!1)}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t)}off(e,t){const s=this.eventListeners.get(e);s&&s.delete(t)}destroy(){for(const[e,t]of this.boundHandlers)this.video.removeEventListener(e,t);this.boundHandlers.clear(),this.eventListeners.clear(),this.deferredSeekTime=null,this.id3TrackListener&&(this.id3TrackListener.track.removeEventListener("cuechange",this.id3TrackListener.handler),this.id3TrackListener=null),this.parsedCues.clear(),this.presentedPts.clear()}setupHlsListeners(){this.hls.on(e.Events.FRAG_CHANGED,(e,t)=>{t.frag.programDateTime&&this.anchorProgramDateTime(t.frag.programDateTime,t.frag.start)}),this.hls.on(e.Events.LEVEL_LOADED,(e,t)=>{if(t.details.hasProgramDateTime&&t.details.fragments.length>0){const e=t.details.fragments[0];e.programDateTime&&this.anchorProgramDateTime(e.programDateTime,e.start)}}),this.hls.on(e.Events.FRAG_PARSING_METADATA,(e,i)=>{for(const e of i.samples??[]){const i=s(e.data);if(0===i.length){const t={type:"id3",startTime:e.pts,raw:e.data,phase:"parsed"};this.emit("timedmetadata",t);continue}const r=[];for(const s of i){const i={type:"id3",startTime:e.pts,description:s.description,mimeType:s.mimeType,messageData:t(s.data),raw:e.data,phase:"parsed"};r.push(i),this.emit("timedmetadata",i)}this.parsedCues.set(e.pts,r),this.pruneParsedCues()}this.attachId3CueEnterListener()})}attachId3CueEnterListener(){if(this.id3TrackListener)return;const e=this.video.textTracks;if(!e)return;let t;for(let s=0;s<e.length;s++){const i=e[s];if("metadata"===i.kind&&"id3"===i.label){t=i;break}}if(!t||"function"!=typeof t.addEventListener)return;"disabled"===t.mode&&(t.mode="hidden");const s=()=>{const e=t?.activeCues;if(e)for(let t=0;t<e.length;t++){const s=e[t].startTime;if(this.presentedPts.has(s))continue;const i=this.parsedCues.get(s);if(i){this.presentedPts.add(s);for(const e of i)this.emit("timedmetadata",{...e,phase:"presented"})}}};t.addEventListener("cuechange",s),this.id3TrackListener={track:t,handler:s}}pruneParsedCues(){const e=this.video.currentTime-120;for(const t of this.parsedCues.keys())t<e&&(this.parsedCues.delete(t),this.presentedPts.delete(t))}setupVideoListeners(){const e=()=>{this.updateProgramDateTime(),this.emit("timeupdate")},t=()=>{this.emit("ended")},s=()=>{this.emit("error",this.video.error)},i=()=>{if(this.emit("seeked"),null!==this.deferredSeekTime){const e=this.deferredSeekTime;this.deferredSeekTime=null,this.seek(e)}},r=()=>{this.emit("volumechange")},n=()=>{this.emit("waiting")},a=()=>{this.emit("playing")},o=()=>{this.emit("pause")};this.video.addEventListener("timeupdate",e),this.video.addEventListener("ended",t),this.video.addEventListener("error",s),this.video.addEventListener("seeked",i),this.video.addEventListener("volumechange",r),this.video.addEventListener("waiting",n),this.video.addEventListener("playing",a),this.video.addEventListener("pause",o),this.boundHandlers.set("timeupdate",e),this.boundHandlers.set("ended",t),this.boundHandlers.set("error",s),this.boundHandlers.set("seeked",i),this.boundHandlers.set("volumechange",r),this.boundHandlers.set("waiting",n),this.boundHandlers.set("playing",a),this.boundHandlers.set("pause",o)}updateProgramDateTime(){const e=this.hls.levels,t=this.hls.currentLevel;if(t>=0&&e[t]?.details){const s=e[t].details;if(s?.hasProgramDateTime&&s.fragments.length>0){const e=s.fragments[0];e.programDateTime&&this.anchorProgramDateTime(e.programDateTime,e.start)}}}anchorProgramDateTime(e,t){this._programDateTime=new Date(e+1e3*(this.video.currentTime-t))}emit(e,t){const s=this.eventListeners.get(e);if(s)for(const e of s)try{e(t)}catch(e){console.error("[HlsJsAdapter] Event handler error:",e)}}}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{HlsJsAdapter}from"./HlsJsAdapter";export{parseId3GeobFrames,parseId3GeobBody,decodeUtf8}from"@dolby-optiview/ads-sdk-core/id3";
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dolby-optiview/ads-sdk-adapter-hlsjs",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"description": "HLS.js adapter for OptiView Ads SDK",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -b tsconfig.build.json",
|
|
18
|
+
"test": "jest",
|
|
19
|
+
"test:watch": "jest --watch",
|
|
20
|
+
"clean": "rm -rf dist"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"dolby",
|
|
24
|
+
"ads",
|
|
25
|
+
"hlsjs",
|
|
26
|
+
"hls.js",
|
|
27
|
+
"video",
|
|
28
|
+
"adapter"
|
|
29
|
+
],
|
|
30
|
+
"author": "Dolby",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://ads-sdk.xnappet.live",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"sideEffects": false,
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@dolby-optiview/ads-sdk-core": "1.0.0-beta.1"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"hls.js": "^1.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|