HTML Packages
Doohly supports the use of dynamic HTML Creatives. This opens up the possibilities of what your creatives can do. We have made using HTML Creatives as simple as possible.
Minimum player version: v4.2.3 [android] [windows] [linux]
HTML Template package: See section below
Basic Requirements
HTML Creatives can be uploaded into Doohly by using a zip file. There are a few basic requirements of the contents that make up a HTML Creative.
- It needs to contain a single index.html file in the root of the zip file
- The index.html file must contain a
<head>
section - It cannot contain another zip file
These requirements will get checked when you try to upload the zip file into the Doohly Creative Library. If there is an issue, an error will appear, otherwise, it will be uploaded and ready for use!
Assets
Assets refers to CSS, JS, images, videos, fonts & other files. There are two ways of using assets in HTML Creatives: local & remote.
- Local assets are those that get bundled into the zip file along with index.html.
- Remote assets are those that are requested by index.html but are not bundled into the zip file
Note: It is always preferable to make use of local assets as these will be cached on the player which allows for playback even without an internet connection.
- Video: MP4 (h.264)
- Image: JPG, JPEG, PNG, GIF
Note: WebM files that are used in HTML packages will be streamed instead of preloaded. This will cause higher CPU & disk usage and likely result in crashing. For the best performance, be sure to use MP4 video files.
<head>
Section
<head>
SectionWhen creating a HTML package, it is a requirement to include a <head>
section within the index.html file. This section serves to hold meta-data about your package, but also should include a <script>
section with the package's main() function.
Creative API
There are some cases where you will want to trigger actions in your HTML Creative during its lifecycle. For example, starting a video or animation when the creative is first displayed on screen. Making use of the Creative API will allow you to do this & more.
Setup
First you will need to define a main() function within the of your HTML. This is the entry point and will be automatically called when the creative is being first prepared on the player.
<script>
async function main() {
const playerId = doohly.player.id
try {
const response = await fetch(`https://example.com/${playerId}/data.json`)
const data = await response.json()
return data
} catch (error) {
console.error('error requesting data', error)
}
}
</script>
Alternatively you can make use of the window.getDoohly()
function which will return a Promise
that can be awaited and will resolve to the doohly
object once it's available which is the same time main()
is called.
const doohly = await window.getDoohly()
const playerId = doohly.player.id
try {
const response = await fetch(`https://example.com/${playerId}/data.json`)
const data = await response.json()
return data
} catch (error) {
console.error('error requesting data', error)
}
getDoohly()
can be called at any point in and will return thedoohly
object. Take note to alwaysawait
the call as it will always return aPromise<doohly>
Usage
Once the main() is called you can get access to the Creative API via the doohly
global object. Currently, you are able to:
After main()
is called is when you should subscribe to events related to the creative lifecycle.
-
play: emitted when the creative starts playing and is displayed on the screen. Before this event the package is prepared in the background to allow for loading external resources, caching, etc. The duration before this event is emitted is based on the duration of the creative playing before this creatives slot.
-
pause: emitted when the creative is interrupted. e.g. Someone has interacted with the screen with MappedIn enabled for.
-
resume: emitted when a paused creative is resumed. e.g. The player takes focus on screen after someone has interacted with the screen with MappedIn enabled.
<script> async function main() { doohly.player.on('play', () => { // start any animations / video ... }) doohly.player.on('pause', () => { // stop any animations / video ... }) doohly.player.on('resume', () => { // start any animations / video ... }) // You can access package information here as well. Examples below: // doohly.player.name // doohly.frame.name // doohly.campaign.name // doohly.booking.name // doohly.creative.name } </script>
Note: The
doohly
object will not exist before main() is called so all code requiring it should start from within main()
Creative Package Control
Within a package it is possible to tell the player that it has finished playing or it should be skipped due to some reason.
doohly.player.end()
Calling this function will tell the player that the package has finished playing it's content and it should move onto the next slot. This is useful for cases where the package it scheduled for a slot length of 30 seconds but the content of the package is shorter e.g. 15 seconds
const videoElement = document.createElement('video')
videoElement.addEventListener('ended', () => {
// Video has finished playing so we tell the player we're finished and it can move on
doohly.player.end()
})
videoElement.addEventListener('error', (error) => {
// Encountered an error trying to play the video so we skip so it doesn't get stuck on screen
doohly.player.skip(`errored playing video: ${myVIdeoUrl}`)
})
doohly.player.skip()
Calling this function will tell the player that the package has decided it should not or can not play and should skip it's slot and move onto the next slot. If a package is partway through playing on the screen & this function is called it will stop playing and will be tracked as skipped
try {
const response = await fetch('https://exmaple.com/data.json')
const data = await response.json()
return data
} catch (error) {
// Package cannot function without a successful response
// Should choose to skip this slot so it doesn't show on screen
doohly.player.skip('failed to request data')
return
}
Creative Package Information
There is a range of information that you can make use of within a package. This information can be found on the doohly window object. The campaign & booking information is based on which booking the package was added to.
Below is what information is available within a package:
window.doohly = {
player: {
id: string
name: string
version: string // Current version (X.X.X)
location: {
lat: number // Set via Web App
long: number // Set via Web App
address: string | null
city: string | null // "Melbourne"
state: string | null // "Victoria"
postcode: string | null // "3000"
country: string | null // "Australia"
}
tags: { name: string }[]
}
frame: {
id: string
name: string
}
campaign: {
id: string
name: string
externalId: string | null
tags: { name: string }[]
} | null
booking: {
id: string
name: string
externalId: string | null
tags: { name: string }[]
}
creative: {
id: string
name: string
durationMs: number // Duration of current slot
}
}
Usage Examples
Weather Data Lookup
You can make use of the players location, set via the web app, to look up weather information based on the player and then display the information on the screen. This example shows how you could use the postcode of the player to find local weather information.
<script>
async function main() {
const postcode = doohly.player.location.postcode
const url = `https://some-weather-lookup-service.com?postcode=${postcode}`
const data = await fetch(url)
// Display retruned data
}
</script>
Using Tags As Dynamic Content
Any tags added to a player, campaign or booking are made available in packages and can be used to control or display different content based on those tags. This allows one package to display different content based on what player it is playing on or what campaign/booking it is part of.
This example searches the player tags for one starting with building-name/, an example being building-name/Main Office, and if it's found the name is then displayed in the creative otherwise it uses a default placeholder title. This example could also be done using campaign or booking tags.
<script>
async function main() {
const tagPrefix = 'building-name/'
const buildingNameTag = doohly.player.tags.find((tag) => tag.name.startsWith(tagPrefix))
const buildingNameElement = document.getElementById('building-name')
const name = buildingNameTag ? buildingNameTag.name.replace(tagPrefix, '') : 'Placeholder Building Name'
buildingNameElement.innerText = name
}
</script>
Caching
There are many cases where you want to cache assets (images/videos) or data (json) in order to minimise network usage by packages. For Doohly player packages it is recommended to utilise IndexDB for caching this data as it has advantages like being able to store images/videos & the ability to share cached assets/data across different packages & package versions
Within the provided template package you'll find storage_cache.js
which implements a simple interface for caching on Doohly players. You can download the template and include storage_cache.js
into your own packages to immediately get started with caching.
StorageCache
The StorageCache
class is a simple interface that can be used to store data and assets (images, videos) for use in packages. It uses localforage under the hood to interact with IndexDB to cache with automatic handling for cache expiry built in.
Requirements
Import storage_cache.js
script in your HTML file before using the StorageCache
class. (available in template package)
<script src="./js/storage_cache.js"></script>
Example usage
Setup
const cache = new StorageCache({
name: 'package-author-company-name.example-package',
maxCacheAgeMs: 1000 * 60 * 60 * 24 * 7, // 7 Days
autoClearStaleCache: true,
defaultCache: {
lastRequestTs: null,
data: null,
},
})
Basic Caching Usage
This is a basic example flow of how to cache data and assets in a package.
const myDataId = 'example-123'
const myDataUrl = `https://example.com/${myDataId}/data.json`
try {
const response = await fetch(myDataUrl)
const data = await response.json()
await cache.setCacheData(myDataId, { data, lastRequestTs: Date.now() })
} catch (error) {
console.error('Failed to fetch data', error)
}
const latest = await cache.getCacheData(myDataId)
if (!latest.data) {
console.error('Data is missing')
doohly.player.skip()
return
}
// Asset URL that is returned in request data and needs to be cached
const imageUrl = cachedData.data.imageUrl
const videoUrl = cachedData.data.videoUrl
// Will download and store image in cache if missing
const cachedImageUrl = await cachedData.getCacheAsset(imageUrl)
<img src={cachedImageUrl} />
// Will download and store video in cache if missing
const cachedVideoUrl = await cachedData.getCacheAsset(videoUrl)
<video src={cachedVideoUrl} />
Request throttling Example
This example shows how to throttle requests to a data endpoint to minimise network requests and network data usage. This can be useful when the data does not change frequently and can be updated less often.
const myDataId = doohly.player.id
const myDataUrl = `https://example.com/${myDataId}/data.json`
const minimumTimeBetweenDataRequestsMs = 1000 * 60 * 5 // 5 minutes
// If first time will return defaultCache specified in setup
const cachedData = await cache.getCacheData(myDataId)
const missingCacheData = !cachedData.data
const longEnoughSinceLastDataRequest = cachedData.lastRequestTs < Date.now() - minimumTimeBetweenDataRequestsMs
// Check if data is missing or it has been long enough since last request
if (missingCacheData || longEnoughSinceLastDataRequest) {
try {
const response = await fetch(myDataUrl)
const data = await response.json()
await cachedData.setCacheData(myDataId, { data, lastRequestTs: Date.now() })
} catch (error) {
console.error('Failed to fetch data', error)
}
}
const latest = await cache.getCacheData(myDataId)
if (!latest.data) {
console.error('Data is missing')
doohly.player.skip()
return
}
// Continue with data
Deleting & clearing Cache
// Delete any stale cache
// Can be called manually or automatically when storage is created
// Cache is cleared based on when cached item was last used (get/set)
await cache.clearStaleCache()
// Delete specific cache item
await cache.deleteCacheData(myDataId)
await cache.deleteCacheAsset(imageUrl)
await cache.deleteCacheAsset(videoUrl)
Error Handling
The Doohly Player will attempt to prepare the HTML Creative just before it’s going to be played, but if the HTML Creative fails during the preparation phase, it will be skipped automatically and proceed to play the remainder of the loop.
Template Package
We have also prepared a template that can be used as the starting point for a new HTML Creative.
You can download it here.
Updated 16 days ago