@creative-realities/vue-rvhost-plugin
TypeScript icon, indicating that this package has built-in type declarations

2.0.0 • Public • Published

RVHost Plugin 2.0.0

Breaking changes! Plugin has been adapted to use the getContentSnapshot call to RVHost. This will change data parsing in all associated 'boilerplate/plugin' based architectured apps. For apps using instanceProperties, use v1.6.3

as of 4/22/24, VZW Promo-15 and VZW 39" apps have been updated to this version of the plugin. ​ A Vue3 plugin for the CRI ReflectView player ecosystem.

Features

  • Pulls channel and feed containers from instance properties and makes them available to app developers.
  • Highly configurable developer options through a factory method flow.
  • Architected as a factory function that retrieves data, configures the app, and exposes APIs and data sets through provide/inject.
  • Supports Matomo Analytics configurable via instance properties.
  • Fires realtime containerUpdated event bus events when channel and feed containers are updated.
  • Supports user-defined update interval for single container apps.
  • Supports multi-container apps and their intervals through configuration and user-defined properties.
  • Provides unit testing of all API calls.
  • Instance properties can be mocked by providing a userDefinedProperties object.

Installation & Setup

Install PNPM:

pnpm add @creative-realities/vue-rvhost-plugin

Install Yarn:

yarn add @creative-realities/vue-rvhost-plugin

Install NPM:

npm i @creative-realities/vue-rvhost-plugin

Setup Main.js:

import { rvHostPluginFactory } from "@creative-realities/vue-rvhost-plugin";

// factory configuration object 
rvHostPluginFactory({
  logLevel: 'verbose', 'error', 'none',
  getHost: boolean,
  getLocation: boolean,
  getInstanceProperties: boolean,
  getLocationClassifications: boolean,
  getChannelClassifications: Number[],
  feedDetails: Array<{channel: number, interval?: number, instanceUID?: string,  feedChannelName?: string,}>,
  containerDetails: Array<{channel: number, interval?: number}>,
}).then((plugin) => {
  const app = createApp(App);

  // mock user-defined properties
  app.use(plugin, {
    userDefinedProperties: Record<string,string,
  });

  // mount vue 
  app.mount("#app");
});

Setup Main.js (Async/Await):

import { rvHostPluginFactory } from "@creative-realities/vue-rvhost-plugin";

const app = createApp(App);

// factory configuration object 
const plugin = await rvHostPluginFactory({
  logLevel: 'verbose', 'error', 'none',
  getHost: boolean,
  getLocation: boolean,
  getInstanceProperties: boolean,
  getLocationClassifications: boolean,
  getChannelClassifications: Number[],
  feedDetails: Array<{channel: number, interval?: number, instanceUID?: string,  feedChannelName?: string,}>,
  containerDetails: Array<{channel: number, interval?: number}>,
})


// mock user-defined properties
app.use(plugin, {
  userDefinedProperties: Record<string,string,
});

// mount vue 
app.mount("#app");

Usage & Configuration

The plugin will provide three important objects that are able to be injected through the vue provide/inject pattern: rvData, rvHost, and logger. These are described below:

1. rvData: This object encapsulates all of the data that will update on the configurable interval. The response will include the following data, contingent on the Options object that is passed into the rvHostPluginFactory (see plugin instantiation section for field descriptions):

export interface InitialData extends Options {
	matomoHost?: string
	matomoSiteId?: number
	hostInfo?: IHostInfo | null
	locationInfo?: ILocationInfo | null
	locationClassifications?: string[] | null
	channelClassifications?: ChannelClassification[]
	userDefinedProperties?: Record<string, string>
	containers?: Container[]
	feedContainers?: Container[]
}

Following is the prototype for the IHostInfo interface:

interface IHostInfo {
	ChannelNumber: string
	LocationCode: string
	PlayerID: string
	PlayerName: string
	PlayerNetworkName: string
	PlayerVersion?: string
	DeviceFamily?: string
	DeviceModel?: string
	DeviceSerial?: string
}

Following is the prototype for the ILocationInfo interface:

interface ILocationInfo {
	Address: string
	AddressLine2: string
	AdvertisementText?: string
	AdvertisementTempo?: string
	AtomicDuration?: string
	BaseCPM?: string
	City: string
	CloseDate?: string
	CreationDate?: string
	Currency?: string
	DeletionDate?: string
	DeniesAudio?: string
	Description: string
	FaxNumber: string
	Filter1?: string
	Filter2?: string
	Filter3?: string
	ID?: string
	ImageURL?: string
	Latitude: string
	LocationActive?: string
	LocationManaged?: string
	LocationTag: string
	Longitude: string
	ManagingEntity?: string
	Market: string
	Name: string
	OpenDate?: string
	PhoneNumber: string
	PhoneNumber2: string
	PostalCode: string
	Production?: string
	SiteOwner?: string
	StateOrProvince: string
	Tempo?: string
	TimeZone: string
	Traffic?: string
}

