@streamlayer/feature-advertisement 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/README.md +7 -0
- package/lib/advertisement.d.ts +12 -0
- package/lib/advertisement.js +49 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +11 -0
- package/lib/units/external-pause-ad.d.ts +15 -0
- package/lib/units/external-pause-ad.js +96 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
+
import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad';
|
|
3
|
+
export type ExternalPauseAdStore = ReturnType<ReturnType<typeof externalPauseAd>>;
|
|
4
|
+
export declare class Advertisement extends AbstractFeatureUnit {
|
|
5
|
+
getExternalPauseAd: (adUrl: string, options: ExternalPauseAdOptions) => [ExternalPauseAdStore, string];
|
|
6
|
+
private externalPauseAdStores;
|
|
7
|
+
private externalPauseAdEnabled;
|
|
8
|
+
private log;
|
|
9
|
+
constructor(instance: StreamLayerContext);
|
|
10
|
+
connect: () => void;
|
|
11
|
+
disconnect: () => void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AbstractFeatureUnit, createSingleStore } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
+
import { createLogger } from '@streamlayer/sdk-web-logger';
|
|
3
|
+
import { externalPauseAd } from './units/external-pause-ad';
|
|
4
|
+
export class Advertisement extends AbstractFeatureUnit {
|
|
5
|
+
getExternalPauseAd;
|
|
6
|
+
externalPauseAdStores = new Map();
|
|
7
|
+
externalPauseAdEnabled = new Map();
|
|
8
|
+
log;
|
|
9
|
+
constructor(instance) {
|
|
10
|
+
super();
|
|
11
|
+
this.log = createLogger('advertisement');
|
|
12
|
+
const externalPauseAdGenerator = externalPauseAd(instance);
|
|
13
|
+
this.getExternalPauseAd = (adUrl, options) => {
|
|
14
|
+
if (this.externalPauseAdStores.has(adUrl)) {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16
|
+
const $enabled = this.externalPauseAdEnabled.get(adUrl);
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
18
|
+
const $store = this.externalPauseAdStores.get(adUrl);
|
|
19
|
+
const shouldRevalidate = $enabled.get() === false && options.$enabled === true;
|
|
20
|
+
$enabled.set(options.$enabled);
|
|
21
|
+
if (shouldRevalidate) {
|
|
22
|
+
window.requestAnimationFrame(() => {
|
|
23
|
+
$store.revalidate();
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return [$store, adUrl];
|
|
27
|
+
}
|
|
28
|
+
const $enabled = createSingleStore(options.$enabled);
|
|
29
|
+
const store = externalPauseAdGenerator(adUrl, $enabled, options);
|
|
30
|
+
this.externalPauseAdEnabled.set(adUrl, $enabled);
|
|
31
|
+
this.externalPauseAdStores.set(adUrl, store);
|
|
32
|
+
return [store, adUrl];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
connect = () => {
|
|
36
|
+
this.log.debug('connect');
|
|
37
|
+
};
|
|
38
|
+
disconnect = () => {
|
|
39
|
+
this.log.debug('disconnect');
|
|
40
|
+
this.externalPauseAdStores.forEach((store) => {
|
|
41
|
+
store.off();
|
|
42
|
+
});
|
|
43
|
+
this.externalPauseAdEnabled.forEach((enabled) => {
|
|
44
|
+
enabled.off();
|
|
45
|
+
});
|
|
46
|
+
this.externalPauseAdEnabled.clear();
|
|
47
|
+
this.externalPauseAdStores.clear();
|
|
48
|
+
};
|
|
49
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
+
declare module '@streamlayer/sdk-web-interfaces' {
|
|
3
|
+
interface StreamLayerSDK {
|
|
4
|
+
advertisement: import('./advertisement').Advertisement;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export { type ExternalPauseAdStore } from './advertisement';
|
|
8
|
+
export { type GAMStaticCore } from './units/external-pause-ad';
|
|
9
|
+
export declare const advertisement: (instance: StreamLayerContext, _opts: unknown, done: () => void) => void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Advertisement } from './advertisement';
|
|
2
|
+
export const advertisement = (instance, _opts, done) => {
|
|
3
|
+
instance.sdk.advertisement = new Advertisement(instance);
|
|
4
|
+
instance.sdk.onMount({ name: 'advertisement' }, () => {
|
|
5
|
+
instance.sdk.advertisement.connect();
|
|
6
|
+
return () => {
|
|
7
|
+
instance.sdk.advertisement.disconnect();
|
|
8
|
+
};
|
|
9
|
+
});
|
|
10
|
+
done();
|
|
11
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createSingleStore, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
+
import '@streamlayer/sdk-web-api';
|
|
3
|
+
export type GAMStaticCore = {
|
|
4
|
+
id: string;
|
|
5
|
+
parentId: string;
|
|
6
|
+
url: string;
|
|
7
|
+
imageSrc?: string;
|
|
8
|
+
adUrl?: string;
|
|
9
|
+
};
|
|
10
|
+
export type ExternalPauseAdOptions = {
|
|
11
|
+
refetchInterval: number;
|
|
12
|
+
prefetch: boolean;
|
|
13
|
+
$enabled: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare const externalPauseAd: (instance: StreamLayerContext) => (gamUrl: string, $enabled: ReturnType<typeof createSingleStore<boolean>>, options: ExternalPauseAdOptions) => import("@nanostores/query").FetcherStore<GAMStaticCore | undefined, any>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { eventBus } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
+
import { createLogger } from '@streamlayer/sdk-web-logger';
|
|
3
|
+
import '@streamlayer/sdk-web-api';
|
|
4
|
+
import { VASTClient } from '@dailymotion/vast-client';
|
|
5
|
+
const vastClient = new VASTClient();
|
|
6
|
+
const log = createLogger('ui:gam-static');
|
|
7
|
+
const preloadImage = (src) => {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const image = new Image();
|
|
10
|
+
image.onload = () => resolve(src);
|
|
11
|
+
image.onerror = () => reject();
|
|
12
|
+
image.src = src;
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
export const externalPauseAd = (instance) => {
|
|
16
|
+
return (gamUrl, $enabled, options) => instance.transport.nanoquery.createFetcherStore([gamUrl, $enabled], {
|
|
17
|
+
dedupeTime: options.prefetch ? options.refetchInterval : Infinity,
|
|
18
|
+
revalidateInterval: options.refetchInterval > 0 ? options.refetchInterval : 0,
|
|
19
|
+
fetcher: async (adUrl) => {
|
|
20
|
+
if (!adUrl || typeof adUrl !== 'string') {
|
|
21
|
+
throw new Error('no_valid_ad');
|
|
22
|
+
}
|
|
23
|
+
eventBus.emit('exposedPauseAd', {
|
|
24
|
+
action: 'load',
|
|
25
|
+
payload: {},
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
const parsedVAST = await vastClient.get(adUrl);
|
|
29
|
+
const validAd = parsedVAST.ads.find((ads) => ads.creatives.length > 0);
|
|
30
|
+
if (validAd) {
|
|
31
|
+
const parentId = validAd.id || '';
|
|
32
|
+
log.debug(validAd, 'validAd');
|
|
33
|
+
const nonlinear = validAd.creatives.find((creative) => creative.type === 'nonlinear');
|
|
34
|
+
if (nonlinear) {
|
|
35
|
+
const id = nonlinear.adId || nonlinear.id || parentId;
|
|
36
|
+
log.debug(nonlinear, 'nonlinear');
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
const staticResource = nonlinear.variations?.[0]?.staticResource;
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
const adUrlTemplate = nonlinear.variations?.[0]?.nonlinearClickThroughURLTemplate;
|
|
43
|
+
if (staticResource) {
|
|
44
|
+
await preloadImage(staticResource);
|
|
45
|
+
eventBus.emit('exposedPauseAd', {
|
|
46
|
+
action: 'loaded',
|
|
47
|
+
payload: {
|
|
48
|
+
id,
|
|
49
|
+
parentId,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
id,
|
|
54
|
+
parentId,
|
|
55
|
+
url: adUrl,
|
|
56
|
+
imageSrc: staticResource,
|
|
57
|
+
adUrl: adUrlTemplate,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const companion = validAd.creatives.find((creative) => creative.type === 'companion');
|
|
62
|
+
log.debug(companion, 'companion');
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
const staticResource = companion?.variations?.[0]?.staticResources?.[0]?.url;
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
const adUrlTemplate = companion?.variations?.[0]?.companionClickThroughURLTemplate;
|
|
69
|
+
if (staticResource) {
|
|
70
|
+
const id = companion.adId || companion.id || parentId;
|
|
71
|
+
await preloadImage(staticResource);
|
|
72
|
+
eventBus.emit('exposedPauseAd', {
|
|
73
|
+
action: 'loaded',
|
|
74
|
+
payload: {
|
|
75
|
+
id,
|
|
76
|
+
parentId,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
id,
|
|
81
|
+
parentId,
|
|
82
|
+
url: adUrl,
|
|
83
|
+
imageSrc: staticResource,
|
|
84
|
+
adUrl: adUrlTemplate,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw new Error('no_valid_ad');
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.error(err, 'err');
|
|
92
|
+
throw new Error('no_valid_ad');
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@streamlayer/feature-advertisement",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"peerDependencies": {
|
|
5
|
+
"@dailymotion/vast-client": "^6.4.2",
|
|
6
|
+
"@streamlayer/sl-eslib": "^5.228.0",
|
|
7
|
+
"nanostores": "^1.1.0",
|
|
8
|
+
"@streamlayer/sdk-web-core": "^1.19.0",
|
|
9
|
+
"@streamlayer/sdk-web-api": "^1.15.0",
|
|
10
|
+
"@streamlayer/sdk-web-interfaces": "^1.9.0",
|
|
11
|
+
"@streamlayer/sdk-web-types": "^1.16.9",
|
|
12
|
+
"@streamlayer/sdk-web-logger": "^1.0.94"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/vast-client": "^3.0.4"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "./lib/index.js",
|
|
19
|
+
"typings": "./lib/index.d.ts",
|
|
20
|
+
"files": [
|
|
21
|
+
"lib/",
|
|
22
|
+
"package.json"
|
|
23
|
+
]
|
|
24
|
+
}
|