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' || mime === '.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.className = 'play'; button.addEventListener('click', () => { if (context != null) { clearInterval(timer); context.close(); context = null; timestamp.textContent = '00:00 / 00:00'; button.className = 'play'; 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.className = 'play'; button.replaceChildren(createIcon('fa-solid', 'play')); })) .then(ctx => { context = ctx; button.className = 'stop'; 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; }); } export function createFile(url, icon = 'file-alt') { return createElement('div', `ui-media-file ${icon}`, createIcon('fa-solid', icon), createElement('a', a => { a.target = '_blank'; a.href = url; a.innerText = 'Click here to view the file'; }) ); }