Following is the prototype for the Container Interface:

export interface Container {
	guid: string | null
	container: IContainer | null
	instanceUID?: string | null
	feedChannelName?: string | null
	newContainer: IContainer | null
	containerInterval: NodeJS.Timer | null
	hydrateContainer(): boolean
	updateMethod(): Promise<void>
}

The Container interface initalizes the container member to container data defined by the IContainer interface, shown below:

interface IContainer {
	ContainerType: string
	Creator: string
	CreatorVersion: string
	DataVersion: string
	FeedContainer: IFeedItem
	IsEventList: boolean
	IsSubList: boolean
	Items: IContainerItem[]
	Name: string
	RejectAuthority: boolean
	Schedule: string | null
	ShufflePercentage?: string | null
	SyncUID?: string | null
	UID: string
	UnconfiguredItem?: null
	version: string
}

A containerUpdated bus event fires when container data is refreshed. This will occur on a default interval of one hour, or at the override interval that may be provided via an updateInterval value exposed by Publisher.xml, or by passing in a UserDefinedProperties object with an updateInterval member initialized to the desired number of milliseconds. See the section titled "User Defined Property config" for more information about the UserDefinedProperties object. When this event fires, calling hydrateContainer() on the desired channelContainerData index will synchronize the app data against the refreshed data for that channel container.

import { useEventBus } from '@vueuse/core'

pluginBus.on(event => {
	switch (event.type) {
		case 'containerUpdated':
			console.log(event.info)
			// this bus notifies the app that there is new container data to be consumed.
			// Call the hydrateContainer Method for the container in rvData that matches the guid when desired by your application.
			break

		default:
			break
	}
})

2. RVHost:

Various API calls are provided by the RVHost. Not all of them are currently supported by the plugin. Following is the list of those that are currently supported:

RV_GetClassifications (asynchronous):
Purpose: Get the ReflectView Location Classifications associated with the ReflectView Location the Player is in.

Parameters:

  • {Function} callback Function to execute on completion or error.
  • {Object} thisArgument The "this" value when the callback is executed.
  • {Object} context An optional context object that is passed to the callback.

Return Value: If no callback is provided, it returns a Promise that will resolve with an Array of String classifications. Otherwise, this gets passed into the callback.

RV_GetChannelClassifications (asynchronous):
Purpose: Get the ReflectView Channel Classifications for channel and location.

Parameters:

  • {Number} channel Channel number, starting from a base of 1.
  • {Boolean} merge Get location classifications as well, defaults to false.
  • {Function} callback Function to execute on completion or error.
  • {Object} thisArgument The "this" value when the callback is executed.
  • {Object} context An optional context object that is passed to the callback.

Return Value: If no callback is provided, it returns a Promise that will resolve with an Array of String classifications. Otherwise, this gets passed into the callback.

RV_GetLocationInfo (asynchronous)
Purpose: Get the ReflectView Location info.

Parameters:

  • {Function} callback Function to execute on completion or error.
  • {Object} thisArgument The "this" value when the callback is executed.
  • {Object} context An optional context object that is passed to the callback.

Return Value: If no callback is provided, it returns a Promise that will resolve with an Object with details about the player location. Otherwise, this gets passed into the callback.

RV_GetHostInfo (asynchronous):
Purpose: Returns host information

Parameters:

  • {Function} callback Function to execute on completion or error.
  • {Object} thisArgument The "this" value when the callback is executed.
  • {Object} context An optional context object that is passed to the callback.

Return Value: If no callback is provided, it returns a Promise that will resolve with an Object with details about the ReflectView Player hosting the application. Otherwise, this gets passed into the callback.

RV_GetChannelContainers (asynchronous):
Purpose: Get a list of Guids for Pending, Playing, Playlist, and Validated channel container statuses. Playing is generally the GUID you will want to use when calling getContainer.

Parameters:

  • {Number} channel Channel number, starting from a base of 1.
  • {Function} callback Function to execute on completion or error.
  • {Object} thisArgument The "this" value when the callback is executed.
  • {Object} context An optional context object that is passed to the callback.

Return Value: If no callback is provided, it returns a Promise that will resolve with an Object with GUIDs for containers by Status. Otherwise, this gets passed into the callback.

