@streamlayer/feature-advertisement 0.2.7 → 0.3.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.
@@ -1,8 +1,13 @@
1
1
  import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ import { externalAd, type ExternalAdOptions } from './units/external-ad';
2
3
  import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad';
4
+ export type ExternalAdStore = ReturnType<ReturnType<typeof externalAd>>;
3
5
  export type ExternalPauseAdStore = ReturnType<ReturnType<typeof externalPauseAd>>;
4
6
  export declare class Advertisement extends AbstractFeatureUnit {
7
+ getExternalAd: (adUrl: string, options: ExternalAdOptions) => [ExternalAdStore, string];
5
8
  getExternalPauseAd: (adUrl: string, options: ExternalPauseAdOptions) => [ExternalPauseAdStore, string];
9
+ private externalAdStores;
10
+ private externalAdEnabled;
6
11
  private externalPauseAdStores;
7
12
  private externalPauseAdEnabled;
8
13
  private log;
@@ -1,20 +1,42 @@
1
1
  import { AbstractFeatureUnit, createSingleStore } from '@streamlayer/sdk-web-interfaces';
2
2
  import { createLogger } from '@streamlayer/sdk-web-logger';
3
+ import { externalAd } from './units/external-ad';
3
4
  import { externalPauseAd } from './units/external-pause-ad';
4
5
  export class Advertisement extends AbstractFeatureUnit {
6
+ getExternalAd;
5
7
  getExternalPauseAd;
8
+ externalAdStores = new Map();
9
+ externalAdEnabled = new Map();
6
10
  externalPauseAdStores = new Map();
7
11
  externalPauseAdEnabled = new Map();
8
12
  log;
9
13
  constructor(instance) {
10
14
  super();
11
15
  this.log = createLogger('advertisement');
16
+ const externalAdGenerator = externalAd(instance);
12
17
  const externalPauseAdGenerator = externalPauseAd(instance);
18
+ this.getExternalAd = (adUrl, options) => {
19
+ if (this.externalAdStores.has(adUrl)) {
20
+ const $enabled = this.externalAdEnabled.get(adUrl);
21
+ const $store = this.externalAdStores.get(adUrl);
22
+ const shouldRevalidate = $enabled.get() === false && options.$enabled === true;
23
+ $enabled.set(options.$enabled);
24
+ if (shouldRevalidate) {
25
+ window.requestAnimationFrame(() => {
26
+ $store.revalidate();
27
+ });
28
+ }
29
+ return [$store, adUrl];
30
+ }
31
+ const $enabled = createSingleStore(options.$enabled);
32
+ const store = externalAdGenerator(adUrl, $enabled, options);
33
+ this.externalAdEnabled.set(adUrl, $enabled);
34
+ this.externalAdStores.set(adUrl, store);
35
+ return [store, adUrl];
36
+ };
13
37
  this.getExternalPauseAd = (adUrl, options) => {
14
38
  if (this.externalPauseAdStores.has(adUrl)) {
15
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
16
39
  const $enabled = this.externalPauseAdEnabled.get(adUrl);
17
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
18
40
  const $store = this.externalPauseAdStores.get(adUrl);
19
41
  const shouldRevalidate = $enabled.get() === false && options.$enabled === true;
20
42
  $enabled.set(options.$enabled);
@@ -37,6 +59,14 @@ export class Advertisement extends AbstractFeatureUnit {
37
59
  };
38
60
  disconnect = () => {
39
61
  this.log.debug('disconnect');
62
+ this.externalAdStores.forEach((store) => {
63
+ store.off();
64
+ });
65
+ this.externalAdEnabled.forEach((enabled) => {
66
+ enabled.off();
67
+ });
68
+ this.externalAdEnabled.clear();
69
+ this.externalAdStores.clear();
40
70
  this.externalPauseAdStores.forEach((store) => {
41
71
  store.off();
42
72
  });
package/lib/index.d.ts CHANGED
@@ -4,6 +4,7 @@ declare module '@streamlayer/sdk-web-interfaces' {
4
4
  advertisement: import('./advertisement').Advertisement;
5
5
  }
6
6
  }
7
- export { type ExternalPauseAdStore } from './advertisement';
7
+ export { type ExternalAdStore, type ExternalPauseAdStore } from './advertisement';
8
+ export { type GAMStaticAdCore } from './units/external-ad';
8
9
  export { type GAMStaticCore } from './units/external-pause-ad';
9
10
  export declare const advertisement: (instance: StreamLayerContext, _opts: unknown, done: () => void) => void;
@@ -0,0 +1,19 @@
1
+ import { createSingleStore, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ import '@streamlayer/sdk-web-api';
3
+ export type GAMStaticAdCore = {
4
+ id: string;
5
+ parentId: string;
6
+ url: string;
7
+ imageSrc?: string;
8
+ videoSrc?: string;
9
+ sponsorSrc?: string;
10
+ title?: string;
11
+ description?: string;
12
+ adUrl?: string;
13
+ };
14
+ export type ExternalAdOptions = {
15
+ refetchInterval: number;
16
+ prefetch: boolean;
17
+ $enabled: boolean;
18
+ };
19
+ export declare const externalAd: (instance: StreamLayerContext) => (gamUrl: string, $enabled: ReturnType<typeof createSingleStore<boolean>>, options: ExternalAdOptions) => import("@nanostores/query").FetcherStore<GAMStaticAdCore | undefined, any>;
@@ -0,0 +1,83 @@
1
+ import { createLogger } from '@streamlayer/sdk-web-logger';
2
+ import '@streamlayer/sdk-web-api';
3
+ import { VASTClient } from '@dailymotion/vast-client';
4
+ const vastClient = new VASTClient();
5
+ const log = createLogger('ui:gam-static-ad');
6
+ const preloadImage = (src) => {
7
+ return new Promise((resolve, reject) => {
8
+ const image = new Image();
9
+ image.onload = () => resolve(src);
10
+ image.onerror = () => reject();
11
+ image.src = src;
12
+ });
13
+ };
14
+ export const externalAd = (instance) => {
15
+ return (gamUrl, $enabled, options) => instance.transport.nanoquery.createFetcherStore([gamUrl, $enabled], {
16
+ dedupeTime: options.prefetch ? options.refetchInterval : Infinity,
17
+ revalidateInterval: options.refetchInterval > 0 ? options.refetchInterval : 0,
18
+ fetcher: async (adUrl) => {
19
+ if (!adUrl || typeof adUrl !== 'string') {
20
+ throw new Error('ad url is empty');
21
+ }
22
+ try {
23
+ const parsedVAST = await vastClient.get(adUrl);
24
+ const validAd = parsedVAST.ads.find((ads) => ads.creatives.length > 0);
25
+ if (validAd) {
26
+ const parentId = validAd.id || '';
27
+ const store = {
28
+ id: '',
29
+ parentId,
30
+ url: adUrl,
31
+ title: validAd.title || '',
32
+ description: validAd.description || '',
33
+ };
34
+ log.debug(validAd, 'validAd');
35
+ const linear = validAd.creatives.find((creative) => creative.type === 'linear');
36
+ if (linear) {
37
+ store.id = linear.adId || linear.id || parentId;
38
+ log.debug(linear, 'linear');
39
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
40
+ // @ts-ignore
41
+ store.videoSrc = linear.mediaFiles?.find((mediaFile) => !!mediaFile.fileURL)?.fileURL;
42
+ }
43
+ const nonlinear = validAd.creatives.find((creative) => creative.type === 'nonlinear');
44
+ if (nonlinear) {
45
+ store.id = nonlinear.adId || nonlinear.id || parentId;
46
+ log.debug(nonlinear, 'nonlinear');
47
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
48
+ // @ts-ignore
49
+ const staticResource = nonlinear.variations?.[0]?.staticResource;
50
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
51
+ // @ts-ignore
52
+ store.adUrl = nonlinear.variations?.[0]?.nonlinearClickThroughURLTemplate;
53
+ if (staticResource) {
54
+ await preloadImage(staticResource);
55
+ store.imageSrc = staticResource;
56
+ }
57
+ }
58
+ const companion = validAd.creatives.find((creative) => creative.type === 'companion');
59
+ log.debug(companion, 'companion');
60
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
61
+ // @ts-ignore
62
+ const staticResource = companion?.variations?.[0]?.staticResources?.[0]?.url;
63
+ if (staticResource) {
64
+ await preloadImage(staticResource);
65
+ store.sponsorSrc = staticResource;
66
+ }
67
+ if (store.imageSrc || store.videoSrc) {
68
+ return store;
69
+ }
70
+ }
71
+ throw new Error('no_valid_ad');
72
+ }
73
+ catch (err) {
74
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
75
+ // @ts-expect-error
76
+ if (err.message !== 'no_valid_ad' && err.message !== 'ad url is empty') {
77
+ console.error(err);
78
+ }
79
+ throw err;
80
+ }
81
+ },
82
+ });
83
+ };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@streamlayer/feature-advertisement",
3
- "version": "0.2.7",
3
+ "version": "0.3.0",
4
4
  "peerDependencies": {
5
5
  "@dailymotion/vast-client": "^6.4.2",
6
6
  "@streamlayer/sl-eslib": "^5.229.1",
7
7
  "nanostores": "^1.1.0",
8
- "@streamlayer/sdk-web-core": "^1.21.7",
9
- "@streamlayer/sdk-web-api": "^1.16.7",
10
- "@streamlayer/sdk-web-interfaces": "^1.9.15",
11
- "@streamlayer/sdk-web-types": "^1.17.7",
12
- "@streamlayer/sdk-web-logger": "^1.0.109"
8
+ "@streamlayer/sdk-web-core": "^1.22.0",
9
+ "@streamlayer/sdk-web-interfaces": "^1.10.0",
10
+ "@streamlayer/sdk-web-types": "^1.18.0",
11
+ "@streamlayer/sdk-web-api": "^1.17.0",
12
+ "@streamlayer/sdk-web-logger": "^1.1.0"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/vast-client": "^3.0.4"