@streamlayer/feature-advertisement 1.0.0 → 1.1.1-rc.10.p6eb7304a
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/classic/package.json +5 -0
- package/lib/advertisement.d.ts +6 -6
- package/lib/advertisement.js +32 -17
- package/lib/cjs/advertisement.d.ts +64 -0
- package/lib/cjs/advertisement.js +322 -0
- package/lib/cjs/index.d.ts +21 -0
- package/lib/cjs/index.js +41 -0
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/units/ad-macros.d.ts +7 -0
- package/lib/cjs/units/ad-macros.js +91 -0
- package/lib/cjs/units/external-ad.d.ts +127 -0
- package/lib/cjs/units/external-ad.js +421 -0
- package/lib/cjs/units/external-pause-ad.d.ts +50 -0
- package/lib/cjs/units/external-pause-ad.js +259 -0
- package/lib/cjs/units/gam.d.ts +26 -0
- package/lib/cjs/units/gam.js +152 -0
- package/lib/cjs/units/parse-vast-extensions.d.ts +26 -0
- package/lib/cjs/units/parse-vast-extensions.js +56 -0
- package/lib/cjs/units/pause-ad-card.d.ts +49 -0
- package/lib/cjs/units/pause-ad-card.js +136 -0
- package/lib/cjs/units/pause-ad.d.ts +16 -0
- package/lib/cjs/units/pause-ad.js +52 -0
- package/lib/cjs/units/sponsor-logo.d.ts +78 -0
- package/lib/cjs/units/sponsor-logo.js +194 -0
- package/lib/cjs/units/ssai-ad-break.d.ts +21 -0
- package/lib/cjs/units/ssai-ad-break.js +11 -0
- package/lib/cjs/units/ssai-creative.d.ts +89 -0
- package/lib/cjs/units/ssai-creative.js +136 -0
- package/lib/cjs/units/ssai-detector.d.ts +180 -0
- package/lib/cjs/units/ssai-detector.js +1172 -0
- package/lib/cjs/units/ssai-log.d.ts +16 -0
- package/lib/cjs/units/ssai-log.js +23 -0
- package/lib/cjs/units/ssai-preload.d.ts +20 -0
- package/lib/cjs/units/ssai-preload.js +62 -0
- package/lib/cjs/units/ssai-providers.d.ts +117 -0
- package/lib/cjs/units/ssai-providers.js +300 -0
- package/lib/cjs/units/vast-tracker.d.ts +45 -0
- package/lib/cjs/units/vast-tracker.js +64 -0
- package/lib/classic/_virtual/_rollupPluginBabelHelpers.js +71 -0
- package/lib/classic/advertisement.js +348 -0
- package/lib/classic/cjs/_virtual/_rollupPluginBabelHelpers.js +77 -0
- package/lib/classic/cjs/advertisement.js +350 -0
- package/lib/classic/cjs/index.js +49 -0
- package/lib/classic/cjs/package.json +1 -0
- package/lib/classic/cjs/units/ad-macros.js +115 -0
- package/lib/classic/cjs/units/external-ad.js +500 -0
- package/lib/classic/cjs/units/external-pause-ad.js +278 -0
- package/lib/classic/cjs/units/gam.js +173 -0
- package/lib/classic/cjs/units/parse-vast-extensions.js +60 -0
- package/lib/classic/cjs/units/pause-ad-card.js +134 -0
- package/lib/classic/cjs/units/pause-ad.js +72 -0
- package/lib/classic/cjs/units/sponsor-logo.js +201 -0
- package/lib/classic/cjs/units/ssai-ad-break.js +11 -0
- package/lib/classic/cjs/units/ssai-creative.js +142 -0
- package/lib/classic/cjs/units/ssai-detector.js +1195 -0
- package/lib/classic/cjs/units/ssai-log.js +23 -0
- package/lib/classic/cjs/units/ssai-preload.js +72 -0
- package/lib/classic/cjs/units/ssai-providers.js +361 -0
- package/lib/classic/cjs/units/vast-tracker.js +81 -0
- package/lib/classic/index.js +29 -0
- package/lib/classic/units/ad-macros.js +113 -0
- package/lib/classic/units/external-ad.js +491 -0
- package/lib/classic/units/external-pause-ad.js +275 -0
- package/lib/classic/units/gam.js +171 -0
- package/lib/classic/units/parse-vast-extensions.js +58 -0
- package/lib/classic/units/pause-ad-card.js +131 -0
- package/lib/classic/units/pause-ad.js +70 -0
- package/lib/classic/units/sponsor-logo.js +197 -0
- package/lib/classic/units/ssai-ad-break.js +9 -0
- package/lib/classic/units/ssai-creative.js +140 -0
- package/lib/classic/units/ssai-detector.js +1192 -0
- package/lib/classic/units/ssai-log.js +21 -0
- package/lib/classic/units/ssai-preload.js +68 -0
- package/lib/classic/units/ssai-providers.js +351 -0
- package/lib/classic/units/vast-tracker.js +77 -0
- package/lib/index.d.ts +15 -12
- package/lib/index.js +14 -7
- package/lib/units/ad-macros.d.ts +7 -0
- package/lib/units/ad-macros.js +87 -0
- package/lib/units/external-ad.d.ts +71 -2
- package/lib/units/external-ad.js +94 -66
- package/lib/units/external-pause-ad.js +7 -1
- package/lib/units/gam.d.ts +11 -1
- package/lib/units/gam.js +65 -17
- package/lib/units/parse-vast-extensions.d.ts +10 -1
- package/lib/units/parse-vast-extensions.js +5 -1
- package/lib/units/pause-ad-card.d.ts +49 -0
- package/lib/units/pause-ad-card.js +130 -0
- package/lib/units/pause-ad.d.ts +1 -18
- package/lib/units/pause-ad.js +2 -49
- package/lib/units/sponsor-logo.d.ts +78 -0
- package/lib/units/sponsor-logo.js +188 -0
- package/lib/units/ssai-ad-break.d.ts +1 -1
- package/lib/units/ssai-creative.d.ts +1 -1
- package/lib/units/ssai-creative.js +1 -1
- package/lib/units/ssai-detector.d.ts +1 -1
- package/lib/units/ssai-detector.js +27 -11
- package/lib/units/ssai-log.d.ts +16 -0
- package/lib/units/ssai-log.js +18 -0
- package/lib/units/ssai-preload.d.ts +1 -1
- package/lib/units/ssai-providers.d.ts +1 -1
- package/lib/units/ssai-providers.js +48 -11
- package/lib/units/vast-tracker.d.ts +1 -1
- package/lib/vast-client.d.ts +9 -0
- package/package.json +43 -10
package/lib/advertisement.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
-
import { externalAd } from './units/external-ad';
|
|
3
|
-
import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad';
|
|
4
|
-
import { type PauseAdListOptions, type PauseAdStore } from './units/pause-ad';
|
|
5
|
-
import { type SsaiProviderOption } from './units/ssai-providers';
|
|
6
|
-
import { type HlsLike, type SsaiDebugSnapshot } from './units/ssai-detector';
|
|
7
|
-
import { type SsaiAdBreakStore } from './units/ssai-ad-break';
|
|
2
|
+
import { externalAd } from './units/external-ad.js';
|
|
3
|
+
import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad.js';
|
|
4
|
+
import { type PauseAdListOptions, type PauseAdStore } from './units/pause-ad.js';
|
|
5
|
+
import { type SsaiProviderOption } from './units/ssai-providers.js';
|
|
6
|
+
import { type HlsLike, type SsaiDebugSnapshot } from './units/ssai-detector.js';
|
|
7
|
+
import { type SsaiAdBreakStore } from './units/ssai-ad-break.js';
|
|
8
8
|
export type ExternalAdStore = ReturnType<ReturnType<typeof externalAd>>;
|
|
9
9
|
export type ExternalPauseAdStore = ReturnType<ReturnType<typeof externalPauseAd>>;
|
|
10
10
|
export type SsaiMonitorOptions = {
|
package/lib/advertisement.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { AbstractFeatureUnit, createSingleStore, eventBus } from '@streamlayer/sdk-web-interfaces';
|
|
2
2
|
import { createLogger } from '@streamlayer/sdk-web-logger';
|
|
3
|
-
import { externalAd } from './units/external-ad';
|
|
4
|
-
import { externalPauseAd } from './units/external-pause-ad';
|
|
5
|
-
import { pauseAdListStore } from './units/pause-ad';
|
|
6
|
-
import { SsaiCreativeCache } from './units/ssai-creative';
|
|
7
|
-
import { resolveProvider } from './units/ssai-providers';
|
|
8
|
-
import { createSsaiDetector, } from './units/ssai-detector';
|
|
9
|
-
import { createSsaiAdBreakStore } from './units/ssai-ad-break';
|
|
10
|
-
import {
|
|
3
|
+
import { externalAd } from './units/external-ad.js';
|
|
4
|
+
import { externalPauseAd } from './units/external-pause-ad.js';
|
|
5
|
+
import { pauseAdListStore } from './units/pause-ad.js';
|
|
6
|
+
import { SsaiCreativeCache } from './units/ssai-creative.js';
|
|
7
|
+
import { resolveProvider } from './units/ssai-providers.js';
|
|
8
|
+
import { createSsaiDetector, } from './units/ssai-detector.js';
|
|
9
|
+
import { createSsaiAdBreakStore } from './units/ssai-ad-break.js';
|
|
10
|
+
import { ssaiq } from './units/ssai-log.js';
|
|
11
|
+
import { preloadCreativeAssets } from './units/ssai-preload.js';
|
|
11
12
|
export class Advertisement extends AbstractFeatureUnit {
|
|
12
13
|
getExternalAd;
|
|
13
14
|
getExternalPauseAd;
|
|
@@ -121,10 +122,25 @@ export class Advertisement extends AbstractFeatureUnit {
|
|
|
121
122
|
const provider = resolveProvider(options.provider);
|
|
122
123
|
const cache = new SsaiCreativeCache(provider);
|
|
123
124
|
this.ssaiCache = cache;
|
|
125
|
+
/**
|
|
126
|
+
* Every beacon the SDK fires gets a line, with the URL it was fired at.
|
|
127
|
+
* The narrative log is read as a live trace of one ad, and "we called the
|
|
128
|
+
* ad server's own tracking URL" is as much of the story as the request
|
|
129
|
+
* that fetched the creative in the first place.
|
|
130
|
+
*/
|
|
131
|
+
const logBeacon = (creativeId, event) => {
|
|
132
|
+
const url = cache.peek(creativeId)?.trackingEvents?.find((entry) => entry.event === event)?.url;
|
|
133
|
+
ssaiq.info({ creativeId, event, url }, `[beacon] ${event} fired — ${creativeId}`);
|
|
134
|
+
};
|
|
124
135
|
this.fireSsaiImpression = (creativeId) => {
|
|
136
|
+
// The UI only calls this once the bar is actually rendered, so this is
|
|
137
|
+
// the moment the viewer saw the ad — not the moment it resolved.
|
|
138
|
+
ssaiq.info({ creativeId }, `[ad] ad on screen — ${cache.peek(creativeId)?.advertiser || creativeId}`);
|
|
139
|
+
logBeacon(creativeId, 'impression');
|
|
125
140
|
cache.fireImpressionById(creativeId);
|
|
126
141
|
};
|
|
127
142
|
this.fireSsaiTracking = (creativeId, event) => {
|
|
143
|
+
logBeacon(creativeId, event);
|
|
128
144
|
cache.fireTrackingById(creativeId, event);
|
|
129
145
|
};
|
|
130
146
|
this.fireSsaiCompanionClick = (creativeId) => {
|
|
@@ -144,12 +160,14 @@ export class Advertisement extends AbstractFeatureUnit {
|
|
|
144
160
|
const prefetchCreative = (creativeId) => {
|
|
145
161
|
cache
|
|
146
162
|
.resolve(creativeId)
|
|
147
|
-
.then((creative) => preloadCreativeAssets(creative))
|
|
148
|
-
.then(() => {
|
|
163
|
+
.then((creative) => preloadCreativeAssets(creative).then(() => creative))
|
|
164
|
+
.then((creative) => {
|
|
149
165
|
preloadedIds.add(creativeId);
|
|
166
|
+
ssaiq.info({ creativeId }, `[ad] creative ready — ${creative.advertiser} (artwork preloaded)`);
|
|
150
167
|
})
|
|
151
|
-
.catch((
|
|
152
|
-
|
|
168
|
+
.catch(() => {
|
|
169
|
+
// Already on `ssaiq` from the provider; the break may still be
|
|
170
|
+
// minutes away, and `loadCreative` retries when it actually starts.
|
|
153
171
|
});
|
|
154
172
|
};
|
|
155
173
|
const publishReady = (range, creative) => {
|
|
@@ -214,11 +232,9 @@ export class Advertisement extends AbstractFeatureUnit {
|
|
|
214
232
|
videoRef: options.videoRef,
|
|
215
233
|
manifestUrl: options.manifestUrl,
|
|
216
234
|
onAdDiscovered: (creativeId) => {
|
|
217
|
-
this.log.debug({ creativeId }, 'ssai ad discovered, prefetching creative');
|
|
218
235
|
prefetchCreative(creativeId);
|
|
219
236
|
},
|
|
220
237
|
onBreakStart: (range) => {
|
|
221
|
-
this.log.debug({ creativeId: range.creativeId }, 'ssai break start');
|
|
222
238
|
eventBus.emit('ssaiAdBreak', {
|
|
223
239
|
action: 'breakStart',
|
|
224
240
|
payload: { creativeId: range.creativeId },
|
|
@@ -226,7 +242,6 @@ export class Advertisement extends AbstractFeatureUnit {
|
|
|
226
242
|
loadCreative(range);
|
|
227
243
|
},
|
|
228
244
|
onCreativeChange: (range) => {
|
|
229
|
-
this.log.debug({ creativeId: range.creativeId }, 'ssai creative change');
|
|
230
245
|
loadCreative(range);
|
|
231
246
|
},
|
|
232
247
|
onAdEnding: (range) => {
|
|
@@ -239,11 +254,11 @@ export class Advertisement extends AbstractFeatureUnit {
|
|
|
239
254
|
this.ssaiAdBreak.set({ ...current, status: 'ending' });
|
|
240
255
|
},
|
|
241
256
|
onProgress: (range, event) => {
|
|
242
|
-
|
|
257
|
+
// Logged by the detector as `progress:<event>` at the moment playback
|
|
258
|
+
// crosses the marker; nothing to add here.
|
|
243
259
|
cache.fireTrackingById(range.creativeId, event);
|
|
244
260
|
},
|
|
245
261
|
onBreakEnd: () => {
|
|
246
|
-
this.log.debug('ssai break end');
|
|
247
262
|
const current = this.ssaiAdBreak.get();
|
|
248
263
|
eventBus.emit('ssaiAdBreak', {
|
|
249
264
|
action: 'breakEnd',
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
+
import { externalAd } from './units/external-ad.js';
|
|
3
|
+
import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad.js';
|
|
4
|
+
import { type PauseAdListOptions, type PauseAdStore } from './units/pause-ad.js';
|
|
5
|
+
import { type SsaiProviderOption } from './units/ssai-providers.js';
|
|
6
|
+
import { type HlsLike, type SsaiDebugSnapshot } from './units/ssai-detector.js';
|
|
7
|
+
import { type SsaiAdBreakStore } from './units/ssai-ad-break.js';
|
|
8
|
+
export type ExternalAdStore = ReturnType<ReturnType<typeof externalAd>>;
|
|
9
|
+
export type ExternalPauseAdStore = ReturnType<ReturnType<typeof externalPauseAd>>;
|
|
10
|
+
export type SsaiMonitorOptions = {
|
|
11
|
+
player?: HlsLike;
|
|
12
|
+
videoRef?: HTMLVideoElement;
|
|
13
|
+
manifestUrl?: string;
|
|
14
|
+
/** Creative provider: a ready instance, a built-in name, or `{ name, config }`. Defaults to `streamlayer`. */
|
|
15
|
+
provider?: SsaiProviderOption;
|
|
16
|
+
};
|
|
17
|
+
export declare class Advertisement extends AbstractFeatureUnit {
|
|
18
|
+
getExternalAd: (adUrl?: string) => [ExternalAdStore, string];
|
|
19
|
+
getExternalPauseAd: (adUrl: string, options: ExternalPauseAdOptions) => [ExternalPauseAdStore, string];
|
|
20
|
+
getPauseAdList: (options: PauseAdListOptions) => PauseAdStore;
|
|
21
|
+
readonly ssaiAdBreak: SsaiAdBreakStore;
|
|
22
|
+
startSsaiMonitoring: (options: SsaiMonitorOptions) => void;
|
|
23
|
+
stopSsaiMonitoring: () => void;
|
|
24
|
+
/**
|
|
25
|
+
* Fire the impression tracking pixel for a creative. Called by the UI the
|
|
26
|
+
* moment the bar actually renders the creative, so "impression" means the
|
|
27
|
+
* viewer saw it (not merely that metadata resolved).
|
|
28
|
+
*/
|
|
29
|
+
fireSsaiImpression: (creativeId: string) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Fire a named tracking beacon for a creative (`clickThrough`, or any event
|
|
32
|
+
* the creative carries). Lifecycle beacons — `start`, the quartiles and
|
|
33
|
+
* `complete` — are fired automatically by the detector as playback crosses
|
|
34
|
+
* them; this is for the ones only the UI knows about.
|
|
35
|
+
*/
|
|
36
|
+
fireSsaiTracking: (creativeId: string, event: string) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Fire the companion's `clickTracking` beacons. Exposed for hosts that render
|
|
39
|
+
* the companion banner themselves — the TV overlay has no companion surface,
|
|
40
|
+
* so nothing in this SDK calls it.
|
|
41
|
+
*/
|
|
42
|
+
fireSsaiCompanionClick: (creativeId: string) => void;
|
|
43
|
+
/**
|
|
44
|
+
* User-dismissed the current break: hide the bar now and keep it hidden for
|
|
45
|
+
* the rest of this break (a later break/creative shows again).
|
|
46
|
+
*/
|
|
47
|
+
dismissSsaiBreak: () => void;
|
|
48
|
+
/**
|
|
49
|
+
* Read-only timing introspection of the running SSAI monitor (null while no
|
|
50
|
+
* monitor runs). Feeds the demo app's SSAI debug window; safe to poll.
|
|
51
|
+
*/
|
|
52
|
+
getSsaiDebugSnapshot: () => SsaiDebugSnapshot | null;
|
|
53
|
+
private externalAdStores;
|
|
54
|
+
private externalPauseAdStores;
|
|
55
|
+
private externalPauseAdEnabled;
|
|
56
|
+
private pauseAdStore;
|
|
57
|
+
private pauseAdEnabled;
|
|
58
|
+
private ssaiDetector;
|
|
59
|
+
private ssaiCache;
|
|
60
|
+
private log;
|
|
61
|
+
constructor(instance: StreamLayerContext);
|
|
62
|
+
connect: () => void;
|
|
63
|
+
disconnect: () => void;
|
|
64
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sdkWebInterfaces = require('@streamlayer/sdk-web-interfaces');
|
|
4
|
+
var sdkWebLogger = require('@streamlayer/sdk-web-logger');
|
|
5
|
+
var externalAd = require('./units/external-ad.js');
|
|
6
|
+
var externalPauseAd = require('./units/external-pause-ad.js');
|
|
7
|
+
var pauseAd = require('./units/pause-ad.js');
|
|
8
|
+
var ssaiCreative = require('./units/ssai-creative.js');
|
|
9
|
+
var ssaiProviders = require('./units/ssai-providers.js');
|
|
10
|
+
var ssaiDetector = require('./units/ssai-detector.js');
|
|
11
|
+
var ssaiAdBreak = require('./units/ssai-ad-break.js');
|
|
12
|
+
var ssaiLog = require('./units/ssai-log.js');
|
|
13
|
+
var ssaiPreload = require('./units/ssai-preload.js');
|
|
14
|
+
|
|
15
|
+
class Advertisement extends sdkWebInterfaces.AbstractFeatureUnit {
|
|
16
|
+
getExternalAd;
|
|
17
|
+
getExternalPauseAd;
|
|
18
|
+
getPauseAdList;
|
|
19
|
+
ssaiAdBreak = ssaiAdBreak.createSsaiAdBreakStore();
|
|
20
|
+
startSsaiMonitoring;
|
|
21
|
+
stopSsaiMonitoring;
|
|
22
|
+
/**
|
|
23
|
+
* Fire the impression tracking pixel for a creative. Called by the UI the
|
|
24
|
+
* moment the bar actually renders the creative, so "impression" means the
|
|
25
|
+
* viewer saw it (not merely that metadata resolved).
|
|
26
|
+
*/
|
|
27
|
+
fireSsaiImpression;
|
|
28
|
+
/**
|
|
29
|
+
* Fire a named tracking beacon for a creative (`clickThrough`, or any event
|
|
30
|
+
* the creative carries). Lifecycle beacons — `start`, the quartiles and
|
|
31
|
+
* `complete` — are fired automatically by the detector as playback crosses
|
|
32
|
+
* them; this is for the ones only the UI knows about.
|
|
33
|
+
*/
|
|
34
|
+
fireSsaiTracking;
|
|
35
|
+
/**
|
|
36
|
+
* Fire the companion's `clickTracking` beacons. Exposed for hosts that render
|
|
37
|
+
* the companion banner themselves — the TV overlay has no companion surface,
|
|
38
|
+
* so nothing in this SDK calls it.
|
|
39
|
+
*/
|
|
40
|
+
fireSsaiCompanionClick;
|
|
41
|
+
/**
|
|
42
|
+
* User-dismissed the current break: hide the bar now and keep it hidden for
|
|
43
|
+
* the rest of this break (a later break/creative shows again).
|
|
44
|
+
*/
|
|
45
|
+
dismissSsaiBreak;
|
|
46
|
+
/**
|
|
47
|
+
* Read-only timing introspection of the running SSAI monitor (null while no
|
|
48
|
+
* monitor runs). Feeds the demo app's SSAI debug window; safe to poll.
|
|
49
|
+
*/
|
|
50
|
+
getSsaiDebugSnapshot;
|
|
51
|
+
externalAdStores = new Map();
|
|
52
|
+
externalPauseAdStores = new Map();
|
|
53
|
+
externalPauseAdEnabled = new Map();
|
|
54
|
+
pauseAdStore = null;
|
|
55
|
+
pauseAdEnabled = null;
|
|
56
|
+
ssaiDetector = null;
|
|
57
|
+
ssaiCache = null;
|
|
58
|
+
log;
|
|
59
|
+
constructor(instance) {
|
|
60
|
+
super();
|
|
61
|
+
this.log = sdkWebLogger.createLogger('advertisement');
|
|
62
|
+
const externalAdGenerator = externalAd.externalAd(instance);
|
|
63
|
+
const externalPauseAdGenerator = externalPauseAd.externalPauseAd(instance);
|
|
64
|
+
const pauseAdGenerator = pauseAd.pauseAdListStore(instance);
|
|
65
|
+
this.fireSsaiImpression = () => undefined;
|
|
66
|
+
this.fireSsaiTracking = () => undefined;
|
|
67
|
+
this.fireSsaiCompanionClick = () => undefined;
|
|
68
|
+
this.dismissSsaiBreak = () => undefined;
|
|
69
|
+
// Stable across start/stop cycles — always answers from the CURRENT detector.
|
|
70
|
+
this.getSsaiDebugSnapshot = () => this.ssaiDetector?.getDebugSnapshot() ?? null;
|
|
71
|
+
this.getPauseAdList = (options) => {
|
|
72
|
+
if (this.pauseAdStore) {
|
|
73
|
+
const $enabled = this.pauseAdEnabled;
|
|
74
|
+
const wasDisabled = $enabled.get() === false;
|
|
75
|
+
$enabled.set(options.$enabled);
|
|
76
|
+
if (wasDisabled && options.$enabled) {
|
|
77
|
+
window.requestAnimationFrame(() => {
|
|
78
|
+
if (this.pauseAdStore)
|
|
79
|
+
this.pauseAdStore.$list.revalidate();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return this.pauseAdStore;
|
|
83
|
+
}
|
|
84
|
+
const $enabled = sdkWebInterfaces.createSingleStore(options.$enabled);
|
|
85
|
+
this.pauseAdEnabled = $enabled;
|
|
86
|
+
this.pauseAdStore = pauseAdGenerator($enabled, options);
|
|
87
|
+
return this.pauseAdStore;
|
|
88
|
+
};
|
|
89
|
+
this.getExternalAd = (adUrl = '') => {
|
|
90
|
+
if (this.externalAdStores.has(adUrl)) {
|
|
91
|
+
const $store = this.externalAdStores.get(adUrl);
|
|
92
|
+
return [$store, adUrl];
|
|
93
|
+
}
|
|
94
|
+
const store = externalAdGenerator(adUrl);
|
|
95
|
+
this.externalAdStores.set(adUrl, store);
|
|
96
|
+
return [store, adUrl];
|
|
97
|
+
};
|
|
98
|
+
this.getExternalPauseAd = (adUrl, options) => {
|
|
99
|
+
// Store identity is (template, url): the template selects a DIFFERENT creative
|
|
100
|
+
// from the SAME tag, so the two must not share a store. Required because the
|
|
101
|
+
// cache-hit branch below applies only `$enabled` and drops the rest of `options`.
|
|
102
|
+
const storeKey = `${options.template ?? 'default'}::${adUrl}`;
|
|
103
|
+
if (this.externalPauseAdStores.has(storeKey)) {
|
|
104
|
+
const $enabled = this.externalPauseAdEnabled.get(storeKey);
|
|
105
|
+
const $store = this.externalPauseAdStores.get(storeKey);
|
|
106
|
+
const shouldRevalidate = $enabled.get() === false && options.$enabled === true;
|
|
107
|
+
$enabled.set(options.$enabled);
|
|
108
|
+
if (shouldRevalidate) {
|
|
109
|
+
window.requestAnimationFrame(() => {
|
|
110
|
+
$store.revalidate();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// The 2nd element MUST stay the BARE url — `ExposedPauseAd` compares it to
|
|
114
|
+
// `vastUrlRef.current` (index.tsx:121) to scope store errors to the selected tag.
|
|
115
|
+
return [$store, adUrl];
|
|
116
|
+
}
|
|
117
|
+
const $enabled = sdkWebInterfaces.createSingleStore(options.$enabled);
|
|
118
|
+
const store = externalPauseAdGenerator(adUrl, $enabled, options);
|
|
119
|
+
this.externalPauseAdEnabled.set(storeKey, $enabled);
|
|
120
|
+
this.externalPauseAdStores.set(storeKey, store);
|
|
121
|
+
return [store, adUrl];
|
|
122
|
+
};
|
|
123
|
+
this.startSsaiMonitoring = (options) => {
|
|
124
|
+
this.stopSsaiMonitoring();
|
|
125
|
+
const provider = ssaiProviders.resolveProvider(options.provider);
|
|
126
|
+
const cache = new ssaiCreative.SsaiCreativeCache(provider);
|
|
127
|
+
this.ssaiCache = cache;
|
|
128
|
+
/**
|
|
129
|
+
* Every beacon the SDK fires gets a line, with the URL it was fired at.
|
|
130
|
+
* The narrative log is read as a live trace of one ad, and "we called the
|
|
131
|
+
* ad server's own tracking URL" is as much of the story as the request
|
|
132
|
+
* that fetched the creative in the first place.
|
|
133
|
+
*/
|
|
134
|
+
const logBeacon = (creativeId, event) => {
|
|
135
|
+
const url = cache.peek(creativeId)?.trackingEvents?.find((entry) => entry.event === event)?.url;
|
|
136
|
+
ssaiLog.ssaiq.info({ creativeId, event, url }, `[beacon] ${event} fired — ${creativeId}`);
|
|
137
|
+
};
|
|
138
|
+
this.fireSsaiImpression = (creativeId) => {
|
|
139
|
+
// The UI only calls this once the bar is actually rendered, so this is
|
|
140
|
+
// the moment the viewer saw the ad — not the moment it resolved.
|
|
141
|
+
ssaiLog.ssaiq.info({ creativeId }, `[ad] ad on screen — ${cache.peek(creativeId)?.advertiser || creativeId}`);
|
|
142
|
+
logBeacon(creativeId, 'impression');
|
|
143
|
+
cache.fireImpressionById(creativeId);
|
|
144
|
+
};
|
|
145
|
+
this.fireSsaiTracking = (creativeId, event) => {
|
|
146
|
+
logBeacon(creativeId, event);
|
|
147
|
+
cache.fireTrackingById(creativeId, event);
|
|
148
|
+
};
|
|
149
|
+
this.fireSsaiCompanionClick = (creativeId) => {
|
|
150
|
+
cache.fireCompanionClickById(creativeId);
|
|
151
|
+
};
|
|
152
|
+
// Creative ids whose artwork preload has completed. Tracked apart from the
|
|
153
|
+
// cache: `peek` alone would let the bar show with a logo still in flight.
|
|
154
|
+
const preloadedIds = new Set();
|
|
155
|
+
/**
|
|
156
|
+
* Resolve + warm a creative the moment its id shows up in the manifest —
|
|
157
|
+
* typically seconds before its break starts, since the playhead trails the
|
|
158
|
+
* live edge. By break start the sync fast-path in `loadCreative` then flips
|
|
159
|
+
* the store straight to 'ready'. Failure here is only a debug note: the
|
|
160
|
+
* break may still be minutes away, and `loadCreative` retries (and warns)
|
|
161
|
+
* when the ad actually starts.
|
|
162
|
+
*/
|
|
163
|
+
const prefetchCreative = (creativeId) => {
|
|
164
|
+
cache
|
|
165
|
+
.resolve(creativeId)
|
|
166
|
+
.then((creative) => ssaiPreload.preloadCreativeAssets(creative).then(() => creative))
|
|
167
|
+
.then((creative) => {
|
|
168
|
+
preloadedIds.add(creativeId);
|
|
169
|
+
ssaiLog.ssaiq.info({ creativeId }, `[ad] creative ready — ${creative.advertiser} (artwork preloaded)`);
|
|
170
|
+
})
|
|
171
|
+
.catch(() => {
|
|
172
|
+
// Already on `ssaiq` from the provider; the break may still be
|
|
173
|
+
// minutes away, and `loadCreative` retries when it actually starts.
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
const publishReady = (range, creative) => {
|
|
177
|
+
this.ssaiAdBreak.set({
|
|
178
|
+
creativeId: range.creativeId,
|
|
179
|
+
creative,
|
|
180
|
+
status: 'ready',
|
|
181
|
+
startMs: range.startMs,
|
|
182
|
+
durationSec: range.durationSec,
|
|
183
|
+
});
|
|
184
|
+
// "loaded" = metadata resolved. Impression (eventBus + pixel) fires
|
|
185
|
+
// from the UI when the bar is actually rendered, not here.
|
|
186
|
+
sdkWebInterfaces.eventBus.emit('ssaiAdBreak', {
|
|
187
|
+
action: 'vastLoaded',
|
|
188
|
+
payload: {
|
|
189
|
+
creativeId: creative.creativeId,
|
|
190
|
+
advertiser: creative.advertiser,
|
|
191
|
+
clickThroughUrl: creative.clickThroughUrl,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
const loadCreative = (range) => {
|
|
196
|
+
// Fast path: prefetched at discovery, artwork warm — go straight to
|
|
197
|
+
// 'ready'. Skipping the 'detecting' hop matters for pods in particular:
|
|
198
|
+
// it momentarily empties the store, which the UI renders as the bar
|
|
199
|
+
// starting to close between two back-to-back ads.
|
|
200
|
+
const prefetched = cache.peek(range.creativeId);
|
|
201
|
+
if (prefetched && preloadedIds.has(range.creativeId)) {
|
|
202
|
+
publishReady(range, prefetched);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
this.ssaiAdBreak.set({
|
|
206
|
+
creativeId: range.creativeId,
|
|
207
|
+
status: 'detecting',
|
|
208
|
+
startMs: range.startMs,
|
|
209
|
+
durationSec: range.durationSec,
|
|
210
|
+
});
|
|
211
|
+
cache
|
|
212
|
+
.resolve(range.creativeId)
|
|
213
|
+
// Warm the artwork BEFORE flipping to 'ready', so the bar fades in with
|
|
214
|
+
// its logo already decoded instead of popping it in mid-animation. Capped
|
|
215
|
+
// internally, so a slow image host delays nothing.
|
|
216
|
+
.then((creative) => ssaiPreload.preloadCreativeAssets(creative).then(() => {
|
|
217
|
+
preloadedIds.add(range.creativeId);
|
|
218
|
+
return creative;
|
|
219
|
+
}))
|
|
220
|
+
.then((creative) => {
|
|
221
|
+
const current = this.ssaiAdBreak.get();
|
|
222
|
+
// Re-checked after the preload as well as the fetch: the break can end
|
|
223
|
+
// while either is in flight.
|
|
224
|
+
if (!current || current.creativeId !== range.creativeId) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
publishReady(range, creative);
|
|
228
|
+
})
|
|
229
|
+
.catch((error) => {
|
|
230
|
+
this.log.warn(error, 'ssai creative resolution failed');
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
this.ssaiDetector = ssaiDetector.createSsaiDetector({
|
|
234
|
+
player: options.player,
|
|
235
|
+
videoRef: options.videoRef,
|
|
236
|
+
manifestUrl: options.manifestUrl,
|
|
237
|
+
onAdDiscovered: (creativeId) => {
|
|
238
|
+
prefetchCreative(creativeId);
|
|
239
|
+
},
|
|
240
|
+
onBreakStart: (range) => {
|
|
241
|
+
sdkWebInterfaces.eventBus.emit('ssaiAdBreak', {
|
|
242
|
+
action: 'breakStart',
|
|
243
|
+
payload: { creativeId: range.creativeId },
|
|
244
|
+
});
|
|
245
|
+
loadCreative(range);
|
|
246
|
+
},
|
|
247
|
+
onCreativeChange: (range) => {
|
|
248
|
+
loadCreative(range);
|
|
249
|
+
},
|
|
250
|
+
onAdEnding: (range) => {
|
|
251
|
+
const current = this.ssaiAdBreak.get();
|
|
252
|
+
// Only a bar currently showing this creative has anything to wind
|
|
253
|
+
// down — a dismissed or still-resolving break stays as it is.
|
|
254
|
+
if (!current || current.creativeId !== range.creativeId || current.status !== 'ready') {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
this.ssaiAdBreak.set({ ...current, status: 'ending' });
|
|
258
|
+
},
|
|
259
|
+
onProgress: (range, event) => {
|
|
260
|
+
// Logged by the detector as `progress:<event>` at the moment playback
|
|
261
|
+
// crosses the marker; nothing to add here.
|
|
262
|
+
cache.fireTrackingById(range.creativeId, event);
|
|
263
|
+
},
|
|
264
|
+
onBreakEnd: () => {
|
|
265
|
+
const current = this.ssaiAdBreak.get();
|
|
266
|
+
sdkWebInterfaces.eventBus.emit('ssaiAdBreak', {
|
|
267
|
+
action: 'breakEnd',
|
|
268
|
+
payload: { creativeId: current?.creativeId },
|
|
269
|
+
});
|
|
270
|
+
this.ssaiAdBreak.set(null);
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
this.dismissSsaiBreak = () => {
|
|
274
|
+
this.ssaiDetector?.dismissActiveBreak();
|
|
275
|
+
this.ssaiAdBreak.set(null);
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
this.stopSsaiMonitoring = () => {
|
|
279
|
+
if (this.ssaiDetector) {
|
|
280
|
+
this.ssaiDetector.destroy();
|
|
281
|
+
this.ssaiDetector = null;
|
|
282
|
+
}
|
|
283
|
+
if (this.ssaiCache) {
|
|
284
|
+
this.ssaiCache.destroy();
|
|
285
|
+
this.ssaiCache = null;
|
|
286
|
+
}
|
|
287
|
+
this.fireSsaiImpression = () => undefined;
|
|
288
|
+
this.fireSsaiTracking = () => undefined;
|
|
289
|
+
this.fireSsaiCompanionClick = () => undefined;
|
|
290
|
+
this.dismissSsaiBreak = () => undefined;
|
|
291
|
+
this.ssaiAdBreak.set(null);
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
connect = () => {
|
|
295
|
+
this.log.debug('connect');
|
|
296
|
+
};
|
|
297
|
+
disconnect = () => {
|
|
298
|
+
this.log.debug('disconnect');
|
|
299
|
+
this.stopSsaiMonitoring();
|
|
300
|
+
this.externalAdStores.forEach((store) => {
|
|
301
|
+
store.off();
|
|
302
|
+
});
|
|
303
|
+
this.externalAdStores.clear();
|
|
304
|
+
this.externalPauseAdStores.forEach((store) => {
|
|
305
|
+
store.off();
|
|
306
|
+
});
|
|
307
|
+
this.externalPauseAdEnabled.forEach((enabled) => {
|
|
308
|
+
enabled.off();
|
|
309
|
+
});
|
|
310
|
+
this.externalPauseAdEnabled.clear();
|
|
311
|
+
this.externalPauseAdStores.clear();
|
|
312
|
+
if (this.pauseAdStore) {
|
|
313
|
+
this.pauseAdStore.$list.off();
|
|
314
|
+
this.pauseAdStore.$index.off();
|
|
315
|
+
this.pauseAdEnabled?.off();
|
|
316
|
+
this.pauseAdStore = null;
|
|
317
|
+
this.pauseAdEnabled = null;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
exports.Advertisement = Advertisement;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// <reference path="../vast-client.d.ts" />
|
|
2
|
+
import { StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
|
|
3
|
+
declare module '@streamlayer/sdk-web-interfaces' {
|
|
4
|
+
interface StreamLayerSDK {
|
|
5
|
+
advertisement: import('./advertisement.js').Advertisement;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export { type ExternalAdStore, type ExternalPauseAdStore, type SsaiMonitorOptions } from './advertisement.js';
|
|
9
|
+
export { type GAMStaticCore, type PauseAdTemplate, isFullbleedVideoTemplate } from './units/external-pause-ad.js';
|
|
10
|
+
export { INSIGHT_BODY_MAX_LENGTH, type PauseAdCard, type PauseAdKind, type PauseAdMediaMode, } from './units/pause-ad-card.js';
|
|
11
|
+
export { type PauseAdStore } from './units/pause-ad.js';
|
|
12
|
+
export { type AdStore, type AdData, type AdCompanion, type AdNonLinear } from './units/external-ad.js';
|
|
13
|
+
export { selectSponsorLogo, type SponsorLogoAsset } from './units/sponsor-logo.js';
|
|
14
|
+
export { gam, type GamNonceResult, type PalNonceManager } from './units/gam.js';
|
|
15
|
+
export { createCompanionTracker, createVastTracker, resolveVastTrackerTarget, type VastTrackerTarget, } from './units/vast-tracker.js';
|
|
16
|
+
export { type SsaiCreative, type SsaiTrackingEvent, type SsaiCompanion, type SsaiProgressEvent, DEFAULT_CREATIVE_API_BASE, } from './units/ssai-creative.js';
|
|
17
|
+
export { type SsaiCreativeProvider, type SsaiProviderOption, type SsaiProviderName, type SsaiProviderConfig, type StreamLayerProviderConfig, type QuortexProviderConfig, createStreamLayerProvider, createQuortexProvider, createVastProvider, resolveProvider, fireBeacon, fireTrackingEvents, BUILTIN_PROVIDERS, } from './units/ssai-providers.js';
|
|
18
|
+
export { type SsaiDateRange, type SsaiTrackingMarker, type HlsLike, type SsaiClockSource, type SsaiDebugEvent, type SsaiDebugRange, type SsaiDebugSnapshot, } from './units/ssai-detector.js';
|
|
19
|
+
export { preloadImages, preloadCreativeAssets, DEFAULT_PRELOAD_TIMEOUT_MS } from './units/ssai-preload.js';
|
|
20
|
+
export { type SsaiAdBreakState, type SsaiAdBreakStatus, type SsaiAdBreakStore } from './units/ssai-ad-break.js';
|
|
21
|
+
export declare const advertisement: (instance: StreamLayerContext, _opts: unknown, done: () => void) => void;
|
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var advertisement$1 = require('./advertisement.js');
|
|
4
|
+
var externalPauseAd = require('./units/external-pause-ad.js');
|
|
5
|
+
var pauseAdCard = require('./units/pause-ad-card.js');
|
|
6
|
+
var sponsorLogo = require('./units/sponsor-logo.js');
|
|
7
|
+
var gam = require('./units/gam.js');
|
|
8
|
+
var vastTracker = require('./units/vast-tracker.js');
|
|
9
|
+
var ssaiProviders = require('./units/ssai-providers.js');
|
|
10
|
+
var ssaiPreload = require('./units/ssai-preload.js');
|
|
11
|
+
|
|
12
|
+
const advertisement = (instance, _opts, done) => {
|
|
13
|
+
instance.sdk.advertisement = new advertisement$1.Advertisement(instance);
|
|
14
|
+
instance.sdk.onMount({ name: 'advertisement' }, () => {
|
|
15
|
+
instance.sdk.advertisement.connect();
|
|
16
|
+
return () => {
|
|
17
|
+
instance.sdk.advertisement.disconnect();
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
done();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
exports.isFullbleedVideoTemplate = externalPauseAd.isFullbleedVideoTemplate;
|
|
24
|
+
exports.INSIGHT_BODY_MAX_LENGTH = pauseAdCard.INSIGHT_BODY_MAX_LENGTH;
|
|
25
|
+
exports.selectSponsorLogo = sponsorLogo.selectSponsorLogo;
|
|
26
|
+
exports.gam = gam.gam;
|
|
27
|
+
exports.createCompanionTracker = vastTracker.createCompanionTracker;
|
|
28
|
+
exports.createVastTracker = vastTracker.createVastTracker;
|
|
29
|
+
exports.resolveVastTrackerTarget = vastTracker.resolveVastTrackerTarget;
|
|
30
|
+
exports.BUILTIN_PROVIDERS = ssaiProviders.BUILTIN_PROVIDERS;
|
|
31
|
+
exports.DEFAULT_CREATIVE_API_BASE = ssaiProviders.DEFAULT_CREATIVE_API_BASE;
|
|
32
|
+
exports.createQuortexProvider = ssaiProviders.createQuortexProvider;
|
|
33
|
+
exports.createStreamLayerProvider = ssaiProviders.createStreamLayerProvider;
|
|
34
|
+
exports.createVastProvider = ssaiProviders.createVastProvider;
|
|
35
|
+
exports.fireBeacon = ssaiProviders.fireBeacon;
|
|
36
|
+
exports.fireTrackingEvents = ssaiProviders.fireTrackingEvents;
|
|
37
|
+
exports.resolveProvider = ssaiProviders.resolveProvider;
|
|
38
|
+
exports.DEFAULT_PRELOAD_TIMEOUT_MS = ssaiPreload.DEFAULT_PRELOAD_TIMEOUT_MS;
|
|
39
|
+
exports.preloadCreativeAssets = ssaiPreload.preloadCreativeAssets;
|
|
40
|
+
exports.preloadImages = ssaiPreload.preloadImages;
|
|
41
|
+
exports.advertisement = advertisement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AdTagParams } from '@streamlayer/sdk-web-core';
|
|
2
|
+
/**
|
|
3
|
+
* Fill the macros this SDK knows in an ad-tag URL. One timestamp per call, so a tag naming the macro
|
|
4
|
+
* twice stays self-consistent. `params` is read as given — the caller passes the host's CURRENT
|
|
5
|
+
* values at request time.
|
|
6
|
+
*/
|
|
7
|
+
export declare const substituteAdTagMacros: (url: string, params?: AdTagParams) => string;
|