Audio embed previews (no player yet).
This commit is contained in:
parent
a4be74cae3
commit
e6e5c873f6
5 changed files with 593 additions and 10 deletions
|
@ -204,3 +204,162 @@
|
|||
-webkit-backdrop-filter: blur(10px);
|
||||
transition: opacity .2s, transform .2s;
|
||||
}
|
||||
|
||||
.aembed {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-shadow: initial;
|
||||
}
|
||||
|
||||
.aembedph {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
color: var(--text-colour);
|
||||
text-decoration: none;
|
||||
text-shadow: initial;
|
||||
max-width: 500px;
|
||||
min-width: 300px;
|
||||
height: 70px;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
.aembedph:hover .aembedph-play,
|
||||
.aembedph:active .aembedph-play,
|
||||
.aembedph:focus .aembedph-play,
|
||||
.aembedph:focus-within .aembedph-play {
|
||||
opacity: 1;
|
||||
}
|
||||
.aembedph-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.aembedph-bg-none {
|
||||
background: var(--background-pattern);
|
||||
background-color: var(--aembedph-colour, var(--accent-colour));
|
||||
background-blend-mode: multiply;
|
||||
}
|
||||
.aembedph-bg img {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
transform: scale(1.1);
|
||||
filter: blur(10px) brightness(80%);
|
||||
}
|
||||
.aembedph-fg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.aembedph-info {
|
||||
display: flex;
|
||||
background-color: var(--background-colour-translucent-5);
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
.aembedph-info-cover {
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.aembedph-info-cover-none {
|
||||
background-color: var(--aembedph-colour, var(--accent-colour));
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
}
|
||||
.aembedph-info-cover img {
|
||||
max-width: 60px;
|
||||
max-height: 60px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.aembedph-info-cover-none img {
|
||||
display: none;
|
||||
}
|
||||
.aembedph-info-body {
|
||||
padding: 0 5px;
|
||||
}
|
||||
.aembedph-info-title {
|
||||
font-size: 1.4em;
|
||||
font-weight: 400;
|
||||
line-height: 1.2em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 430px;
|
||||
}
|
||||
.aembedph-info-title-artist {
|
||||
max-width: 200px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 700;
|
||||
}
|
||||
.aembedph-info-album {
|
||||
line-height: 1.4em;
|
||||
word-break: break-word;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 430px;
|
||||
}
|
||||
.aembedph-info-site {
|
||||
font-size: .9em;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.aembedph-play {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity .2s;
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
.aembedph-play-internal {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
text-align: center;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.aembedph-play-external {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
}
|
||||
.aembedph-play-external-link {
|
||||
padding: 2px 5px;
|
||||
line-height: 1.5em;
|
||||
text-decoration: none;
|
||||
color: var(--text-colour);
|
||||
background-color: var(--background-colour-translucent-6);
|
||||
border-radius: 5px;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
transition: background-color .2s;
|
||||
}
|
||||
.aembedph-play-external-link:hover,
|
||||
.aembedph-play-external-link:focus {
|
||||
background-color: var(--background-colour-translucent-8);
|
||||
}
|
||||
.aembedph-play-external-link:active {
|
||||
background-color: var(--background-colour-translucent-5);
|
||||
}
|
||||
|
|
365
assets/js/misuzu/aembed.js
Normal file
365
assets/js/misuzu/aembed.js
Normal file
|
@ -0,0 +1,365 @@
|
|||
const MszAudioEmbedPlayerEvents = function() {
|
||||
return [
|
||||
'play', 'pause', 'stop',
|
||||
'mute', 'volume',
|
||||
'rate', 'duration', 'time',
|
||||
];
|
||||
};
|
||||
|
||||
const MszAudioEmbed = function(player) {
|
||||
const elem = $e({
|
||||
attrs: {
|
||||
classList: ['aembed', 'aembed-' + player.getType()],
|
||||
},
|
||||
child: player,
|
||||
});
|
||||
|
||||
return {
|
||||
getElement: function() {
|
||||
return elem;
|
||||
},
|
||||
appendTo: function(target) {
|
||||
target.appendChild(elem);
|
||||
},
|
||||
insertBefore: function(ref) {
|
||||
$ib(ref, elem);
|
||||
},
|
||||
nuke: function() {
|
||||
$r(elem);
|
||||
},
|
||||
replaceElement(target) {
|
||||
$ib(target, elem);
|
||||
$r(target);
|
||||
},
|
||||
getPlayer: function() {
|
||||
return player;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const MszAudioEmbedPlayer = function(metadata, options) {
|
||||
options = options || {};
|
||||
|
||||
const shouldAutoplay = options.autoplay === undefined || options.autoplay,
|
||||
haveNativeControls = options.nativeControls !== undefined && options.nativeControls;
|
||||
|
||||
const playerAttrs = {
|
||||
src: metadata.url,
|
||||
style: {},
|
||||
};
|
||||
|
||||
if(shouldAutoplay)
|
||||
playerAttrs.autoplay = 'autoplay';
|
||||
if(haveNativeControls)
|
||||
playerAttrs.controls = 'controls';
|
||||
|
||||
const watchers = new MszWatcherCollection;
|
||||
watchers.define(MszAudioEmbedPlayerEvents());
|
||||
|
||||
const player = $e({
|
||||
tag: 'audio',
|
||||
attrs: playerAttrs,
|
||||
});
|
||||
|
||||
const pub = {
|
||||
getElement: function() {
|
||||
return player;
|
||||
},
|
||||
appendTo: function(target) {
|
||||
target.appendChild(player);
|
||||
},
|
||||
insertBefore: function(ref) {
|
||||
$ib(ref, player);
|
||||
},
|
||||
nuke: function() {
|
||||
$r(player);
|
||||
},
|
||||
replaceElement(target) {
|
||||
$ib(target, player);
|
||||
$r(target);
|
||||
},
|
||||
getType: function() { return 'external'; },
|
||||
};
|
||||
|
||||
watchers.proxy(pub);
|
||||
|
||||
player.addEventListener('play', function() { watchers.call('play', pub); });
|
||||
|
||||
const pPlay = function() { player.play(); };
|
||||
pub.play = pPlay;
|
||||
|
||||
const pPause = function() { player.pause(); };
|
||||
pub.pause = pPause;
|
||||
|
||||
let stopCalled = false;
|
||||
player.addEventListener('pause', function() {
|
||||
watchers.call(stopCalled ? 'stop' : 'pause', pub);
|
||||
stopCalled = false;
|
||||
});
|
||||
|
||||
const pStop = function() {
|
||||
stopCalled = true;
|
||||
player.pause();
|
||||
player.currentTime = 0;
|
||||
};
|
||||
pub.stop = pStop;
|
||||
|
||||
const pIsPlaying = function() { return !player.paused; };
|
||||
pub.isPlaying = pIsPlaying;
|
||||
|
||||
const pIsMuted = function() { return player.muted; };
|
||||
pub.isMuted = pIsMuted;
|
||||
|
||||
let lastMuteState = player.muted;
|
||||
player.addEventListener('volumechange', function() {
|
||||
if(lastMuteState !== player.muted) {
|
||||
lastMuteState = player.muted;
|
||||
watchers.call('mute', pub, [lastMuteState]);
|
||||
} else
|
||||
watchers.call('volume', pub, [player.volume]);
|
||||
});
|
||||
|
||||
const pSetMuted = function(state) { player.muted = state; };
|
||||
pub.setMuted = pSetMuted;
|
||||
|
||||
const pGetVolume = function() { return player.volume; };
|
||||
pub.getVolume = pGetVolume;
|
||||
|
||||
const pSetVolume = function(volume) { player.volume = volume; };
|
||||
pub.setVolume = pSetVolume;
|
||||
|
||||
const pGetPlaybackRate = function() { return player.playbackRate; };
|
||||
pub.getPlaybackRate = pGetPlaybackRate;
|
||||
|
||||
player.addEventListener('ratechange', function() {
|
||||
watchers.call('rate', pub, [player.playbackRate]);
|
||||
});
|
||||
|
||||
const pSetPlaybackRate = function(rate) { player.playbackRate = rate; };
|
||||
pub.setPlaybackRate = pSetPlaybackRate;
|
||||
|
||||
window.addEventListener('durationchange', function() {
|
||||
watchers.call('duration', pub, [player.duration]);
|
||||
});
|
||||
|
||||
const pGetDuration = function() { return player.duration; };
|
||||
pub.getDuration = pGetDuration;
|
||||
|
||||
window.addEventListener('timeupdate', function() {
|
||||
watchers.call('time', pub, [player.currentTime]);
|
||||
});
|
||||
|
||||
const pGetTime = function() { return player.currentTime; };
|
||||
pub.getTime = pGetTime;
|
||||
|
||||
const pSeek = function(time) { player.currentTime = time; };
|
||||
pub.seek = pSeek;
|
||||
|
||||
return pub;
|
||||
};
|
||||
|
||||
const MszAudioEmbedPlaceholder = function(metadata, options) {
|
||||
options = options || {};
|
||||
|
||||
if(typeof options.player !== 'function' && typeof options.onclick !== 'function')
|
||||
throw 'Neither a player nor an onclick handler were provided.';
|
||||
|
||||
let title = [],
|
||||
album = undefined;
|
||||
if(metadata.media !== undefined && metadata.media.tags !== undefined) {
|
||||
const tags = metadata.media.tags;
|
||||
|
||||
if(tags.title !== undefined) {
|
||||
if(tags.artist !== undefined) {
|
||||
title.push({
|
||||
tag: 'span',
|
||||
attrs: {
|
||||
className: 'aembedph-info-title-artist',
|
||||
},
|
||||
child: tags.artist,
|
||||
});
|
||||
title.push(' - ');
|
||||
}
|
||||
|
||||
title.push({
|
||||
tag: 'span',
|
||||
attrs: {
|
||||
className: 'aembedph-info-title-title',
|
||||
},
|
||||
child: tags.title,
|
||||
});
|
||||
} else {
|
||||
title.push({
|
||||
tag: 'span',
|
||||
attrs: {
|
||||
className: 'aembedph-info-title-title',
|
||||
},
|
||||
child: metadata.title,
|
||||
});
|
||||
}
|
||||
|
||||
if(tags.album !== undefined && tags.album !== tags.title)
|
||||
album = tags.album;
|
||||
}
|
||||
|
||||
const infoChildren = [];
|
||||
|
||||
infoChildren.push({
|
||||
tag: 'h1',
|
||||
attrs: {
|
||||
className: 'aembedph-info-title',
|
||||
},
|
||||
child: title,
|
||||
});
|
||||
|
||||
infoChildren.push({
|
||||
tags: 'p',
|
||||
attrs: {
|
||||
className: 'aembedph-info-album',
|
||||
},
|
||||
child: album,
|
||||
});
|
||||
|
||||
infoChildren.push({
|
||||
tag: 'div',
|
||||
attrs: {
|
||||
className: 'aembedph-info-site',
|
||||
},
|
||||
child: metadata.site_name,
|
||||
});
|
||||
|
||||
const style = [];
|
||||
if(typeof metadata.color !== 'undefined')
|
||||
style.push('--aembedph-colour: ' + metadata.color);
|
||||
|
||||
const coverBackground = $e({
|
||||
attrs: {
|
||||
className: 'aembedph-bg',
|
||||
},
|
||||
child: {
|
||||
tag: 'img',
|
||||
attrs: {
|
||||
alt: '',
|
||||
src: metadata.image,
|
||||
onerror: function(ev) {
|
||||
coverBackground.classList.add('aembedph-bg-none');
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const coverPreview = $e({
|
||||
attrs: {
|
||||
className: 'aembedph-info-cover',
|
||||
},
|
||||
child: {
|
||||
tag: 'img',
|
||||
attrs: {
|
||||
alt: '',
|
||||
src: metadata.image,
|
||||
onerror: function(ev) {
|
||||
coverPreview.classList.add('aembedph-info-cover-none');
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const pub = {};
|
||||
|
||||
const elem = $e({
|
||||
attrs: {
|
||||
className: ('aembedph aembedph-' + (options.type || 'external')),
|
||||
style: style.join(';'),
|
||||
title: metadata.title,
|
||||
},
|
||||
child: [
|
||||
coverBackground,
|
||||
{
|
||||
attrs: {
|
||||
className: 'aembedph-fg',
|
||||
},
|
||||
child: [
|
||||
{
|
||||
attrs: {
|
||||
className: 'aembedph-info',
|
||||
},
|
||||
child: [
|
||||
coverPreview,
|
||||
{
|
||||
attrs: {
|
||||
className: 'aembedph-info-body',
|
||||
},
|
||||
child: infoChildren,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
className: 'aembedph-play',
|
||||
onclick: function(ev) {
|
||||
if(ev.target.tagName.toLowerCase() === 'a')
|
||||
return;
|
||||
|
||||
if(typeof options.onclick === 'function') {
|
||||
options.onclick(ev);
|
||||
return;
|
||||
}
|
||||
|
||||
const player = new options.player(metadata, options);
|
||||
|
||||
const embed = new MszAudioEmbed(player);
|
||||
if(options.autoembed === undefined || options.autoembed)
|
||||
embed.replaceElement(elem);
|
||||
|
||||
if(typeof options.onembed === 'function')
|
||||
options.onembed(embed);
|
||||
},
|
||||
},
|
||||
child: [
|
||||
{
|
||||
attrs: {
|
||||
className: 'aembedph-play-internal',
|
||||
},
|
||||
child: {
|
||||
tag: 'i',
|
||||
attrs: {
|
||||
className: 'fas fa-play fa-3x fa-fw',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
className: 'aembedph-play-external',
|
||||
},
|
||||
child: {
|
||||
tag: 'a',
|
||||
attrs: {
|
||||
className: 'aembedph-play-external-link',
|
||||
href: metadata.url,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
},
|
||||
child: ('or listen on ' + metadata.site_name + '?')
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
pub.getElement = function() { return elem; };
|
||||
pub.appendTo = function(target) { target.appendChild(elem); };
|
||||
pub.insertBefore = function(ref) { $ib(ref, elem); };
|
||||
pub.nuke = function() {
|
||||
$r(elem);
|
||||
elem = null;
|
||||
};
|
||||
pub.replaceElement = function(target) {
|
||||
$ib(target, elem);
|
||||
$r(target);
|
||||
};
|
||||
|
||||
return pub;
|
||||
}
|
|
@ -97,7 +97,10 @@ var MszEmbed = (function() {
|
|||
options.maxWidth = 640;
|
||||
options.maxHeight = 360;
|
||||
} else if(metadata.is_audio) {
|
||||
phc = MszAudioEmbedPlaceholder;
|
||||
options.type = 'external';
|
||||
options.player = MszAudioEmbedPlayer;
|
||||
options.nativeControls = true;
|
||||
} else if(metadata.is_image) {
|
||||
options.type = 'external';
|
||||
}
|
||||
|
|
|
@ -81,6 +81,61 @@ const MszVideoEmbed = function(playerOrFrame) {
|
|||
const MszVideoEmbedFrame = function(player, options) {
|
||||
options = options || {};
|
||||
|
||||
const icoStatePlay = 'fa-play',
|
||||
icoStatePause = 'fa-pause',
|
||||
icoStateStop = 'fa-stop';
|
||||
const icoVolMute = 'fa-volume-mute',
|
||||
icoVolOff = 'fa-volume-off',
|
||||
icoVolQuiet = 'fa-volume-down',
|
||||
icoVolLoud = 'fa-volume-up';
|
||||
|
||||
const btnPlayPause = $e({
|
||||
attrs: {},
|
||||
child: {
|
||||
tag: 'i',
|
||||
attrs: {
|
||||
classList: ['fas', 'fa-fw', icoStatePlay],
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const btnStop = $e({
|
||||
attrs: {},
|
||||
child: {
|
||||
tag: 'i',
|
||||
attrs: {
|
||||
classList: ['fas', 'fa-fw', icoStateStop],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const numCurrentTime = $e({
|
||||
attrs: {},
|
||||
});
|
||||
|
||||
const sldProgress = $e({
|
||||
attrs: {},
|
||||
child: [],
|
||||
});
|
||||
|
||||
const numDurationRemaining = $e({
|
||||
attrs: {},
|
||||
});
|
||||
|
||||
const btnVolMute = $e({
|
||||
attrs: {},
|
||||
child: {
|
||||
tag: 'i',
|
||||
attrs: {
|
||||
// isMuted === icoVolMute
|
||||
// vol < 0.01 === icoVolOff
|
||||
// vol < 0.5 === icoVolQuiet
|
||||
// vol < 1.0 === icoVolLoud
|
||||
classList: ['fas', 'fa-fw', icoVolLoud],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const elem = $e({
|
||||
attrs: {
|
||||
className: 'embedvf',
|
||||
|
@ -106,7 +161,11 @@ const MszVideoEmbedFrame = function(player, options) {
|
|||
className: 'embedvf-controls',
|
||||
},
|
||||
child: [
|
||||
'Play/Pause Stop [|---------] 1.00x 100%',
|
||||
btnPlayPause,
|
||||
btnStop,
|
||||
numCurrentTime,
|
||||
sldProgress,
|
||||
numDurationRemaining,
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -728,7 +787,6 @@ const MszVideoEmbedPlaceholder = function(metadata, options) {
|
|||
|
||||
const elem = $e({
|
||||
attrs: {
|
||||
href: 'javascript:void(0);',
|
||||
className: ('embedph embedph-' + (options.type || 'external')),
|
||||
style: style.join(';'),
|
||||
},
|
||||
|
@ -821,7 +879,7 @@ const MszVideoEmbedPlaceholder = function(metadata, options) {
|
|||
child: ('or watch on ' + metadata.site_name + '?'),
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -13,13 +13,11 @@ final class AudioTag extends BBCodeTag {
|
|||
if(empty($url['scheme']) || !in_array(mb_strtolower($url['scheme']), ['http', 'https'], true))
|
||||
return $matches[0];
|
||||
|
||||
// return sprintf(
|
||||
// '<span class="js-msz-embed-media" data-msz-embed-url="%1$s"></span>'
|
||||
// . '<noscript><a href="%1$s" class="link" rel="noopener noreferrer">%1$s</a></noscript>',
|
||||
// $matches[1]
|
||||
// );
|
||||
|
||||
return "<audio controls src='{$matches[1]}'></audio>";
|
||||
return sprintf(
|
||||
'<span class="js-msz-embed-media" data-msz-embed-url="%1$s"></span>'
|
||||
. '<noscript><a href="%1$s" class="link" rel="noopener noreferrer">%1$s</a></noscript>',
|
||||
$matches[1]
|
||||
);
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue