forked from andyburke/autonomous.contact
refactor: clean up chat and split up embed handling
This commit is contained in:
parent
03751c6d00
commit
7e4ab72fe6
14 changed files with 352 additions and 274 deletions
48
public/js/embeds/audio.js
Normal file
48
public/js/embeds/audio.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
function embed_audio(link_info) {
|
||||
if (typeof link_info.extension !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
const mime_types = get_mime_types(link_info.extension);
|
||||
const is_audio = mime_types[0]?.indexOf("audio") === 0;
|
||||
|
||||
if (!is_audio) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="audio-container" tabindex="-1">
|
||||
<audio controls>
|
||||
<source src="${link_info.url}" type="${mime_types[0].indexOf("audio/mpeg") === 0 ? "audio/mpeg" : mime_types[0]}">
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
<div class="enhanced-audio-player-container">
|
||||
<div class="audio-player-display-container">
|
||||
<canvas class="audio-player-display"></canvas>
|
||||
</div>
|
||||
<div class="audio-controls-container">
|
||||
<div class="progress-container">
|
||||
<div class="time-container"><span class="current">00:00</span></div>
|
||||
<div class="slider-container">
|
||||
<input type="range" name="progress" title="" min="0" max="1000" step="1" value="0" />
|
||||
<label class="time-container" for="progress"><span class="current">00:00</span></label>
|
||||
</div>
|
||||
<div class="time-container"><span class="duration">00:00</span></div>
|
||||
</div>
|
||||
<div class="buttons-container">
|
||||
<div class="audio-control blank">
|
||||
<a href="${link_info.url}" download>
|
||||
<div class="icon download"></div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="audio-control skip-back"><div class="icon skip-back"></div></div>
|
||||
<div class="audio-control play-pause-toggle"><div class="icon play"></div><div class="icon pause"></div></div>
|
||||
<div class="audio-control skip-forward"><div class="icon skip-forward"></div></div>
|
||||
<div class="audio-control volume">
|
||||
<input type="range" name="volume" title="Volume" min="0" max="100" step="1" value="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
13
public/js/embeds/gif.js
Normal file
13
public/js/embeds/gif.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
function embed_gif(link_info) {
|
||||
if (typeof link_info.extension !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
const is_gif = get_mime_types(link_info.extension).includes("image/gif");
|
||||
|
||||
if (!is_gif) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `<div class="embed-container image gif"><img src="${link_info.url}" alt="A gif from ${link_info.domain}" /></div>`;
|
||||
}
|
||||
13
public/js/embeds/image.js
Normal file
13
public/js/embeds/image.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
function embed_image(link_info) {
|
||||
if (typeof link_info.extension !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
const is_image = get_mime_types(link_info.extension).shift()?.indexOf("image") === 0;
|
||||
|
||||
if (!is_image) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `<div class="embed-container image"><img src="${link_info.url}" alt="An image from ${link_info.domain}" /></div>`;
|
||||
}
|
||||
3
public/js/embeds/link.js
Normal file
3
public/js/embeds/link.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
function embed_link(link_info) {
|
||||
return `<a href="${link_info.url}">${link_info.url}</a>`;
|
||||
}
|
||||
13
public/js/embeds/mp4.js
Normal file
13
public/js/embeds/mp4.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
function embed_mp4(link_info) {
|
||||
if (typeof link_info.extension !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
const is_mp4 = get_mime_types(link_info.extension).includes("video/mp4");
|
||||
|
||||
if (!is_mp4) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `<div class="embed-container image gif letterbox"><video autoplay="true" muted="true" loop="true" playsinline="true"><source src="${link_info.url}" type="video/mp4"><a href="${link_info.url}">${link_info.url}</a></video></div>`;
|
||||
}
|
||||
33
public/js/embeds/spotify.js
Normal file
33
public/js/embeds/spotify.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
const SPOTIFY_EXTRACTOR =
|
||||
/^\/(?<item_type>(?:album|artist|episode|playlist|tracks?))\/?(?<item_id>[a-zA-Z0-9]{22})/gi;
|
||||
|
||||
function embed_spotify(link_info) {
|
||||
const is_spotify_link = ["spotify.com"].includes(link_info.domain?.toLowerCase());
|
||||
|
||||
if (!is_spotify_link) {
|
||||
return;
|
||||
}
|
||||
|
||||
SPOTIFY_EXTRACTOR.lastIndex = 0;
|
||||
|
||||
const {
|
||||
groups: { item_type, item_id },
|
||||
} = SPOTIFY_EXTRACTOR.exec(link_info.path ?? "") ?? { groups: {} };
|
||||
|
||||
if (!item_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="embed-container iframe ${!item_type || item_type.toLowerCase().indexOf("track") === 0 ? "short" : "square"} spotify rounded">
|
||||
<div class="embed-actions-container">
|
||||
<button class="icon plus" onclick="console.log(\"close\");"/>
|
||||
<button class="icon pause" onclick="console.log(\"stop\");"/>
|
||||
</div>
|
||||
<iframe
|
||||
src="https://open.spotify.com/embed/${item_type ?? "track"}/${item_id}"
|
||||
allowfullscreen
|
||||
allow="clipboard-write; encrypted-media; fullscreen; picture-in-picture"
|
||||
loading="lazy"></iframe>
|
||||
</div>`;
|
||||
}
|
||||
35
public/js/embeds/tidal.js
Normal file
35
public/js/embeds/tidal.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
const TIDAL_EXTRACTOR =
|
||||
/^\/(?:(?<action>.*?)\/)?(?<item_type>(?:album|artist|episode|playlist|tracks?))\/(?<item_id>[0-9]+)/gi;
|
||||
|
||||
function embed_tidal(link_info) {
|
||||
const is_tidal_link = ["tidal.com", "tidalhi.fi"].includes(link_info.domain?.toLowerCase());
|
||||
|
||||
if (!is_tidal_link) {
|
||||
return;
|
||||
}
|
||||
|
||||
TIDAL_EXTRACTOR.lastIndex = 0;
|
||||
|
||||
const {
|
||||
groups: { action, item_type, item_id },
|
||||
} = TIDAL_EXTRACTOR.exec(link_info.path ?? "") ?? { groups: {} };
|
||||
|
||||
if (!(item_type && item_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="embed-container iframe ${item_type.toLowerCase().indexOf("track") === 0 ? "short" : "square"} tidal">
|
||||
<div class="embed-actions-container">
|
||||
<button class="icon plus" onclick="console.log(\"close\");"/>
|
||||
<button class="icon pause" onclick="console.log(\"stop\");"/>
|
||||
</div>
|
||||
<iframe
|
||||
src="https://embed.tidal.com/${item_type.at(-1) === "s" ? item_type : `${item_type}s`}/${item_id}"
|
||||
allow="encrypted-media"
|
||||
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
|
||||
title="TIDAL Embed Player"
|
||||
loading="lazy"
|
||||
></iframe>
|
||||
</div>`;
|
||||
}
|
||||
35
public/js/embeds/vimeo.js
Normal file
35
public/js/embeds/vimeo.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
const VIMEO_ID_EXTRACTOR =
|
||||
/(?<video_domain>vimeo\.com)(?:\/(?<action>video|embed|watch|shorts|v))?.*(?:(?:\/|v=)(?<video_id>[A-Za-z0-9._%-]*))\S*/gi;
|
||||
|
||||
function embed_vimeo(link_info) {
|
||||
const is_vimeo_link = ["vimeo.com"].includes(link_info.domain?.toLowerCase());
|
||||
|
||||
if (!is_vimeo_link) {
|
||||
return;
|
||||
}
|
||||
|
||||
VIMEO_ID_EXTRACTOR.lastIndex = 0;
|
||||
|
||||
const {
|
||||
groups: { video_domain, action, video_id },
|
||||
} = VIMEO_ID_EXTRACTOR.exec(link_info.url) ?? { groups: {} };
|
||||
|
||||
if (!video_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="embed-container iframe letterbox vimeo">
|
||||
<div class="embed-actions-container">
|
||||
<button class="icon plus" onclick="console.log(\"close\");"/>
|
||||
<button class="icon pause" onclick="console.log(\"stop\");"/>
|
||||
</div>
|
||||
<iframe
|
||||
src="https://player.vimeo.com/video/${video_id}"
|
||||
frameborder="0"
|
||||
allow="fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share"
|
||||
referrerpolicy="strict-origin-when-cross-origin"
|
||||
title="Star Trek: Legacy"
|
||||
loading="lazy"></iframe>
|
||||
</div>`;
|
||||
}
|
||||
38
public/js/embeds/youtube.js
Normal file
38
public/js/embeds/youtube.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
const YOUTUBE_ID_EXTRACTOR =
|
||||
/(?<video_domain>youtu(?:be\.com|\.be|be\.googleapis\.com))(?:\/(?<action>video|embed|watch|shorts|v))?.*(?:(?:\/|v=)(?<video_id>[A-Za-z0-9._%-]*))\S*/gi;
|
||||
|
||||
function embed_youtube(link_info) {
|
||||
const is_youtube_link = ["youtube.com", "youtu.be", "youtube.googleapis.com"].includes(
|
||||
link_info.domain?.toLowerCase(),
|
||||
);
|
||||
|
||||
if (!is_youtube_link) {
|
||||
return;
|
||||
}
|
||||
|
||||
YOUTUBE_ID_EXTRACTOR.lastIndex = 0;
|
||||
|
||||
const {
|
||||
groups: { video_domain, action, video_id },
|
||||
} = YOUTUBE_ID_EXTRACTOR.exec(link_info.url) ?? { groups: {} };
|
||||
|
||||
if (!video_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="embed-container iframe ${action === "shorts" ? "vertical" : "letterbox"} youtube">
|
||||
<div class="embed-actions-container">
|
||||
<button class="icon plus" onclick="console.log(\"close\");"/>
|
||||
<button class="icon pause" onclick="console.log(\"stop\");"/>
|
||||
</div>
|
||||
<iframe
|
||||
src="https://www.youtube.com/embed/${video_id}"
|
||||
title="YouTube video player"
|
||||
allow="clipboard-write; encrypted-media; picture-in-picture; web-share;"
|
||||
referrerpolicy="strict-origin-when-cross-origin"
|
||||
allowfullscreen
|
||||
loading="lazy"
|
||||
></iframe>
|
||||
</div>`;
|
||||
}
|
||||
55
public/js/htmlify.js
Normal file
55
public/js/htmlify.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// wow https://stackoverflow.com/questions/3891641/regex-test-only-works-every-other-time
|
||||
// watch out for places we need to set `lastIndex` ... :frown:
|
||||
|
||||
const EMBEDS = [
|
||||
embed_tidal,
|
||||
embed_spotify,
|
||||
embed_youtube,
|
||||
embed_vimeo,
|
||||
embed_audio,
|
||||
embed_gif,
|
||||
embed_mp4,
|
||||
embed_image,
|
||||
embed_link,
|
||||
];
|
||||
|
||||
const URL_MATCHING_REGEX =
|
||||
/(?:(?<protocol>[a-zA-Z]+):)?(?:\/\/)?(?:(?<auth>(?<username>\S.+)\:(?<password>.+))\@)?(?<host>(?:(?<hostname>[-a-zA-Z0-9\.]+)\.)?(?<domain>(?:[-a-zA-Z0-9]+?\.(?<tld>[-a-zA-Z0-9]{2,64}))|localhost)(?:\:(?<port>[0-9]{1,6}))?)\b(?<path>[-a-zA-Z0-9@:%_{}\[\]<>\(\)\+.~&\/="]*?(?<extension>\.[^\.?/#"\n<>]+)?)(?:\?(?<query>[a-zA-Z0-9!$%&<>()*+,-\.\/\:\;\=\?\@_~"]+))?(?:#(?<hash>[a-zA-Z0-9!$&'()*+,-\.\/\:\;\=\?\@_~"]*?))?(?:$|\s)/gim;
|
||||
|
||||
function htmlify(content) {
|
||||
let html_content = (content ?? "").replace(/\n/g, "<br/>");
|
||||
let match;
|
||||
|
||||
URL_MATCHING_REGEX.lastIndex = 0;
|
||||
while ((match = URL_MATCHING_REGEX.exec(content)) !== null) {
|
||||
const url = match[0];
|
||||
|
||||
const {
|
||||
groups: { protocol, host, hostname, domain, tld, path, extension, query, hash },
|
||||
} = match;
|
||||
|
||||
const link_info = {
|
||||
url,
|
||||
protocol,
|
||||
host,
|
||||
hostname,
|
||||
domain,
|
||||
tld,
|
||||
path,
|
||||
extension,
|
||||
query,
|
||||
hash,
|
||||
};
|
||||
|
||||
for (const embed of EMBEDS) {
|
||||
const result = embed(link_info);
|
||||
|
||||
if (typeof result === "string") {
|
||||
html_content = html_content.replace(url, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return html_content;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue