This is a guide if you are migrating from Maestro to Vinyl.
For installation instructions and detailed API documentation, please refer to the VINYL_AMZN_USAGE.md guide.
@amzn/Orchestrajs -> @amzn/vinyl-amzn
API translations:
Maestro:
import { Player } from '@amzn/Orchestrajs'
new Player({
flags: {
metrics: false, // Metrics true is a dead branch
},
appName,
appVersion,
oAuthToken,
customerId,
deviceId,
deviceTypeId,
clientId,
hostname,
musicTerritory,
marketplaceId,
tier,
crossDomain,
withCredentials,
ipAddress,
cacheSize: 1, // 3P devices do not support cacheSize > 1. Prefetch tests are separate
})
A Maestro player will create an audio element per cached track.
Vinyl:
import { createVinylAmznPlayer, type Territory } from '@amzn/vinyl-amzn'
const media = new Audio()
media.controls = true
document.body.appendChild(media)
const player = createVinylAmznPlayer({ media })
player.configure({
dmls: {
marketplaceId: customerHome.marketplaceId,
musicTerritory: customerHome.musicTerritory as Territory, // Will be validated
appMetadata: {
appId: codeData.app_name!,
https: true,
appVersion: codeData.app_version,
},
deviceToken: {
deviceId: codeData.device_serial,
deviceTypeId: codeData.device_type,
},
clientMetadata: {
clientId: codeData.app_name!,
},
host: 'https://music.amazon.com',
customer: {
customerId: customerHome.customerId,
oAuthTokenProvider: () => {
/* return promise to oAuthToken */
},
entitlementList,
},
},
})
A Vinyl player is given the single media (audio or video) element. The media element does not need to be on the DOM unless native playback UI is used.
Maestro:
load preloads a track, but does not activate it. How many tracks you can
preload was dependent on the cacheSize configuration. cacheSize can only be
1 for big screen devices.
await player.load('asin://B0157E47K2')
await player.load('https://example.com/myTrack.mp3')
await player.load('coid://d9e4e337-97ba-41a9-9f31-84a8bf99f1dc')
Maestro player.load took optional extra options as a second parameter:
type LoadOptions = {
isDirectedPlay?: boolean
isExplicitLanguageFilterOn?: boolean
isShufflePlay: boolean
pageType?: any
selectionSourceType?: any
selectionSourceId?: any
selectionSourceSessionId?: any
bitrates?: any
position?: number
explicitId?: string
}
These had no effect for playback except when requesting metrics state.
Maestro had no way of preloading a track from a specific position.
To activate the track and begin playback in Maestro, player.play is used with
the same id for loading.
Vinyl:
preload preloads a track but does not activate it. load sets a play queue
and activates the first track. See
TrackController.preload
for more details.
Skyfire WebPlayer if using Vinyl should call preload on a BUFFER action and
load then play on a PLAY action.
Load commands for Vinyl take a load configuration. This configuration depends on the track type. Follow the type definition for VinylTrackLoadOptions to see what's supported. Generally this will have the shape:
type - The type of track. -uri - The track resource identifier.config - e.g. { startTime: 3 } track configuration which may be changed by
subsequent calls. startTime will indicate that the track should preload and
start from the given time (in seconds).player.preload({
type: 'asin',
uri: 'asin://B0157E47K2',
})
player.preload({
type: 'src',
uri: 'https://example.com/myTrack.mp3',
})
player.preload({
type: 'coid',
uri: 'coid://d9e4e337-97ba-41a9-9f31-84a8bf99f1dc',
config: { startTime: 30 },
})
player.load({
type: 'src',
uri: 'https://example.com/myTrack.mp3',
}) // Sets the queue to a single track.
Vinyl does have a queue, but it's optional. Calling load with several tracks
will play each track one after another and manage preloading the next tracks.
When migrating from Maestro, use preload for preloading the next tracks and
load with a single track to activate it. Calling load repeatedly replaces
the queue.
To begin playback, after calling load to activate the track, call play.
Vinyl has dependency configuration when creating the player, the ability to override dependencies, player runtime configuration, and global configuration. Initialization options are provided to Vinyl during createVinylAmznPlayer, global configuration via configureVinylAmznGlobal and runtime configuration via player.configure. The separated structures allows Vinyl to be more modular and give independent systems to define their own configuration.
Maestro has a flat runtime configuration via player.configure.
Vinyl configuration, aside from the DMLS config, can generally be left to their defaults.
Below is a list of Orchestra configuration options and how they would be changed in Vinyl, where applicable.
trackPreloadCount
is not used. preloadCapacity is automatically expanded if preload is
called with more tracks than the set capacity. Example:const player = createVinylAmznPlayer({
media,
trackController: { preloadCapacity: 4 },
})
secondsToPrefetch - number of seconds to prefetch when track is added to the
cache. In Vinyl, use the
segmentController initialization
configuration. secondsToPrefetch would be
dash.segmentController.prefetchInactive
secondsToPrefetchAhead - number of seconds requested when the track is
playing. Use dash.segmentController.prefetchActive.
maxPrecedingTime - Use dash.segmentController.retainTail
secondsToRetryManifest, secondsToRetryManifestJitter,
secondsToRetryManifest - Undocumented in Maestro. In Vinyl, network retries
are controlled in NETWORKING.md.
It's not recommended that this be modified without caution, but may be via a
global override:
requesterWithRetryRef.set(() => {
return createRequester({
retryOptions: RetryStrategy.NO_RETRIES,
})
})
A list of Maestro playback controls and the Vinyl counterpart:
play(...) - load() then play(), In Maestro, play activates the track
with the given id if it's not already active. In Vinyl this is done via the
load command. Vinyl play initiates playback of the current track and the
first attempt to play expected to be done in response to a user interaction.
Vinyl returns a Promise when calling play, which rejects if the play
requestWithRetry cannot be resolved, for example if interrupted by a pause
or if denied an autoplay attempt without user interaction.attemptAudioUnlock() - In Maestro this iterated through the cached elements
and initiated a play/pause interaction in order to 'unlock' all elements for
playback. In Vinyl, the first requestWithRetry to play should be done within
a user interaction. Promises returned from play() should be caught. (Use
noop to do nothing on rejection, e.g. play().catch(noop))pause() - pause() (identical, phew)Player.isSupported(type) (static) - capabilities.canPlayType(type)Player.isEMESupported(type) (static) -
capabilities.supportsKeySystem(keySystem, config?)remove(id) - (Remove a track from the cache) - unload() Unloads the active
track and clears the queue. clearTrackCache() does the former and clears the
cache. Individual tracks are generally not needed to be explicitly unloaded,
they will be unloaded automatically by least-recently-used status once the
cache size has been exceeded.resume() - Resumes playback - play()mute() - Mutes playback - volume = 0repeat() - Toggle repeat - loop = true/falseseekTo(position) - Change current time. Same except returns a Promise that
resolves when the seek completes.volume(value) - Change the volume. volume = valuesetConfig(config) - configure(config). See configuration section.isPlaying() - playingisPaused() - pausedisEnded() - endedisCached(trackId) - isTrackCached(uri)isStalled() - waitingisSeeking() - seekinggetCacheList() - getCachedTracks() (Use
Array.from(player.getCachedTracks()) to convert to an Array)isCurrentTrackId(id) - currentTrack?.uri === idgetCurrentTrackId() - currentTrack?.urigetCurrentTime - currentTimegetCurrentBitrate - playbackQualityAmzn?.bandwidthgetPlaybackQuality() - playbackQualityAmzngetCurrentQualities() - N/AgetBufferedTime() - fetchedTime, fetchedTimePercent -getDuration() - durationgetAudioElement() - N/A, retain a reference to the provided element.getNetworkMetrics(key) - getNetworkMetrics() (global)getBandwidthBPS() - getNetworkMetrics().estimatedDownlinkBandwidth.ewma
(global, weighted moving average in bits per second)getLatestBandwidthBPS() -
getNetworkMetrics().estimatedDownlinkBandwidth.latest (global)getVolume() - volumegetCurrentState() - N/AgetLatestError() - errorReporter.on('error', () => {})getPlaybackTimestamps() - Undocumented - N/AaddEventListener(type, handler) - on(type, handler)removeEventListener(type, handler) - const sub = on(type, handler); sub()decreaseBitrate, increaseBitrate - see ForcedQualitySelector in Uhd.test
to see an example of an overridden quality selector.destroy() - dispose()To observe Vinyl Events, use const sub = player.on(eventName, handler) Then
invoke the returned function to unsubscribe.
Full documentation for using events: EVENTS.md
For full documentation on the events Vinyl emits: VinylPlayerEventMap
A list of Maestro events and the Vinyl counterpart:
paused: pauseerror: errorReporter.on('error', (event) => { ... })ended: endedstarted: playingtimeupdate: timeupdatebuffertime: fetchedRangesChangebitratechange: playbackQualityChangecanplay: canplaystalled: waitingseeking: seekingresumed: playingexpired: N/AfragmentLatency: N/Aplaybackqualitychange: playbackQualityChangeMaestro did not emit events for metrics, instead it had state that was queried
with player.metricsReporter. VinylAmznPlayer instead emits events
corresponding to the
tier 1 business critical
MTS events. To observe these events, use player.metrics.mts.on(...) See the
docs for events emitted here:
MtsEventMap
These events are not emitted directly to Cirrus, the application is responsible
for taking the emitted event from Vinyl and reporting to the required services.
Vinyl metrics events have mtsAttributes which are the known MTS attributes
that correspond exactly to the lighthouse documentation, and support which
provides additional information needed to calculate final values. For example
Vinyl cannot calculate initialPlaybackDelay without knowing the timestamp
requested by the user to start playback. It therefore provides support
attributes describing the time playback started, which can then be used to
calculate the final value.