Skip to content

Commit

Permalink
Libzim support for reading ZIM file content (#1160)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rishabhg71 authored Jan 15, 2024
1 parent f445148 commit 11138ff
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 6 deletions.
4 changes: 4 additions & 0 deletions i18n/en.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions i18n/es.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions i18n/fr.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,23 @@ <h3 data-i18n="configure-expert-settings-title">Expert settings</h3>
<span data-i18n="configure-expert-bypassappcache"><b>Bypass AppCache</b> (<i>disables offline use of PWA!</i>)</span>
</label>
</div>
<div class="checkbox" id="">
<label data-i18n-tip="configure-expert-useLibzim-tip" title="Uses the selected version of libzim to access the ZIM contents (ServiceWorker mode only).">
<input type="checkbox" name="useLibzim" id="useLibzim">
<span><strong data-i18n="configure-expert-useLibzim">Use libzim for loading zim</strong> (<i data-i18n="configure-expert-useLibzim-warning">uses unstable libzim API for reading ZIM</i>)</span>
</label>
</div>
<div class="checkbox" id="libzimMode" style="display: none;">
<label>
<span><strong data-i18n="configure-expert-libzimMode">Select libzim version to use:</strong></span>
<select id="libzimModeSelect">
<option value="wasm">WASM</option>
<option value="asm">ASM</option>
<option value="wasm.dev">WASM Debug</option>
<option value="asm.dev">ASM Debug</option>
</select>
</label>
</div>
<p data-i18n="configure-expert-resetapp-description" class="pt-2">Reset the app to default settings and erase all caches:</p>
<div class="button">
<label data-i18n-tip="configure-expert-resetapp-tip" title="This will return the app to its original settings on launch, and will empty all caches and settings and deregister Service Workers. The app will then reload.">
Expand Down
62 changes: 59 additions & 3 deletions www/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const ASSETS_CACHE = 'kiwixjs-assetsCache';
var appstate = {};

/**
* @type ZIMArchive
* @type ZIMArchive | null
*/
var selectedArchive = null;

Expand Down Expand Up @@ -474,6 +474,18 @@ document.getElementById('bypassAppCacheCheck').addEventListener('change', functi
// This will also send any new values to Service Worker
refreshCacheStatus();
});

if (params.useLibzim) document.getElementById('libzimMode').style.display = '';
document.getElementById('libzimModeSelect').addEventListener('change', function (e) {
settingsStore.setItem('libzimMode', e.target.value);
window.location.reload();
});

document.getElementById('useLibzim').addEventListener('click', function (e) {
settingsStore.setItem('useLibzim', !params.useLibzim);
window.location.reload();
});

document.getElementById('disableDragAndDropCheck').addEventListener('change', function () {
params.disableDragAndDrop = !!this.checked;
settingsStore.setItem('disableDragAndDrop', params.disableDragAndDrop, Infinity);
Expand Down Expand Up @@ -841,7 +853,8 @@ function initServiceWorkerMessaging () {
// Until we find a way to tell where it is coming from, we allow the request through on all controlled clients and try to load the content
console.warn('>>> Allowing passthrough of SW request to process Zimit video <<<');
}
handleMessageChannelMessage(event);
if (params.useLibzim) handleMessageChannelByLibzim(event);
else handleMessageChannelMessage(event);
}
} else if (event.data.msg_type) {
// Messages received from the ReplayWorker
Expand Down Expand Up @@ -886,6 +899,49 @@ function initServiceWorkerMessaging () {
}
}

/**
* Function that handles a message of the messageChannel.
* This function will deal with the messages if useLibzim is set to true
*
* @param {Event} event The event object of the message channel
*/
async function handleMessageChannelByLibzim (event) {
// We received a message from the ServiceWorker
// The ServiceWorker asks for some content
const title = event.data.title;
const messagePort = event.ports[0];
try {
const ret = await selectedArchive.callLibzimWorker({ action: 'getEntryByPath', path: title })
if (ret === null) {
console.error('Title ' + title + ' not found in archive.');
messagePort.postMessage({ action: 'giveContent', title: title, content: '' });
return;
}

if (ret.mimetype === 'unknown') {
// We have a redirect to follow
// this is still a bit flawed, as we do not check if it's a redirect or the file doesn't exist
// We have no way to know if the file exists or not, so we have to assume it does and its just a redirect

const dirEntry = await new Promise((resolve, _reject) => selectedArchive.getMainPageDirEntry((value) => resolve(value)));
if (dirEntry.redirect) {
const redirect = await new Promise((resolve, _reject) => selectedArchive.resolveRedirect(dirEntry, (v) => resolve(v)));
const ret = await selectedArchive.callLibzimWorker({ action: 'getEntryByPath', path: redirect.namespace + '/' + redirect.url })
const message = { action: 'giveContent', title: title, content: ret.content, mimetype: ret.mimetype };
messagePort.postMessage(message);
return;
}
}

// Let's send the content to the ServiceWorker
const message = { action: 'giveContent', title: title, content: ret.content, mimetype: ret.mimetype };
messagePort.postMessage(message);
} catch (error) {
const message = { action: 'giveContent', title: title, content: new Uint8Array(), mimetype: '' };
messagePort.postMessage(message);
}
}

/**
* Sets the given injection mode.
* This involves registering (or re-enabling) the Service Worker if necessary
Expand Down Expand Up @@ -2475,7 +2531,7 @@ function displayArticleContentInIframe (dirEntry, htmlArticle) {
// Get the url and the resolution from the srcset entry
var urlMatch = imgAndResolutionUrl.match(/^\s*([^\s]+)\s+([0-9.]+\w+)\s*$/);
var url = urlMatch ? urlMatch[1] : '';
var resolution = urlMatch ? urlMatch[2]: '';
var resolution = urlMatch ? urlMatch[2] : '';
selectedArchive.getDirEntryByPath(url).then(function (srcEntry) {
selectedArchive.readBinaryFile(srcEntry, function (fileDirEntry, content) {
var mimetype = srcEntry.getMimetype();
Expand Down
6 changes: 6 additions & 0 deletions www/js/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
* @property {string} cacheIDB - Name of the Indexed DB database
* @property {boolean} isFileSystemApiSupported - A boolean indicating whether the FileSystem API is supported.
* @property {boolean} isWebkitDirApiSupported - A boolean indicating whether the Webkit Directory API is supported.
* @property {boolean} useLibzim - A boolean indicating weather to use the libzim to load zim files.
* @property {"wasm-dev" | 'wasm' | 'asm' | 'asm-dev' | 'default'} libzimMode - A value indicating which libzim mode is selected.
* @property {DecompressorAPI} decompressorAPI
/**
Expand Down Expand Up @@ -123,6 +125,8 @@ params['cacheAPI'] = 'kiwix-js'; // Sets name of the prefix used to identify the
params['cacheIDB'] = 'kiwix-zim'; // Sets name of the Indexed DB database
params['isFileSystemApiSupported'] = typeof window.showOpenFilePicker === 'function'; // Sets a boolean indicating whether the FileSystem API is supported
params['isWebkitDirApiSupported'] = 'webkitdirectory' in document.createElement('input'); // Sets a boolean indicating whether the Webkit Directory API is supported
params['libzimMode'] = getSetting('libzimMode') || 'wasm'; // Sets a value indicating which libzim mode is selected
params['useLibzim'] = !!getSetting('useLibzim'); // Sets a value indicating which libzim mode is selected

/**
* Apply any override parameters that might be in the querystring.
Expand Down Expand Up @@ -185,6 +189,8 @@ document.getElementById('useHomeKeyToFocusSearchBarCheck').checked = params.useH
document.getElementById('openExternalLinksInNewTabsCheck').checked = params.openExternalLinksInNewTabs;
document.getElementById('languageSelector').value = params.overrideBrowserLanguage || 'default';
document.getElementById('bypassAppCacheCheck').checked = !params.appCache;
document.getElementById('libzimModeSelect').value = params.libzimMode;
document.getElementById('useLibzim').checked = params.useLibzim;
document.getElementById('appVersion').textContent = 'Kiwix ' + params.appVersion;

// This is a simplified version of code in settingsStore, because that module is not available in init.js
Expand Down
9 changes: 6 additions & 3 deletions www/js/lib/zimArchive.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,19 @@ function ZIMArchive (storage, path, callbackReady, callbackError) {
]).then(function () {
that.libzimReady = null;
// There is currently an exception thrown in the libzim wasm if we attempt to load a split ZIM archive, so we work around
// In case of a split ZIM, It will not be loaded properly by libzim if libzim is enabled
var isSplitZim = /\.zima.$/i.test(that.file._files[0].name);
if (that.file.fullTextIndex && (params.debugLibzimASM || !isSplitZim && typeof Atomics !== 'undefined' &&
// Note that Android and NWJS currently throw due to problems with Web Worker context
!/Android/.test(params.appType) && !(window.nw && that.file._files[0].readMode === 'electron'))) {
var libzimReaderType = params.debugLibzimASM || ('WebAssembly' in self ? 'wasm' : 'asm');
console.log('Instantiating libzim ' + libzimReaderType + ' Web Worker...');
!/Android/.test(params.appType) && !(window.nw && that.file._files[0].readMode === 'electron')) || params.useLibzim) {
var libzimReaderType = params.libzimMode;
if (libzimReaderType === 'default') libzimReaderType = 'WebAssembly' in self ? 'wasm.dev' : 'asm.dev';
console.log('[DEBUG] Instantiating libzim ' + libzimReaderType + ' Web Worker...');
LZ = new Worker('js/lib/libzim-' + libzimReaderType + '.js');
that.callLibzimWorker({ action: 'init', files: that.file._files }).then(function () {
that.libzimReady = 'ready';
params.searchProvider = 'fulltext: ' + libzimReaderType;
if (params.useLibzim) whenZimReady();
// Update the API panel
uiUtil.reportSearchProviderToAPIStatusPanel(params.searchProvider);
}).catch(function (err) {
Expand Down

0 comments on commit 11138ff

Please sign in to comment.