sync working code, and import type-doc
This commit is contained in:
160
lib/ui/media.js
Normal file
160
lib/ui/media.js
Normal file
@ -0,0 +1,160 @@
|
||||
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');
|
||||
}
|
Reference in New Issue
Block a user