let channelId = '0UCmOoyY'; if (!channelId.match(/[a-zA-Z0-9]{8}/)) { alert("The provided channel id is not a valid Live Channel channel ID."); throw new Error('Please modify the channel ID'); }
const VOD_CONFIG = { playlist: 'https://cdn.jwplayer.com/v2/media/yjSRKuXP', // Repeat the VOD indefinitely while we wait for the livestream to become available. 'repeat': true };
const UPDATE_FREQUENCY = 10 * 1e3;
const MAX_RETRIES = 5;
const playerInstance = jwplayer('player').setup(VOD_CONFIG); /** An id used by the setInterval()/clearInterval() functions in order to manage the update loop. */ let intervalId;
/** Start the update loop. */ checkChannelStatus();
/** * Periodically checks whether the specified livestream channel is available, and if it is, configures the player * to start playing it. */ function checkChannelStatus() { console.log(`Checking channel Status: ID `, channelId); if (!intervalId) { // Make sure to execute this method every UPDATE_FREQUENCY milliseconds. intervalId = setInterval(checkChannelStatus, UPDATE_FREQUENCY); } getChannelStatus(channelId).then((channelStatus) => { console.log("Received channel status: %O", channelStatus); if (channelStatus['status'] === 'active') { // Determine the id of the active event based on the returned status. console.log(`Channel is Active`); console.log(channelStatus); const eventId = channelStatus['current_event']; if (eventId != null) { configurePlayer(eventId).catch((error) => { console.log("Failed to start livestream playback: ${error}"); }); clearInterval(intervalId); } else { console.log("Channel active, but event id not yet set"); } } }, (error) => { alert("Unable to fetch the channel status for ${channelId}: ${error}"); // If we fail to retrieve the channel status, then give up. clearInterval(intervalId); }); }
/** * (Re-)configures the active playerInstance to play the livestream identified by eventId. */ async function configurePlayer(eventId) { /** There may be a slight delay between the livestream becoming available, and its playlist to become available. * Therefore, we first attempt to fetch the playlist for the new live event, as soon as we have successfully fetched * a playlist, we will load it on the player and start playback of the livestream. */ console.log(`Fetching livestream playlist with event id`, eventId); let playlist; let attempts = 0; while (!playlist) { try { playlist = await getPlaylist(eventId); } catch (e) { ++attempts; console.error(e); if (attempts >= MAX_RETRIES) { throw e; } /** Retry with exponential backoff, i.e. first retry after 5, 10, 20, 40, 80 seconds * after which we ultimately give up. */ await sleep(2 ** (attempts - 1) * 5 * 1000); } }
// Once a playlist is available, use it to configure the player. playerInstance.setConfig({ repeat: false, });
playerInstance.load(playlist.playlist); // Start playback playerInstance.play(); console.log(`Playing livestream.`); }
/** * Utility function to fetch a JSON document. * @param url */ async function fetchJSON(url) { return await fetch(url) .then((response) => { if (!response.ok) { throw new Error(`Unable to fetch ${url}: ${response.statusText}`); } return response.json(); }); }
/** * Fetches the current status of a Live Channel. * Returns a promise that will yield the status for a particular channel. * * @param channelId The channel to fetch the status for. */ function getChannelStatus(channelId) { var url = `https://cdn.jwplayer.com/live/channels/${channelId}.json`; console.log(`Fetching `, url); return fetchJSON(url); }
/** * Fetches a media item. * * @param mediaId The media id to fetch a single item playlist for. */ function getPlaylist(mediaId) { return fetchJSON(`https://cdn.jwplayer.com/v2/media/${mediaId}`); }
/** * A simple utility method which can be used to wait for some time between retries. * * @param ms The amount of milliseconds to wait between retries. */ function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }