Working version with Lufi API

This commit is contained in:
Booteille 2024-10-31 15:18:51 +01:00
parent 310332bf0d
commit d41cbb164e
No known key found for this signature in database
GPG Key ID: 0AB6C6CA01272646
15 changed files with 721 additions and 905 deletions

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,7 @@ function escapeHtml(string) {
}); });
} }
function changeLang() { function changeLang() {
window.location = langUrl+$(this).val(); window.location = langUrl + $(this).val();
} }
function formatDate(unixTimestamp) { function formatDate(unixTimestamp) {
return new Date(unixTimestamp * 1000).toLocaleString(window.navigator.language, { return new Date(unixTimestamp * 1000).toLocaleString(window.navigator.language, {
@ -27,7 +27,7 @@ function formatDate(unixTimestamp) {
minute: '2-digit', minute: '2-digit',
}) })
} }
$(document).ready(function(){ $(document).ready(function () {
$('select').material_select(); $('select').material_select();
$(".select-lang select").on('change', changeLang); $(".select-lang select").on('change', changeLang);
$(".select-lang-mobile select").on('change', changeLang); $(".select-lang-mobile select").on('change', changeLang);

482
themes/default/public/js/lufi-down.js Normal file → Executable file
View File

@ -1,293 +1,237 @@
// vim:set sw=4 ts=4 sts=4 ft=javascript expandtab: // vim:set sw=4 ts=4 sts=4 ft=javascript expandtab:
import { lufi } from "/js/lufi.js";
import * as lufiApi from "/js/lufi-api.browser.js" const abortedDOM = document.createElement("div");
abortedDOM.innerHTML = `<h4>${i18n.aborted1}</h4>
<p>
<a id="reloadLocation"
class="waves-effect waves-light btn">
${i18n.aborted2}
</a>
</p>`;
/* const loadingDOM = document.getElementById("loading");
* Return the deciphering key stored in anchor part of the URL const passwordDOM = document.getElementById("file_pwd");
* Stolen from https://github.com/sebsauvage/ZeroBin/blob/master/js/zerobin.js const filesizeDOM = document.getElementById("filesize");
*/
function pageKey() {
var key = window.location.hash.substring(1); // Get key
let i;
// Some stupid web 2.0 services and redirectors add data AFTER the anchor document.addEventListener("DOMContentLoaded", () => {
// (such as &utm_source=...). let go = true;
// We will strip any additional data.
// First, strip everything after the equal sign (=) which signals end of base64 string. filesizeDOM.innerHTML = filesize(filesizeDOM.attributes.getNamedItem("data-filesize").value, {
i = key.indexOf('='); base: 10,
let isb64 = false });
if (i > -1) { if (isPasswordNeeded()) {
key = key.substring(0, i + 1); go = false;
isb64 = true passwordDOM.focus()
onPasswordEvents();
}
if (go) {
startDownload();
}
});
const isPasswordNeeded = () => document.querySelectorAll("#file_pwd").length === 1
const startDownload = () => {
warnOnReload();
lufi
.download(window.location, passwordDOM?.value)
.andThen((job) => {
job.onProgress(() => {
updateProgress(job.lufiFile);
});
document.getElementById("abort").onclick = () => {
remove(["please-wait", "pbd", "loading", "abort"]);
job.terminate();
filesizeDOM.parentElement.append(abortedDOM);
warnOnReload(false)
document.getElementById("reloadLocation").onclick = (e) => {
e.preventDefault();
window.location.reload();
};
};
return job.waitForCompletion();
})
.mapErr((error) => {
addAlert(error.message);
warnOnReload(false)
remove(["abort"])
})
.andThen((job) => {
notify(i18n.fileDownloaded, job.lufiFile.name);
remove(["please-wait", "loading"]);
const pbd = document.getElementById("pbd");
pbd.className = "center-align";
const blobURL = URL.createObjectURL(job.tmpFile);
let htmlContent = `<p><a href="${blobURL}" class="btn btn-primary" download="${escapeHtml(
job.lufiFile.name
)}">${i18n.download}</a></p>`;
var isZip = filesizeDOM.getAttribute("data-zipped") === "true";
if (job.lufiFile.type.match(/^image\//) !== null) {
htmlContent += `<img id="render-image" class="responsive-img" alt="${escapeHtml(
job.lufiFile.name
)}" src="${blobURL}">`;
} else if (job.lufiFile.type.match(/^video\//) !== null) {
htmlContent += `<video class="responsive-video" controls>
<source src="${blobURL}" type="${job.lufiFile.type}">
</video>`;
} else if (job.lufiFile.type.match(/^audio\//) !== null) {
htmlContent += `<audio class="responsive-video" controls>
<source src="${blobURL}" type="${job.lufiFile.type}">
</audio>`;
} else if (isZip) {
htmlContent += `<p><a class="btn btn-primary" id="showZipContent">${i18n.showZipContent}</a></p>`;
}
pbd.innerHTML = htmlContent;
if (isZip) {
showZipContent(job.tmpFile);
}
document.getElementById("abort").remove();
window.completed = true;
});
};
const remove = (elements) => {
elements.forEach((id) => {
if (document.getElementById(id)) {
document.getElementById(id).remove();
} else {
console.error(`${id} does not exist`)
} }
});
};
// If the equal sign was not present, some parameters may remain: const onPasswordEvents = () => {
i = key.indexOf('&'); if (i > -1) { key = key.substring(0, i); } const callback = () => {
document.getElementsByClassName("file-progress")[0].classList.remove("hide");
document.getElementsByClassName("file-abort")[0].classList.remove("hide");
// Then add trailing equal sign if it's missing and was using the Sjcl algorithm passwordDOM.parentElement.parentElement.classList.add("hide");
if (isb64) {
if (key.charAt(key.length - 1) !== '=') key += '='; startDownload();
}
document.getElementById("go").onclick = () => {
callback();
};
document.getElementById("file_pwd").onkeydown = (event) => {
if (event.key === "Enter") {
callback();
} }
}
return key;
} }
// Something's wring // Something's wring
function addAlert(msg) { const addAlert = (msg) => {
$('#please-wait').remove(); // remove(["please-wait"]);
var pbd = $('.file-progress'); let pbd = document.getElementsByClassName("file-progress")[0];
pbd.attr('role', 'alert');
pbd.removeClass('progress'); pbd.setAttribute("role", "alert");
pbd.html(`<div class="card pink">
pbd.classList.remove("progress");
pbd.innerHTML = `<div class="card pink">
<div class="card-content white-text"> <div class="card-content white-text">
<strong>${msg}</strong> <strong>${msg}</strong>
</div> </div>
</div>`); </div>`;
} }
// Spawn WebSocket const warnOnReload = (toWarn = true) => {
function spawnWebsocket(pa) { if (toWarn) {
console.log('Spawning websocket…'); window.onbeforeunload = confirmExit;
var ws = new WebSocket(ws_url); } else {
ws.onopen = function () { window.onbeforeunload = null;
console.log('Connection is established!'); }
var l = $('#loading');
l.html(i18n.loading.replace(/XX1/, (pa + 1)));
if ($('#file_pwd').length === 1) {
val = $('#file_pwd').val();
window.ws.send(`{"part":${pa}, "file_pwd": "${val}"}`);
} else {
window.ws.send(`{"part":${pa}}`);
}
};
ws.onclose = function () {
console.log('Connection is closed');
if (!window.completed) {
window.attempts++;
if (window.attempts < 10) {
console.log(`Connection closed. Retrying to get slice ${pa}`);
window.ws = spawnWebsocket(pa);
} else {
alert(i18n.tooMuchAttempts);
}
}
}
ws.onmessage = function (e) {
var res = e.data.split('XXMOJOXX');
var json = res.shift();
var data = JSON.parse(json);
// Reset counter since we succeded to open a websocket and got a message
window.attempts = 0;
if (data.msg !== undefined) {
addAlert(data.msg);
console.log(data.msg);
if ($('#file_pwd').length === 1) {
$('.file-abort').addClass('hide');
}
window.onbeforeunload = null;
window.attempts = 10;
} else {
console.log(`Getting slice ${data.part + 1} of ${data.total}`);
var slice = JSON.parse(res.shift());
// If file was used using Lufi API
if (slice.iv) {
slice.iv = new Uint8Array(Object.values(slice.iv))
}
var percent = Math.round(1000 * (data.part + 1) / data.total) / 10;
var wClass = percent.toString().replace('.', '-');
var pb = $('#pb');
pb.removeClass();
pb.addClass('determinate');
pb.addClass(`width-${wClass}`);
pb.attr('aria-valuenow', percent);
$('#pbt').html(`${percent}%`);
try {
lufiApi.lufiCrypto.decrypt(window.key, slice).then((decrypted) => {
window.a[data.part] = decrypted;
if (data.part + 1 === data.total) {
var blob = new Blob(a, { type: data.type });
notify(i18n.fileDownloaded, data.name);
$('#please-wait').remove();
$('#loading').remove();
var pbd = $('#pbd');
pbd.attr('class', 'center-align');
// IE & Edge fix for downloading blob files, gives option to save or open the file when the link is opened.
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
var fileName = escapeHtml(data.name);
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
var blobURL = URL.createObjectURL(blob);
}
var innerHTML = `<p><a href="${blobURL}" class="btn btn-primary" download="${escapeHtml(data.name)}">${i18n.download}</a></p>`;
var isZip = ($('#filesize').attr('data-zipped') === 'true');
if (data.type.match(/^image\//) !== null) {
innerHTML += `<img id="render-image" class="responsive-img" alt="${escapeHtml(data.name)}" src="${blobURL}">`;
} else if (data.type.match(/^video\//) !== null) {
innerHTML += `<video class="responsive-video" controls>
<source src="${blobURL}" type="${data.type}">
</video>`;
} else if (data.type.match(/^audio\//) !== null) {
innerHTML += `<audio class="responsive-video" controls>
<source src="${blobURL}" type="${data.type}">
</audio>`;
} else if (isZip) {
innerHTML += `<p><a class="btn btn-primary" id="showZipContent">${i18n.showZipContent}</a></p>`;
}
pbd.html(innerHTML);
if (isZip) {
$('#showZipContent').click(function () {
JSZip.loadAsync(blob)
.then(function (zip) {
var innerHTML = `<h3>${i18n.zipContent}</h3><ul>`;
zip.forEach(function (relativePath, zipEntry) {
innerHTML += `<li>
${escapeHtml(zipEntry.name)}
(${filesize(zipEntry._data.uncompressedSize, { base: 10 })})
<a href="#"
download="${escapeHtml(zipEntry.name)}"
class="download-zip-content"
title="${i18n.download}">
<i class="mdi-file-file-download"></i>
</a>
</li>`
});
innerHTML += '</ul>';
pbd.append(innerHTML);
$('.download-zip-content').click(function (e) {
e.preventDefault();
var t = $(this);
var filename = t.attr('download');
zip.files[filename].async('blob').then(function (blob) {
t.unbind('click');
t.attr('href', URL.createObjectURL(blob));
t[0].click();
});
})
$('#showZipContent').hide();
$('#showZipContent').unbind('click');
});
});
}
if ($('#file_pwd').length === 1) {
val = $('#file_pwd').val();
window.ws.send(`{"ended":true, "file_pwd": "${val}"}`);
} else {
window.ws.send('{"ended":true}');
}
window.onbeforeunload = null;
window.completed = true;
$('#abort').remove();
} else {
var l = $('#loading');
l.html(i18n.loading.replace(/XX1/, (data.part + 1)));
if (ws.readyState === 3) {
window.ws = spawnWebsocket(data.part + 1);
} else {
window.ws.onclose = function () {
console.log('Connection is closed');
if (!window.completed) {
console.log(`Connection closed. Retrying to get slice ${data.part + 1}`);
window.ws = spawnWebsocket(data.part + 1);
}
}
window.ws.onerror = function () {
console.log(`Error. Retrying to get slice ${data.part + 1}`);
window.ws = spawnWebsocket(data.part + 1);
};
if ($('#file_pwd').length === 1) {
val = $('#file_pwd').val();
window.ws.send(`{"part":${data.part + 1}, "file_pwd": "${val}"}`);
} else {
window.ws.send(`{"part":${data.part + 1}}`);
}
}
}
}).catch((e) => {
console.error(e);
})
} catch (err) {
if (err.message === 'ccm: tag doesn\'t match') {
addAlert(i18n.badkey);
} else {
addAlert(err.message);
}
window.onbeforeunload = null;
}
}
}
ws.onerror = function () {
window.attempts++;
if (window.attempts < 10) {
console.log(`Error. Retrying to get slice ${pa}`);
window.ws = spawnWebsocket(pa);
} else {
alert(i18n.tooMuchAttempts);
}
}
return ws;
} }
// When it's ready
$(document).ready(function () { const updateProgress = (lufiFile) => {
$('#abort').click(function () { // Update loading text
window.ws.onclose = function () { }; loadingDOM.textContent = i18n.loading.replace(/XX1/, lufiFile.chunksReady);
window.ws.close();
$('#please-wait, #loading, #pbd, #abort').remove(); // Update progress bar
$('#filesize').parent().append(`<h4>${i18n.aborted1}</h4> const percent = Math.round((1000 * lufiFile.chunksReady) / lufiFile.totalChunks) / 10;
<p> const wClass = percent.toString().replace(".", "-");
<a id="reloadLocation"
class="waves-effect waves-light btn"> const pb = document.getElementById("pb");
${i18n.aborted2} pb.className = `determinate width-${wClass}`;
</a> pb.attributes.getNamedItem("aria-valuenow").value = percent;
</p>`);
window.onbeforeunload = null; document.getElementById("pbt").innerHTML = `${percent}%`;
$('#reloadLocation').on('click', function (e) { }
const showZipContent = (blob) => {
const showZipContentDOM = document.getElementById('showZipContent');
const showZipContentDOMListener = () => {
JSZip.loadAsync(blob)
.then((zip) => {
const newElement = document.createElement("div");
let innerHTML = `<h3>${i18n.zipContent}</h3><ul>`;
zip.forEach(function (_relativePath, zipEntry) {
innerHTML += `<li>
${escapeHtml(zipEntry.name)}
(${filesize(zipEntry._data.uncompressedSize, { base: 10 })})
<a href="#"
download="${escapeHtml(zipEntry.name)}"
class="download-zip-content"
title="${i18n.download}">
<i class="mdi-file-file-download"></i>
</a>
</li>`
});
innerHTML += '</ul>';
newElement.innerHTML = innerHTML
pbd.append(newElement);
console.debug()
document.querySelectorAll('.download-zip-content').forEach((element) => {
const elementListener = (e) => {
e.preventDefault(); e.preventDefault();
window.location.reload(); var filename = element.getAttribute('download');
}) zip.files[filename].async('blob').then((blob) => {
}); element.removeEventListener('click', elementListener);
$('#filesize').html(filesize($('#filesize').attr('data-filesize'), { base: 10 })); element.setAttribute('href', URL.createObjectURL(blob));
window.a = new Array(); element.click();
window.key = pageKey();
window.completed = false;
window.attempts = 0;
if (key !== '=') {
var go = true;
if ($('#file_pwd').length === 1) {
go = false;
$('#go').click(function () {
$('.file-progress, .file-abort').removeClass('hide');
$('#file_pwd').parent().parent().addClass('hide');
// Set websocket
window.ws = spawnWebsocket(0);
// Prevent exiting page before full download
window.onbeforeunload = confirmExit;
}); });
} };
if (go) { element.addEventListener('click', elementListener);
// Set websocket
window.ws = spawnWebsocket(0);
// Prevent exiting page before full download showZipContentDOM.style.display = "none";
window.onbeforeunload = confirmExit;
} showZipContentDOM.removeEventListener('click', showZipContentDOMListener);
} else { });
addAlert(i18n.nokey); })
} };
});
showZipContentDOM.onclick = showZipContentDOMListener
}

1118
themes/default/public/js/lufi-up.js Normal file → Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3
themes/default/templates/index.html.ep Normal file → Executable file
View File

@ -152,8 +152,7 @@
<div id="results"> <div id="results">
<h2><%= l('Uploaded files') %></h2> <h2><%= l('Uploaded files') %></h2>
<div id="misc"></div> <div id="misc"></div>
<ul class="list-group" id="ul-results"> <ul class="list-group" id="ul-results"></ul>
</ul>
</div> </div>
%= include 'delays' %= include 'delays'
% if (defined stash('invitation')) { % if (defined stash('invitation')) {

0
themes/default/templates/partial/render.js.ep Normal file → Executable file
View File

0
themes/default/templates/render.html.ep Normal file → Executable file
View File