RV_GetContainer (asynchronous):
Purpose: Returns container information for the provided channel and Guid. A container could be a ReflectView Playlist or Catalog, or data generated from a ReflectView Feedsource module. The example given uses it in combination with getChannelContainers. It would also commonly be used with the result from getFeedChannelContainers. The example also shows a common pattern where an application would exit if its required playlist content and data are not available. If an app exits immediately, it will be unnoticable and the player moves on to its next content item.

Parameters

  • {Number} channel Channel number, starting from a base of 1.
  • {String} guid The Playing guid for the associated channel. See getChannelContainers
  • {Function} callback Function to execute on completion or error.
  • {Object} thisArgument The "this" value when the callback is executed.
  • {Object} context An optional context object that is passed to the callback.

Return Value: If no callback is provided, it returns a Promise that will resolve with an Object representation of a Container. Otherwise, this gets passed into the callback.

RV_GetFeedChannelContainers (asynchronous):
Purpose: Get a list of GUIDs by status, for Activated, Pending and Validated. Activated is generally the GUID you will want to use when calling getContainer. Feed containers are generated by ReflectView Feedsource modules.

Parameters:

  • {Number} channel Channel number, starting from a base of 1.
  • {String} instanceUID
  • {String} feedChannelName
  • {Function} callback Function to execute on completion or error.
  • {Object} thisArgument The "this" value when the callback is executed.
  • {Object} context An optional context object that is passed to the callback.

Return Value: If no callback is provided, it returns a Promise that will resolve with an Object with GUIDs for containers by Status. Otherwise, this gets passed into the callback.

RV_GetInstanceProperties (asynchronous):
Purpose: Returns instance properties associated with an instance of an application in a Playlist. Instance properties can be set in the ReflectView Publisher on a Playlist item. This allows you to schedule the same application multiple times in a Playlist, while assigning different data for each instance of the application in that Playlist.

Parameters:

  • {Function} callback Function to execute on completion or error.
  • {Object} thisArgument The "this" value when the callback is executed.
  • {Object} context An optional context object that is passed to the callback.

Return Value: {object} associated properties.

RV_Trace (asynchronous): Purpose: Writes output to the host logfile.

Parameters:

  • {Any}

Return Value: None.


3. logger:

The logger utility is available via provide / inject as follows:

const logger = inject('logger')
const rvHost = RVHost.getRVHost()
if (rvHost.isRunnable) rvHost.log(message)

Entries may be written to the Developer's Console and the RVHost log file, or to neither if logging is turned off. The log level is determined by the Options.logLevel that is passed into the rvHostPluginFactory when it is instantiated. Available levels are:

none or undefined: calls to log will not write to the console or call the RVHost log API.
error: calls to log will write to the console.
verbose: calls to log will write to the console and also call the RVHost log API.


Example:

In a .vue file the APIs, data sets, and logging capabilities may then be injected into the app as follows:

const logger = inject('logger')
log('This is an app log entry.');

Plugin instantiation

Instantiate the plugin with an Options object which may include any of the following optional members. For those calls that call APIs, the response data will be made available to the app in the rvData object, which is provided and here and injected into the app:

  • getHost: boolean - if true then hostInfo will be pulled and returned. This will expose the following:
    channelNumber: the channel on which the app is running
    DeviceFamily: the player OS (e.g., "malibu")
    DeviceFirmware: the player firmware version
    DeviceModel: the player model (e.g., "XD1034")
    DeviceSerial: the player serial number
    LocationCode: the player Location Code as assigned in Publisher
    PlayerID: the unique UID that identifies the player
    PlayerName: the player name as assigned in Publisher
    PlayerNetworkName: the player name as assigned in the RV Server
    PlayerVersion: the Reflect Player version

  • getLocation: boolean - if true then player location info will be pulled and returned. The response object will include the following:
    Address, AddressLine2, City, Description, FaxNumber, Latitude, LocationTag, Longitude, Market, Name, PhoneNumber, PhoneNumber2, PostalCode, StateOrProvince, TimeZone

  • getInstanceProperties: boolean - if true then userDefinedProperties will be pulled and returned.

  • getLocationClassifications: boolean - if true then locationClassifications will be pulled and returned as an array of location classification objects.

  • getChannelClassifications: number array - channelClassifications will be pulled and returned as an array of channel classification objects having the shape: {[channel: number, classifications: [string]]}

  • containerDetails: ChannelContainerOptions - container details will be pulled and returned as an array of objects. The ChannelContainerOptions object has the shape: {channel: number, interval: number | undefined }

  • feedDetails: FeedChannelOptions array - feedContainers details will be pulled and returned as an array of objects. FeedChannelOptions are a modified ChannelContainerOptions object:

	ChannelContainerOptions {
		channel: number
		interval: number | undefined
	}

	FeedChannelOptions = {
		ChannelContainerOptions,
		{
			type: 'Activated'
			instanceUID?: string
			feedChannelName?: string
		}
	}
  • matomoOptions: PluginOptions, defined as follows:
    immediateSession: boolean - if true then the session will start immediately after loading the Matomo libraries; otherwise it will wait 200 milliseconds.
    isDevelopment: boolean - if true then calls to getMatomoHost will return the default Matomo host and siteId; otherwise the actual Matomo host info will be returned.
    matomoHost: string - the Matomo host URI.
    matomoSiteId: string | number - the Matomo Site Id as defined in the Matomo portal.
    debugMatomo: boolean - sets the value of the VueMatomo plugin debug flag.
    useHttp: boolean - if true then Matomo will be setup using the fully qualified base URL. Otherwise it will setup using host and siteId.
    dimensions: Record<number, string> - an array of key/value pairs of custom dimensions.

  • userDefinedProperties: Record<string, string> - an array of key/value pairs with custom user-defined properties. See the section titled "User Defined Property config" for more information.

  • logLevel: string | undefined - defined as follows:
    none or undefined: calls to log will not write to the console or call the RVHost log API.
    error: calls to log will write to the console.
    verbose: calls to log will write to the console and also call the RVHost log API.


