Skip to content

Commit

Permalink
Update dom parser for audio files
Browse files Browse the repository at this point in the history
  • Loading branch information
nikkuAg committed Aug 22, 2024
1 parent 053afe3 commit c6a9e12
Show file tree
Hide file tree
Showing 12 changed files with 679 additions and 1,112 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ js-dist
!kolibri/**/static/assets/
!kolibri/**/static/assets/*
!kolibri/**/static/assets/**/*
!kolibri/**/static/bloom/

# swap files
*.swp
Expand Down

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions kolibri/core/content/static/bloom/bloomplayer.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!-- For this file to work it must have a param named "url" which points the folder containing the bloom book. For example,
<iframe src="\bloomplayer.htm?url='https:\\example.com\mybook"></iframe> -->

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body style="background-color: #2e2e2e;">
<div id="root"><span style="color: #d65649">Loading Bloom Player...</span></div>
<script type="text/javascript" src="bloomPlayer-469de4b6cca7dc5c3e04.min.js"></script></body>
<!-- At build time, we insert a script tag pointing at bloomPlayer.min.js but with a hash created at build time,
to avoid getting a stale version from a cache (while allowing us to cache it for a long time).
It has a style sheet which sets the same color on the body, but it takes a while to fetch and load,
so we reduce flicker by setting a background color explicitly. -->
</html>
Binary file not shown.
Binary file not shown.
4 changes: 1 addition & 3 deletions packages/hashi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"build": "yarn run build-base --mode=production",
"dev": "yarn run build-base --mode=development --watch",
"compat": "eslint -c ./compat.js ./src/*.js",
"build-h5p": "node ./downloadH5PVendor.js && webpack --config ./webpack.config.h5p.js --mode=production",
"build-bloom": "npm install bloom-player && webpack --config ./webpack.config.bloom.js --mode=production"
"build-h5p": "node ./downloadH5PVendor.js && webpack --config ./webpack.config.h5p.js --mode=production"
},
"author": "Learning Equality",
"license": "MIT",
Expand All @@ -22,7 +21,6 @@
"purgecss": "^6.0.0"
},
"dependencies": {
"bloom-player": "^2.3.2",
"core-js": "3.37",
"dayjs": "^1.11.12",
"iri": "^1.3.1",
Expand Down
71 changes: 27 additions & 44 deletions packages/hashi/src/Bloom/BloomRunner.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ZipFile from 'kolibri-zip';
import { strToU8 } from 'fflate';
import { defaultFilePathMappers } from 'kolibri-zip/src/fileUtils';
import { defaultFilePathMappers, getAudioId, replaceAudioId } from 'kolibri-zip/src/fileUtils';
import { events } from '../hashiBase';

const CONTENT_ID = '1234567890';
Expand All @@ -19,7 +19,6 @@ export default class BloomRunner {
this.shim = shim;
this.data = shim.data;
this.events = events;
this.scriptLoader = this.scriptLoader.bind(this);
}

init(iframe, filepath, loaded, errored) {
Expand Down Expand Up @@ -58,59 +57,43 @@ export default class BloomRunner {
* Start Bloom player in the contentWindow.
*/
initBloom() {
return this.scriptLoader('../bloom/storageEvent.js').then(() => {
try {
this.loaded();
this.iframe.contentWindow.postMessage(
{
event: this.events.BLOOMDATAREQUESTED,
data: [this.contentUrl, this.distributionUrl, this.metaUrl],
},
'*',
);
this.iframe.src = `../bloom/bloomplayer.htm?url=${this.contentUrl}`;
} catch (e) {
this.errored(e);
}
});
}

scriptLoader(url, css = false) {
const iframeDocument = this.iframe.contentWindow.document;
return new Promise((resolve, reject) => {
let script;
if (!css) {
script = iframeDocument.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.async = true;
script.addEventListener('load', () => resolve(script));
script.addEventListener('error', reject);
} else {
script = iframeDocument.createElement('link');
script.rel = 'stylesheet';
script.type = 'text/css';
script.href = url;
// Can't detect loading for css, so just assume it worked.
resolve(script);
}
iframeDocument.body.appendChild(script);
});
try {
this.loaded();
this.iframe.src = `../bloom/bloomplayer.htm?url=${this.contentUrl}&distributionUrl=${this.distributionUrl}&metaJsonUrl=${this.metaUrl}`;
} catch (e) {
this.errored(e);
}
}

processContent() {
const mapper = new defaultFilePathMappers.bloom(this.contentfile);
const files = mapper.getPaths().filter(file => !file.startsWith('blob:'));
if (files.length > 0) {
const replacementFileMap = {};
const audioIds = getAudioId(this.contentfile.toString(), this.contentfile.mimeType);
const replacementFileMap = {};
if (files.length > 0 || audioIds.length > 0) {
for (const file of this.packageFiles) {
if (files.includes(file.name)) {
replacementFileMap[file.name] = file.toUrl();
}
if (files.includes(encodeURI(file.name))) {
replacementFileMap[encodeURI(file.name)] = file.toUrl();
}
const audioFile = file.name.substring(6);
const audioFileName = audioFile.split('.')[0];
const url = file.toUrl();
if (audioIds.includes(audioFileName)) {
replacementFileMap[audioFileName] = `_${url.split('/').at(-1)}`;
}
if (audioIds.includes(audioFile)) {
replacementFileMap[audioFile] = `_${url.split('/').at(-1)}`;
}
}
this.contentfile.obj = strToU8(mapper.replacePaths(replacementFileMap));
this.contentUrl = this.contentfile.toUrl();
}
let newHtmlFile = mapper.replacePaths(replacementFileMap);
newHtmlFile = replaceAudioId(newHtmlFile, replacementFileMap, this.contentfile.mimeType);

this.contentfile.obj = strToU8(newHtmlFile);
this.contentUrl = this.contentfile.toUrl();
}

/*
Expand Down
27 changes: 0 additions & 27 deletions packages/hashi/src/Bloom/requestInterceptor.js

This file was deleted.

7 changes: 0 additions & 7 deletions packages/hashi/src/Bloom/storageEvent.js

This file was deleted.

56 changes: 0 additions & 56 deletions packages/hashi/webpack.config.bloom.js

This file was deleted.

65 changes: 51 additions & 14 deletions packages/kolibri-zip/src/fileUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ const attributes = ['src', 'href'];

const attributesSelector = attributes.map(attr => `[${attr}]`).join(', ');

const styleAttributeUrlSelector = '[style*="url("]';
const urlStyleAttributeSelector = '[style*="url("]';

const queryParamRegex = /([^?)]+)?(\?.*)/g;
const audioClassAttributeSelector = '[class*="audio-sentence"], [data-backgroundaudio]';

const styleUrlRegex = /url\(["']?([^"')]+)["']?\)/;
const queryParamRegex = /([^?)]+)?(\?.*)/g;

export function getDOMPaths(fileContents, mimeType) {
const dom = domParser.parseFromString(fileContents.trim(), mimeType);
Expand All @@ -91,13 +91,14 @@ export function getDOMPaths(fileContents, mimeType) {
);
}

export function getStylePaths(fileContents, mimeType) {
export function getStyleUrlPaths(fileContents, mimeType) {
const dom = domParser.parseFromString(fileContents.trim(), mimeType);
const elements = dom.querySelectorAll(styleAttributeUrlSelector);
const elements = dom.querySelectorAll(urlStyleAttributeSelector);
return flatten(
Array.from(elements).map(element => {
const styleAttr = element.getAttribute('style');
return styleAttr.match(cssPathRegex).map(path => path.match(styleUrlRegex)[1]);
const styleUrl = styleAttr.split('url(').at(-1);
return styleUrl.substring(1, styleUrl.length - 2);
}),
);
}
Expand Down Expand Up @@ -127,18 +128,18 @@ export function replaceDOMPaths(fileContents, packageFiles, mimeType) {
return domSerializer.serializeToString(dom);
}

export function replaceStylePaths(fileContents, packageFiles, mimeType) {
export function replaceStyleUrlPaths(fileContents, packageFiles, mimeType) {
const dom = domParser.parseFromString(fileContents.trim(), mimeType);
const elements = Array.from(dom.querySelectorAll(styleAttributeUrlSelector));
const elements = Array.from(dom.querySelectorAll(urlStyleAttributeSelector));
for (const element of elements) {
let styleAttr = element.getAttribute('style');
if (!styleAttr) {
continue;
}
styleAttr = styleAttr.replace(cssPathRegex, match => {
const newUrl = packageFiles[match.replace(styleUrlRegex, '$1')];
return newUrl ? `url(${newUrl})` : match;
});
styleAttr = styleAttr.split('url(');
const oldUrl = styleAttr[1];
const newUrl = packageFiles[oldUrl.substring(1, oldUrl.length - 2)];
styleAttr = styleAttr[0] + "url('" + newUrl + "')";

element.setAttribute('style', styleAttr);
}
Expand All @@ -151,18 +152,54 @@ export function replaceStylePaths(fileContents, packageFiles, mimeType) {
return domSerializer.serializeToString(dom);
}

export function getAudioId(fileContents, mimeType) {
const dom = domParser.parseFromString(fileContents.trim(), mimeType);
const elements = dom.querySelectorAll(audioClassAttributeSelector);
return Array.from(elements).map(element => {
let value = element.getAttribute('id');
const backgroundAudio = element.getAttribute('data-backgroundAudio');
if (backgroundAudio) {
value = backgroundAudio;
}
return value;
});
}

export function replaceAudioId(fileContents, packageFiles, mimeType) {
const dom = domParser.parseFromString(fileContents.trim(), mimeType);
const elements = Array.from(dom.querySelectorAll(audioClassAttributeSelector));
for (const element of elements) {
const backgroundAudio = element.getAttribute('data-backgroundAudio');
let id = element.getAttribute('id');
id = packageFiles[id];
if (backgroundAudio) {
id = packageFiles[backgroundAudio];
element.setAttribute('data-backgroundAudio', id);
} else {
element.setAttribute('id', id);
}
}
if (mimeType === 'text/html') {
// Remove the namespace attribute from the root element
// as serializeToString adds it by default and without this
// it gets repeated.
dom.documentElement.removeAttribute('xmlns');
}
return domSerializer.serializeToString(dom);
}

class BloomMapper extends Mapper {
getPaths() {
return [
...new Set([
...getDOMPaths(this.file.toString(), this.file.mimeType),
...getStylePaths(this.file.toString(), this.file.mimeType),
...getStyleUrlPaths(this.file.toString(), this.file.mimeType),
]),
];
}

replacePaths(packageFiles) {
return replaceStylePaths(
return replaceStyleUrlPaths(
replaceDOMPaths(this.file.toString(), packageFiles, this.file.mimeType),
packageFiles,
this.file.mimeType,
Expand Down
Loading

0 comments on commit c6a9e12

Please sign in to comment.