UNPKG

@speechkit/speechkit-audio-player

Version:

A web player component that can play audio from https://speechkit.io

285 lines (244 loc) 10.2 kB
// SPEECHKIT: GHOST CMS CUSTOM INTEGRATION SCRIPT // Version 1.0 (() => { //hashing function (SO:7616461) const cyrb53 = (str, seed = 0) => { let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; for (let i = 0, ch; i < str.length; i++) { ch = str.charCodeAt(i); h1 = Math.imul(h1 ^ ch, 2654435761); h2 = Math.imul(h2 ^ ch, 1597334677); } h1 = Math.imul(h1 ^ h1>>>16, 2246822507) ^ Math.imul(h2 ^ h2>>>13, 3266489909); h2 = Math.imul(h2 ^ h2>>>16, 2246822507) ^ Math.imul(h1 ^ h1>>>13, 3266489909); return 4294967296 * (2097151 & h2) + (h1>>>0); }; const getIframe = () => document.getElementById('speechkit-io-iframe'); const setIframeDisplay = display => { try { getIframe().style.display = display; } catch (e) {} }; const setIframeHeight = height => { try { getIframe().style.height = height; } catch (e) {} }; const getJavascriptSrcUrl = () => { //TODO: make better when know url const sScriptUrl = new RegExp('speechkit-ghost\.js'), aScripts = document.getElementsByTagName('script'); for (let i = 0; i < aScripts.length; i++) { let sSrc = aScripts[i].getAttribute('src'); //Confirm src is for spkt javascript and return src if (sSrc !== null && sSrc.search(sScriptUrl) !== -1) { return sSrc; } } return null; }; const domReady = initSpeechKit => { document.readyState === 'interactive' || document.readyState === 'complete' ? initSpeechKit() : document.addEventListener('DOMContentLoaded', initSpeechKit); }; const newApiRequest = (url, sPostData) => { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.onerror = reject; if (sPostData) { xhr.open('POST', url, true); xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); xhr.send(sPostData); } else{ xhr.open('GET', url, true); xhr.send(); } xhr.onreadystatechange = () => { //everytime ready state changes (0-4), check it if (xhr.readyState === 4) { //if request finished & response ready (4) if (xhr.status === 0 || xhr.status === 200) { //then if status OK (local file || server) const data = JSON.parse(xhr.responseText); //parse the returned JSON string resolve(data); } else { reject(); } } }; }); }; const createNewAudio = (oArticleContent, sSpktAPIKey, sProjectID, skBackend) => { if(!sSpktAPIKey) { console.log(`${String.fromCodePoint(0x1F9D0)} Speechkit Error: Speechkit API Key not found.`); return; } const url = `${skBackend}/api/v3/projects/${sProjectID}/audio?api_key=${sSpktAPIKey}`; const sPostData = JSON.stringify(oArticleContent); return newApiRequest(url, sPostData); }; // Use Speechkit API to check if audio exists for certain sProjectId for this sPageUrl const checkIfAudioExists = (sPageUrl, sProjectID, skBackend) => { const sAPIBaseUrl = skBackend + '/api/v2/projects/'; const sRequestUrl = sAPIBaseUrl + sProjectID + '/podcasts/search?url=' + encodeURIComponent(sPageUrl); // https://staging-app.speechkit.io/api/v2/projects/2824/podcasts/search?url=http://165.22.123.203/ return newApiRequest(sRequestUrl); }; // init function - Main functionality, run on DOM ready const initSpeechKit = () => { // first do page check to make sure we have an element to insert player into const eArticleElement = getArticleElement(); if(!eArticleElement) { console.log(`${String.fromCodePoint(0x1F9D0)} SpeechKit Error: If you are expecting a player to appear on this page, please add the class "speechkit-container" to an element within your template. \n The SpeechKit player will then be inserted into this element.`); return; } // get parameters from src const spktJsSource = getJavascriptSrcUrl(), srcQueryString = spktJsSource.substring(spktJsSource.lastIndexOf('?')), urlParams = new URLSearchParams(srcQueryString), encodedData = urlParams.get('data'), env = urlParams.get('env') || 'prod', // get single param - data= btoa() oJsonData = atob(encodedData), oData = JSON.parse(oJsonData), project_id = oData.projectId, spkt_api_key = oData.sk, ghost_content_key = oData.gk, skBackend = (env === 'prod') ? 'https://app.speechkit.io' : 'https://staging-app.speechkit.io'; if(!project_id) { console.log(`${String.fromCodePoint(0x1F9D0)} Spkt Error: Project ID not found.`); return; } const sPageUrl = window.location.href; // check content exists in API: checkIfAudioExists(sPageUrl, project_id, skBackend) .then((result) => { // console.log('Audio Found, Loading Player', result) //if content exists, load player: createPlayer(project_id, eArticleElement, env); }).catch((e) => { //if no audio exists in Speechkit, get content from ghost and create audio getGhostContent(ghost_content_key) .then((oGhostContent) => { const oArticle = oGhostContent.posts[0]; // Make sure we have html in string format for the hash const sArticleText = String(oArticle.html); //.replace(/<[^>]+>/gm, ''); const sUniqueHash = cyrb53(sArticleText+oArticle.title+oArticle.url); const oArticleContent = { 'body': sArticleText, 'title': oArticle.title, 'article_url': oArticle.url, 'external_id': sUniqueHash }; // if content does not exist, create: createNewAudio(oArticleContent, spkt_api_key, project_id, skBackend) .then((result) => { //if content exists, load player: console.log('Speechkit: Audio submitted to API', result); }).catch((e) => { console.log(`${String.fromCodePoint(0x1F9D0)} Speechkit Error: API error creating audio ${e}`); }); }) .catch((e) => { console.log(`${String.fromCodePoint(0x1F9D0)} Speechkit Error: Cannot get page content via Ghost API ${e}`); // An error occurred }); }); }; // get specific element on the page ready to insert iframe alongside // TODO: (may update to pass in class here) const getArticleElement = () => { // First look for the speechkit custom container, if present return it const sElementClass = 'speechkit-container'; const eSpeechkit = document.getElementsByClassName(sElementClass)[0]; if(eSpeechkit) return eSpeechkit; // If they have not added a custom container, we need to look for the post-template class next // This is to make sure we do not try and insert a player on the index page for example const bIsAPost = document.body.classList.contains('post-template'); // this is not a Ghost article post, so return null to exit if(!bIsAPost) return null; // next look for div with class post-full-content as second choice const sContentClass = 'post-full-content'; const eContent = document.getElementsByClassName(sContentClass)[0]; if(eContent) return eContent; // if not present then simply look for the article header and return that: const eArticle = document.getElementsByTagName('Article')[0]; if(eArticle) { const eHeaderTag = eArticle.getElementsByTagName('Header')[0]; if(eHeaderTag) return eHeaderTag; } // next look for div with class post-full-content as second choice const eContentDiv = document.getElementsByClassName('content')[0]; if(eContentDiv) return eContentDiv; //otherwise exit as we cannot find suitable element to add player return null; }; const updateAttributes = data => { const iframe = getIframe(); Object.keys(data.attrs).forEach(attr => { if (iframe && data.attrs[attr]) { iframe.setAttribute(attr, data.attrs[attr]); } }); }; // Use ghost content API to fetch html content for current page const getGhostContent = sContentAPIKey => { if(!sContentAPIKey) { console.log(`${String.fromCodePoint(0x1F9D0)}, 'Speechkit Error: Ghost Content API Key not found.`); } const sGhostUrl = window.location.origin; const sPageUrl = window.location.pathname.replace(/\//g,''); //slug, eg: /article-about-this/ const sGhostPath = `${sGhostUrl}/ghost/api/v2/content/posts/slug/${sPageUrl}/?key=${sContentAPIKey}`; return newApiRequest(sGhostPath); }; // create player iframe below eArticleElement, with audio for sProjectID const createPlayer = (sProjectID, eArticleElement, env) => { const sDomain = (env === 'prod') ? 'https://spkt.io' : 'https://staging.spkt.io'; const src = `${sDomain}/r/${sProjectID}`; const eIframe = document.createElement('iframe'); eIframe.style.display = 'none'; eIframe.src = src; eIframe.id = 'speechkit-io-iframe'; eIframe.allowfullscreen = 'false'; eIframe.frameborder = '0'; eIframe.scrolling = 'no'; // iframe.style = 'display: none'; if(eArticleElement.tagName === 'HEADER') { eArticleElement.insertAdjacentElement('afterend', eIframe); } else { eArticleElement.prepend(eIframe); } }; window.addEventListener( 'message', e => { if (process.env.ORIGINS.indexOf(e.origin) === -1) return; const key = e.message ? 'message' : 'data'; const data = e[key]; // return if no data present in PM if(!data) return; if (data.attrs) { if (data.msg && data.msg === 'iframe-resize') { setIframeHeight(data.attrs.height) } //append margin-bottom custom styling if(data.attrs.style) { data.attrs.style += 'margin-bottom:15px!important;' } updateAttributes(data); } if (data === 'sk-fail') { setIframeDisplay('none'); } else if (data === 'sk-success') { setIframeDisplay('block'); } }, false ); // run init script on domReady event domReady(initSpeechKit); })();