User Defined Property config (added in minor 1.2.0)

​ Refresh intervals may be defined by channel and feed number using Channel-1.Interval / Feed-1.Interval / Feed-1.Channel as the naming convention for the user-defined properties. These can be mapped to specific channel or feedContainers in the app. This is to configure apps that have multiple sources, but we also support an updateInterval property for single source apps. Note that this property can only be used if there is a single source; the app will log a warning if used with multiple sources.


HelloWorld Example

App.vue

<template>
  <hello-world :msg="helloMessage" />
</template>

<script setup>
/** Utils */
import { useEventBus } from "@vueuse/core";

/** Components */
import HelloWorld from "@/components/HelloWorld.vue";
import { inject } from "vue";

const helloMessage = "Welcome to the rvHost boilerplate";
const rvData = inject("rvData");
const rvHost = inject("rvHost");
const logger = inject("logger");
const eventBusKey = Symbol("eventBusSymbol");

console.log(logger);
console.log(rvData);
console.log(rvHost);

/** Plugin event Bus DO NOT REMOVE */
const pluginBus = useEventBus(eventBusKey); // This is the event bus for notifications about container updates based on your configuration

pluginBus.on((event) => {
  switch (event.type) {
    case "containerUpdated":
      console.log(event.info);
      // this bus notifies the app that there is new container data to be consumed.
      // Call the hydrateContainer Method for the container in rvData that matches the guid when desired by your application.
      break;

    default:
      break;
  }
});
</script>

HelloWorld.vue

<script setup>
import { ref } from 'vue'

defineProps({
  msg: String,
})

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button>
    <p>
      Edit
      <code>components/HelloWorld.vue</code> to test HMR
    </p>
  </div>

  <p>
    Check out
    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
      >create-vue</a
    >, the official Vue + Vite starter
  </p>
  <p>
    Install
    <a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
    in your IDE for a better DX
  </p>
  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>

<style scoped>
.read-the-docs {
  color: #888;
}
</style>

Unit Testing

​ Run pnpm test:unit to run unit tests. ​

Build

​ Run pnpm build to build a publishable app. Files will be written to the lib subdirectory (exposed by package.json).

package.json exposes the lib folder and uses microbundle without source maps:

  "version": "1.2.0",
  "description": "Vue plugin for CRI Vue3",
  "type": "module",
  "source": "./src/index.ts",
  "exports": {
    "types": "./lib/index.d.ts",
    "require": "./lib/index.cjs",
    "default": "./lib/index.modern.js"
  },
  "main": "./lib/index.cjs",
  "types": "./lib/index.d.ts",
  "module": "./lib/index.module.js",
  "unpkg": "./lib/index.umd.js",
  "files": [
    "lib"
  ],

Package Sidebar

Install

npm i @creative-realities/vue-rvhost-plugin

Weekly Downloads

89

Version

2.0.0

License

ISC

Unpacked Size

644 kB

Total Files

11

Last publish

Collaborators

  • leifworkmaster
  • adrian_cri
  • adamlockhart