ui-lib/lib/ui/media.js

160 lines
5.0 KiB
JavaScript

import "./css/media.scss";
import { createElement } from "../functions";
import { createIcon } from "./icon";
import { get } from "../utility";
export function createPicture(url) {
return createElement('a', a => {
a.className = 'ui-media-picture';
a.target = '_blank';
a.href = url;
},
createElement('img', img => {
img.src = url;
})
);
}
function readBlob(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = e => {
const data = new Uint8Array(e.target.result);
resolve(data);
};
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
});
}
function playAmrArray(array) {
return new Promise((resolve, reject) => {
const samples = AMR.decode(array);
if (samples != null) {
resolve(samples);
} else {
reject();
}
});
}
function playPcm(samples, ended) {
return new Promise(resolve => {
const ctx = new AudioContext();
ctx.addEventListener('statechange', () => resolve(ctx));
const source = ctx.createBufferSource();
if (typeof ended === 'function') {
source.addEventListener('ended', () => ended(ctx));
}
const buffer = ctx.createBuffer(1, samples.length, 8000);
if (typeof buffer.copyToChannel === 'function') {
buffer.copyToChannel(samples, 0, 0);
} else {
const channelBuffer = buffer.getChannelData(0);
channelBuffer.set(samples);
}
source.buffer = buffer;
source.connect(ctx.destination);
ctx.duration = buffer.duration;
source.start();
// resolve(ctx);
});
}
function getTimeLabel(time) {
time = Math.round(time);
return String(Math.floor(time / 60)).padStart(2, '0') + ':' + String(time % 60).padStart(2, '0');
}
export function createAudio(mime, url) {
if (mime === 'audio/amr' && typeof AMR !== 'undefined') {
const timestamp = createElement('span', 'ui-media-timestamp');
timestamp.textContent = '00:00 / 00:00';
let context;
let timer;
return createElement('div', 'ui-media-audio',
createElement('button', button => {
button.addEventListener('click', () => {
if (context != null) {
clearInterval(timer);
context.close();
context = null;
timestamp.textContent = '00:00 / 00:00';
button.replaceChildren(createIcon('fa-solid', 'play'));
return;
}
get(url, {
accept: mime
})
.then(r => r.blob())
.then(r => readBlob(r))
.then(r => playAmrArray(r))
.then(r => playPcm(r, ctx => {
context = null;
clearInterval(timer);
timestamp.textContent = '00:00 / ' + getTimeLabel(ctx.duration);
button.replaceChildren(createIcon('fa-solid', 'play'));
}))
.then(ctx => {
context = ctx;
button.replaceChildren(createIcon('fa-solid', 'stop'));
const total = getTimeLabel(ctx.duration);
const refresh = () => timestamp.textContent = getTimeLabel(ctx.currentTime) + ' / ' + total;
refresh();
timer = setInterval(refresh, 500);
})
.catch(e => {
clearInterval(timer);
console.error(e);
});
});
},
createIcon('fa-solid', 'play')
),
timestamp
);
}
return createElement('audio', audio => {
audio.src = url;
audio.controls = true;
});
}
export function createVideo(url) {
return createElement('video', video => {
video.className = 'ui-media-video';
video.src = url;
video.controls = true;
});
}
function createFileElement(url, icon) {
return createElement('div', 'ui-media-file',
createIcon('fa-solid', icon),
createElement('a', a => {
a.target = '_blank';
a.href = url;
a.innerText = 'Click here to view the file';
})
);
}
export function createPdf(url) {
return createFileElement(url, 'file-pdf');
}
export function createSmilefile(url) {
return createFileElement(url, 'smile');
}
export function createVcard(url) {
return createFileElement(url, 'id-card');
}
export function createVideofile(url) {
return createFileElement(url, 'photo-video');
}
export function createFile(url) {
return createFileElement(url, 'file-alt');
}