updated plugin AudioIgniter
version 2.0.0
This commit is contained in:
File diff suppressed because one or more lines are too long
@ -22,6 +22,8 @@
|
||||
* http://api.jqueryui.com/category/ui-core/
|
||||
*/
|
||||
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.min.js
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
<!doctype html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>AudioIgniter</title><style>body {
|
||||
padding-bottom: 120px;
|
||||
}</style><script defer="defer" src="style.js"></script><script defer="defer" src="app.js"></script><link href="style.css" rel="stylesheet"></head><body><div id="audioigniter-2799" class="audioigniter-root" data-player-type="full" data-tracks-url="/dev-tracks.json" data-display-active-cover="true" data-display-tracklist-covers="true" data-display-credits="true" data-display-tracklist="true" data-allow-tracklist-toggle="true" data-allow-tracklist-loop="true" data-allow-track-loop="true" data-allow-playback-rate="true" data-display-track-no="true" data-display-artist-names="true" data-display-buy-buttons="true" data-buy-buttons-target="true" data-volume="50" data-cycle-tracks="true" data-limit-tracklist-height="true" data-tracklist-height="185" data-reverse-track-order="false" data-skip-amount="15" data-max-width="600px" data-initial-track="1" data-stop-on-finish="false" data-tracks-delay="5" data-timer-countdown="false" data-shuffle="true" data-shuffle-default="false" data-soundcloud-client-id="" data-remember-last="true" data-player-buttons='[
|
||||
}</style><script defer="defer" src="style.js"></script><script defer="defer" src="app.js"></script><link href="style.css" rel="stylesheet"></head><body><div id="audioigniter-01" class="audioigniter-root" data-player-type="full" data-tracks-url="/dev-tracks.json" data-display-active-cover="true" data-display-tracklist-covers="true" data-display-credits="true" data-display-tracklist="true" data-allow-tracklist-toggle="true" data-allow-tracklist-loop="true" data-allow-track-loop="true" data-allow-playback-rate="true" data-display-track-no="true" data-display-artist-names="true" data-display-buy-buttons="true" data-buy-buttons-target="true" data-volume="50" data-cycle-tracks="true" data-limit-tracklist-height="true" data-tracklist-height="185" data-reverse-track-order="false" data-skip-amount="15" data-max-width="600px" data-initial-track="1" data-stop-on-finish="true" data-tracks-delay="5" data-timer-countdown="false" data-shuffle="true" data-shuffle-default="true" data-soundcloud-client-id="" data-remember-last="true" data-player-buttons='[
|
||||
{
|
||||
"title": "CSSIgniter",
|
||||
"url": "https://cssigniter.com",
|
||||
@ -17,7 +17,7 @@
|
||||
"icon": ""
|
||||
}
|
||||
]
|
||||
'></div><div id="audioigniter-2999" class="audioigniter-root" data-player-type="simple" data-tracks-url="/dev-tracks.json" data-display-credits="true" data-display-track-no="true" data-allow-playback-rate="true" data-display-artist-names="true" data-display-buy-buttons="true" data-buy-buttons-target="true" data-allow-track-loop="true" data-volume="50" data-reverse-track-order="false" data-max-width="600px" data-initial-track="3" data-stop-on-finish="false" data-tracks-delay="0" data-timer-countdown="false" data-shuffle="true" data-soundcloud-client-id="" data-player-buttons='[
|
||||
'></div><div id="audioigniter-02" class="audioigniter-root" data-player-type="simple" data-tracks-url="/dev-tracks.json" data-display-credits="true" data-display-track-no="true" data-allow-playback-rate="true" data-display-artist-names="true" data-display-buy-buttons="true" data-buy-buttons-target="true" data-allow-track-loop="true" data-volume="50" data-reverse-track-order="false" data-max-width="600px" data-initial-track="3" data-stop-on-finish="false" data-tracks-delay="0" data-timer-countdown="false" data-shuffle="true" data-soundcloud-client-id="" data-player-buttons='[
|
||||
{
|
||||
"title": "CSSIgniter",
|
||||
"url": "https://cssigniter.com",
|
||||
@ -34,7 +34,7 @@
|
||||
"icon": ""
|
||||
}
|
||||
]
|
||||
'></div><div id="audioigniter-2519" class="audioigniter-root" data-track='{"title":"Sunrise","subtitle":"Thoribass","audio":"https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3","buyUrl":"https:\/\/google.com","downloadUrl":"https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3","cover":"https:\/\/www.cssigniter.com\/demos\/audioigniter\/wp-content\/uploads\/sites\/48\/2016\/08\/CyberSDF-Flame-and-Go.jpg","lyrics":"Some lyrics"}' data-display-active-cover="false" data-display-credits="true" data-allow-playback-rate="false" data-display-buy-buttons="true" data-volume="100" data-skip-amount="0" data-stop-on-finish="true" data-timer-countdown="false" data-player-type="full" data-tracks-url="" data-display-tracklist="false" data-allow-tracklist-toggle="true" data-allow-tracklist-loop="true" data-allow-track-loop="true" data-display-track-no="false" data-display-artist-names="true" data-buy-buttons-target="true" data-cycle-tracks="false" data-limit-tracklist-height="false" data-tracklist-height="185" data-reverse-track-order="false" data-max-width="600px" data-initial-track="1" data-tracks-delay="0" data-shuffle="false" data-shuffle-default="false" data-remember-last="false"></div><div id="audioigniter-8999" class="audioigniter-root" data-player-type="global-footer" data-tracks-url="/dev-tracks.json" data-display-active-cover="true" data-display-tracklist-covers="true" data-display-credits="true" data-display-tracklist="false" data-allow-tracklist-toggle="true" data-allow-tracklist-loop="true" data-allow-track-loop="true" data-display-track-no="true" data-allow-playback-rate="true" data-display-artist-names="true" data-display-buy-buttons="true" data-buy-buttons-target="true" data-volume="50" data-skip-amount="15" data-cycle-tracks="false" data-limit-tracklist-height="true" data-tracklist-height="185" data-reverse-track-order="false" data-max-width="600px" data-initial-track="1" data-stop-on-finish="false" data-tracks-delay="0" data-timer-countdown="true" data-shuffle="true" data-soundcloud-client-id="" data-player-buttons='[
|
||||
'></div><div id="audioigniter-03" class="audioigniter-root" data-track='{"title":"Sunrise","subtitle":"Thoribass","audio":"https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3","buyUrl":"https:\/\/google.com","downloadUrl":"https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3","cover":"https:\/\/www.cssigniter.com\/demos\/audioigniter\/wp-content\/uploads\/sites\/48\/2016\/08\/CyberSDF-Flame-and-Go.jpg","lyrics":"Some lyrics"}' data-display-active-cover="false" data-display-credits="true" data-allow-playback-rate="false" data-display-buy-buttons="true" data-volume="100" data-skip-amount="0" data-stop-on-finish="true" data-timer-countdown="false" data-player-type="full" data-tracks-url="" data-display-tracklist="false" data-allow-tracklist-toggle="true" data-allow-tracklist-loop="true" data-allow-track-loop="true" data-display-track-no="false" data-display-artist-names="true" data-buy-buttons-target="true" data-cycle-tracks="false" data-limit-tracklist-height="false" data-tracklist-height="185" data-reverse-track-order="false" data-max-width="600px" data-initial-track="1" data-tracks-delay="0" data-shuffle="false" data-shuffle-default="false" data-remember-last="false"></div><div id="audioigniter-04" class="audioigniter-root" data-player-type="global-footer" data-tracks-url="/dev-tracks.json" data-display-active-cover="true" data-display-tracklist-covers="true" data-display-credits="true" data-display-tracklist="false" data-allow-tracklist-toggle="true" data-allow-tracklist-loop="true" data-allow-track-loop="true" data-display-track-no="true" data-allow-playback-rate="true" data-display-artist-names="true" data-display-buy-buttons="true" data-buy-buttons-target="true" data-volume="50" data-skip-amount="15" data-cycle-tracks="false" data-limit-tracklist-height="true" data-tracklist-height="185" data-reverse-track-order="false" data-max-width="600px" data-initial-track="1" data-stop-on-finish="false" data-tracks-delay="0" data-timer-countdown="true" data-shuffle="true" data-soundcloud-client-id="" data-remember-last="true" data-player-buttons='[
|
||||
{
|
||||
"title": "CSSIgniter",
|
||||
"url": "https://cssigniter.com",
|
||||
|
File diff suppressed because one or more lines are too long
@ -5,6 +5,7 @@
|
||||
"audio": "https://www.cssigniter.com/assets/audioigniter/sunrise.mp3",
|
||||
"buyUrl": "https://www.cssigniter.com",
|
||||
"downloadUrl": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3",
|
||||
"downloadFilename": "sunrise.mp3",
|
||||
"cover": "https://www.cssigniter.com/demos/audioigniter/wp-content/uploads/sites/48/2016/08/CyberSDF-Flame-and-Go.jpg",
|
||||
"lyrics": "Here in my mind\nYou know you might find\nSomething that you\n\nYou thought you once knew\nBut now it's all gone\nAnd you know it's no fun\n\nYeah I know it's no fun\nOh I know it's no fun\nI'm free to be whatever I\nWhatever I choose\nAnd I'll sing the blues if I want\nI'm free to be whatever I\nWhatever I choose\nAnd I'll sing the blues if I want\nWhatever you do\nWhatever you say\nYeah I know it's alright"
|
||||
},
|
||||
@ -13,6 +14,7 @@
|
||||
"subtitle": "The Fisherman",
|
||||
"audio": "https://www.cssigniter.com/assets/audioigniter/sunrise.mp3",
|
||||
"buyUrl": "https://www.cssigniter.com",
|
||||
"downloadFilename": "fisherman.mp3",
|
||||
"downloadUrl": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3",
|
||||
"cover": "https://www.cssigniter.com/demos/audioigniter/wp-content/uploads/sites/48/2016/08/Thoribass-Sunrise.jpg"
|
||||
},
|
||||
@ -32,13 +34,6 @@
|
||||
"downloadUrl": "https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3",
|
||||
"cover": ""
|
||||
},
|
||||
{
|
||||
"title": "Is It Because I'm Black (David August Reconstruction)",
|
||||
"subtitle": "Syl Johnson (SoundCloud)",
|
||||
"audio": "https://soundcloud.com/enterofficial/maceo-plex-b2b-richie-hawtin-enterweek-11-sake-bar-space-ibiza-september-10th-2015",
|
||||
"buyUrl": "",
|
||||
"cover": "https://www.cssigniter.com/demos/audioigniter/wp-content/uploads/sites/48/2016/08/Rocavaco-Remix-Safety-Guide.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Deep House Radio",
|
||||
"subtitle": "",
|
||||
|
@ -58,6 +58,7 @@
|
||||
"webpack-merge": "^4.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fingerprintjs/fingerprintjs": "^3.3.6",
|
||||
"classnames": "2.3.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^18.2.0",
|
||||
|
@ -13,7 +13,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="audioigniter-2799"
|
||||
id="audioigniter-01"
|
||||
class="audioigniter-root"
|
||||
data-player-type="full"
|
||||
data-tracks-url="/dev-tracks.json"
|
||||
@ -37,11 +37,11 @@
|
||||
data-skip-amount="15"
|
||||
data-max-width="600px"
|
||||
data-initial-track="1"
|
||||
data-stop-on-finish="false"
|
||||
data-stop-on-finish="true"
|
||||
data-tracks-delay="5"
|
||||
data-timer-countdown="false"
|
||||
data-shuffle="true"
|
||||
data-shuffle-default="false"
|
||||
data-shuffle-default="true"
|
||||
data-soundcloud-client-id=""
|
||||
data-remember-last="true"
|
||||
data-player-buttons='[
|
||||
@ -65,7 +65,7 @@
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="audioigniter-2999"
|
||||
id="audioigniter-02"
|
||||
class="audioigniter-root"
|
||||
data-player-type="simple"
|
||||
data-tracks-url="/dev-tracks.json"
|
||||
@ -106,7 +106,7 @@
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="audioigniter-2519"
|
||||
id="audioigniter-03"
|
||||
class="audioigniter-root"
|
||||
|
||||
data-track='{"title":"Sunrise","subtitle":"Thoribass","audio":"https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3","buyUrl":"https:\/\/google.com","downloadUrl":"https:\/\/www.cssigniter.com\/assets\/audioigniter\/sunrise.mp3","cover":"https:\/\/www.cssigniter.com\/demos\/audioigniter\/wp-content\/uploads\/sites\/48\/2016\/08\/CyberSDF-Flame-and-Go.jpg","lyrics":"Some lyrics"}'
|
||||
@ -141,7 +141,7 @@
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="audioigniter-8999"
|
||||
id="audioigniter-04"
|
||||
class="audioigniter-root"
|
||||
data-player-type="global-footer"
|
||||
data-tracks-url="/dev-tracks.json"
|
||||
@ -170,6 +170,7 @@
|
||||
data-timer-countdown="true"
|
||||
data-shuffle="true"
|
||||
data-soundcloud-client-id=""
|
||||
data-remember-last="true"
|
||||
data-player-buttons='[
|
||||
{
|
||||
"title": "CSSIgniter",
|
||||
|
@ -25,6 +25,10 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
skip_backward: 'Skip backward',
|
||||
shuffle: 'Shuffle',
|
||||
};
|
||||
|
||||
window.aiStats = {
|
||||
apiUrl: '',
|
||||
};
|
||||
}
|
||||
|
||||
const nodes = document.getElementsByClassName('audioigniter-root');
|
||||
@ -77,7 +81,7 @@ function renderApp(node) {
|
||||
initialTrack: parseInt(node.getAttribute('data-initial-track'), 10),
|
||||
delayBetweenTracks: parseInt(node.getAttribute('data-tracks-delay'), 10),
|
||||
stopOnTrackFinish: JSON.parse(node.getAttribute('data-stop-on-finish')),
|
||||
defaultShuffle: JSON.parse(node.getAttribute('data-shuffle')),
|
||||
defaultShuffle: JSON.parse(node.getAttribute('data-shuffle-default')),
|
||||
shuffleEnabled: JSON.parse(node.getAttribute('data-shuffle')),
|
||||
countdownTimerByDefault: JSON.parse(
|
||||
node.getAttribute('data-timer-countdown'),
|
||||
|
@ -73,6 +73,7 @@ const propTypes = {
|
||||
icon: PropTypes.string,
|
||||
}).isRequired,
|
||||
),
|
||||
playerId: PropTypes.string,
|
||||
};
|
||||
|
||||
const GlobalFooterPlayer = ({
|
||||
@ -83,6 +84,7 @@ const GlobalFooterPlayer = ({
|
||||
position,
|
||||
duration,
|
||||
playbackRate,
|
||||
playerId,
|
||||
|
||||
currentTrack,
|
||||
playTrack,
|
||||
@ -312,6 +314,7 @@ const GlobalFooterPlayer = ({
|
||||
onTrackClick={playTrack}
|
||||
onTrackLoop={allowTrackLoop ? setTrackCycling : undefined}
|
||||
repeatingTrackIndex={repeatingTrackIndex}
|
||||
playerId={playerId}
|
||||
/>
|
||||
|
||||
{playerButtons?.length > 0 && <PlayerButtons buttons={playerButtons} />}
|
||||
|
@ -80,10 +80,12 @@ const propTypes = {
|
||||
icon: PropTypes.string,
|
||||
}).isRequired,
|
||||
),
|
||||
playerId: PropTypes.string,
|
||||
};
|
||||
|
||||
const Player = ({
|
||||
tracks,
|
||||
playerId,
|
||||
playStatus,
|
||||
activeIndex,
|
||||
volume,
|
||||
@ -345,6 +347,7 @@ const Player = ({
|
||||
onTrackClick={playTrack}
|
||||
onTrackLoop={allowTrackLoop ? setTrackCycling : undefined}
|
||||
repeatingTrackIndex={repeatingTrackIndex}
|
||||
playerId={playerId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -10,6 +10,7 @@ import PlayerButtons from './components/PlayerButtons';
|
||||
|
||||
const propTypes = {
|
||||
tracks: PropTypes.arrayOf(PropTypes.object),
|
||||
playerId: PropTypes.string,
|
||||
playStatus: PropTypes.oneOf([
|
||||
Sound.status.PLAYING,
|
||||
Sound.status.PAUSED,
|
||||
@ -82,6 +83,7 @@ const SimplePlayer = props => {
|
||||
setPlaybackRate={props.setPlaybackRate}
|
||||
allowPlaybackRate={props.allowPlaybackRate}
|
||||
buffering={props.buffering}
|
||||
playerId={props.playerId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -16,6 +16,10 @@ const Time = ({ countdown, position, duration }) => {
|
||||
* @returns {string} - Time pretty formatted
|
||||
*/
|
||||
const renderFormattedTime = () => {
|
||||
if (!duration) {
|
||||
return '00:00';
|
||||
}
|
||||
|
||||
const positionInSeconds = showRemaining
|
||||
? (duration - position) / 1000
|
||||
: position / 1000;
|
||||
@ -27,7 +31,7 @@ const Time = ({ countdown, position, duration }) => {
|
||||
min = min >= 10 ? min : `0${min}`;
|
||||
sec = sec >= 10 ? sec : `0${sec}`;
|
||||
|
||||
if (!Number.isNaN(sec)) {
|
||||
if (Number.isInteger(parseInt(sec, 10))) {
|
||||
if (hours) {
|
||||
time = `${hours}:${min}:${sec}`;
|
||||
} else {
|
||||
@ -39,6 +43,10 @@ const Time = ({ countdown, position, duration }) => {
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
if (!duration) {
|
||||
return;
|
||||
}
|
||||
|
||||
setShowRemaining(x => !x);
|
||||
};
|
||||
|
||||
|
@ -46,6 +46,7 @@ const propTypes = {
|
||||
setPlaybackRate: PropTypes.func,
|
||||
allowPlaybackRate: PropTypes.bool,
|
||||
buffering: PropTypes.bool,
|
||||
playerId: PropTypes.string,
|
||||
};
|
||||
|
||||
const Track = ({
|
||||
@ -70,6 +71,7 @@ const Track = ({
|
||||
setPlaybackRate,
|
||||
allowPlaybackRate,
|
||||
buffering,
|
||||
playerId,
|
||||
}) => {
|
||||
const { toggleLyricsModal } = useContext(AppContext);
|
||||
const isPlaying = isActive && playStatus === Sound.status.PLAYING;
|
||||
@ -125,6 +127,7 @@ const Track = ({
|
||||
|
||||
<TrackButtons
|
||||
buyButtonsTarget={buyButtonsTarget}
|
||||
track={track}
|
||||
buyUrl={track.buyUrl}
|
||||
downloadUrl={track.downloadUrl}
|
||||
downloadFilename={track.downloadFilename}
|
||||
@ -138,6 +141,7 @@ const Track = ({
|
||||
setPlaybackRate={setPlaybackRate}
|
||||
allowPlaybackRate={allowPlaybackRate}
|
||||
isPlaying={isPlaying}
|
||||
playerId={playerId}
|
||||
/>
|
||||
|
||||
{hasProgressBar && (
|
||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { CartIcon, DownloadIcon, LyricsIcon, RefreshIcon } from './Icons';
|
||||
import events, { EVENT, normalizePlayerId } from '../services/events';
|
||||
|
||||
const propTypes = {
|
||||
buyButtonsTarget: PropTypes.bool,
|
||||
@ -16,6 +17,10 @@ const propTypes = {
|
||||
setPlaybackRate: PropTypes.func,
|
||||
allowPlaybackRate: PropTypes.bool,
|
||||
isPlaying: PropTypes.bool,
|
||||
track: PropTypes.shape({
|
||||
audio: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
playerId: PropTypes.string,
|
||||
};
|
||||
|
||||
const TrackButtons = ({
|
||||
@ -31,6 +36,8 @@ const TrackButtons = ({
|
||||
playbackRate,
|
||||
allowPlaybackRate,
|
||||
isPlaying,
|
||||
track,
|
||||
playerId,
|
||||
}) => {
|
||||
if (
|
||||
buyUrl == null &&
|
||||
@ -64,6 +71,13 @@ const TrackButtons = ({
|
||||
download={downloadFilename}
|
||||
className="ai-track-btn"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
events.eventTrack({
|
||||
event: EVENT.DOWNLOAD,
|
||||
trackUrl: track.audio,
|
||||
playerId: normalizePlayerId(playerId),
|
||||
});
|
||||
}}
|
||||
aria-label={aiStrings.download_track}
|
||||
title={aiStrings.download_track}
|
||||
>
|
||||
|
@ -31,6 +31,7 @@ const propTypes = {
|
||||
allowPlaybackRate: PropTypes.bool,
|
||||
buffering: PropTypes.bool,
|
||||
repeatingTrackIndex: PropTypes.bool,
|
||||
playerId: PropTypes.string,
|
||||
};
|
||||
|
||||
const Tracklist = ({ ...props }) => {
|
||||
@ -69,6 +70,7 @@ const Tracklist = ({ ...props }) => {
|
||||
setPlaybackRate={props.setPlaybackRate}
|
||||
allowPlaybackRate={props.allowPlaybackRate}
|
||||
buffering={props.buffering}
|
||||
playerId={props.playerId}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@ -21,6 +21,7 @@ const propTypes = {
|
||||
displayArtistNames: PropTypes.bool,
|
||||
onTrackLoop: PropTypes.func,
|
||||
repeatingTrackIndex: PropTypes.number,
|
||||
playerId: PropTypes.string,
|
||||
};
|
||||
|
||||
const TracklistWrap = ({
|
||||
@ -40,6 +41,7 @@ const TracklistWrap = ({
|
||||
displayCovers,
|
||||
displayArtistNames,
|
||||
repeatingTrackIndex,
|
||||
playerId,
|
||||
}) => {
|
||||
const scrollbarRef = useRef(null);
|
||||
|
||||
@ -82,6 +84,7 @@ const TracklistWrap = ({
|
||||
displayArtistNames={displayArtistNames}
|
||||
onTrackLoop={onTrackLoop}
|
||||
repeatingTrackIndex={repeatingTrackIndex}
|
||||
playerId={playerId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,82 +0,0 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
|
||||
const propTypes = {
|
||||
position: PropTypes.number.isRequired,
|
||||
duration: PropTypes.number.isRequired,
|
||||
audio: PropTypes.string,
|
||||
setPosition: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const WaveformProgressBar = ({ audio, position, duration, setPosition }) => {
|
||||
const waveFormDomRef = useRef(null);
|
||||
const wavesurfer = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (waveFormDomRef.current && audio) {
|
||||
wavesurfer.current = WaveSurfer.create({
|
||||
container: waveFormDomRef.current,
|
||||
mediaControls: false,
|
||||
height: 40,
|
||||
barWidth: 2,
|
||||
barGap: 2,
|
||||
barRadius: 3,
|
||||
responsive: true,
|
||||
cursorWidth: 0,
|
||||
backgroundColor: 'transparent',
|
||||
progressColor: '#f70f5d',
|
||||
waveColor: '#fff',
|
||||
xhr: {
|
||||
mode: 'no-cors',
|
||||
},
|
||||
});
|
||||
wavesurfer.current.load(audio);
|
||||
wavesurfer.current.on('ready', () => {
|
||||
console.log('wavesurfer loaded');
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (wavesurfer.current) {
|
||||
wavesurfer.current.destroy();
|
||||
}
|
||||
};
|
||||
}, [audio, waveFormDomRef.current]);
|
||||
|
||||
useEffect(() => {
|
||||
// Sync wavesurfer with current playing position
|
||||
const progress = position / duration;
|
||||
|
||||
if (wavesurfer.current && !Number.isNaN(progress)) {
|
||||
wavesurfer.current.seekTo(progress || 0);
|
||||
}
|
||||
}, [position]);
|
||||
|
||||
const handleClick = event => {
|
||||
if (setPosition == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const offsetX =
|
||||
event.pageX - event.currentTarget.getBoundingClientRect().left;
|
||||
const posX = offsetX / event.currentTarget.offsetWidth;
|
||||
|
||||
setPosition(posX * duration);
|
||||
};
|
||||
|
||||
if (!audio) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ai-waveform-bar" onClick={handleClick}>
|
||||
<div className="ai-waveform" ref={waveFormDomRef} />
|
||||
<div className="ai-waveform-progress" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
WaveformProgressBar.propTypes = propTypes;
|
||||
|
||||
export default WaveformProgressBar;
|
@ -0,0 +1,144 @@
|
||||
/* global aiStats */
|
||||
|
||||
import isStreamTrack from '../../utils/isStreamTrack';
|
||||
|
||||
/**
|
||||
* @enum EVENT
|
||||
* @type {{PAUSE: string, PLAY: string, STOP: string, DOWNLOAD: string, SEEK: string}}
|
||||
*/
|
||||
export const EVENT = {
|
||||
PLAY: 'PLAY',
|
||||
PLAYING: 'PLAYING',
|
||||
PAUSE: 'PAUSE',
|
||||
STOP: 'STOP',
|
||||
SEEK: 'SEEK',
|
||||
DOWNLOAD: 'DOWNLOAD',
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalizes a player ID.
|
||||
*
|
||||
* @param {String} playerId The player ID
|
||||
* @returns {string|null}
|
||||
*/
|
||||
export const normalizePlayerId = playerId => {
|
||||
return playerId?.replace('audioigniter-', '') ?? null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes state and props from soundProvider and returns the formatted event data.
|
||||
* @param state
|
||||
* @param props
|
||||
* @returns {{duration, position, trackUrl: *, playerId: *}}
|
||||
*/
|
||||
export const getEventMeta = (state, props) => {
|
||||
const { activeIndex, tracks, position, duration } = state;
|
||||
const { playerId } = props;
|
||||
const track = tracks[activeIndex];
|
||||
const { title, subtitle, audio } = track ?? {};
|
||||
|
||||
return {
|
||||
trackUrl: audio,
|
||||
// trackName: subtitle ? `${title} - ${subtitle}` : title,
|
||||
trackTitle: title,
|
||||
trackArtist: subtitle ?? '',
|
||||
playerId: normalizePlayerId(playerId),
|
||||
position,
|
||||
duration,
|
||||
isStream: isStreamTrack(audio),
|
||||
};
|
||||
};
|
||||
|
||||
class AudioIgniterEvents {
|
||||
constructor() {
|
||||
this.clientId = null;
|
||||
this.queue = [];
|
||||
|
||||
if (!window.aiStats?.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventQueueTimer();
|
||||
this.initializeFingerprint();
|
||||
|
||||
// Flush the entire queue when the user ends their session.
|
||||
window.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
this.eventQueueFlush();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initializeFingerprint = async () => {
|
||||
const FingerprintJS = await import(/* webpackChunkName: "fingerprintjs" */ '@fingerprintjs/fingerprintjs');
|
||||
const fingerprint = await FingerprintJS.load();
|
||||
const result = await fingerprint.get();
|
||||
this.clientId = result.visitorId;
|
||||
};
|
||||
|
||||
fetch = async () => {
|
||||
const headers = {
|
||||
type: 'application/json',
|
||||
};
|
||||
const blob = new Blob([JSON.stringify(this.queue)], headers);
|
||||
navigator.sendBeacon(`${aiStats.apiUrl}/log`, blob);
|
||||
};
|
||||
|
||||
eventQueueTimer = () => {
|
||||
setInterval(() => {
|
||||
if (this.queue.length > 0) {
|
||||
this.eventQueueFlush();
|
||||
}
|
||||
}, 15000);
|
||||
};
|
||||
|
||||
eventQueueFlush = async () => {
|
||||
await this.fetch();
|
||||
this.queue = [];
|
||||
};
|
||||
|
||||
eventTrack = ({
|
||||
event,
|
||||
trackUrl,
|
||||
// trackName,
|
||||
trackTitle,
|
||||
trackArtist,
|
||||
playerId,
|
||||
position,
|
||||
oldPosition,
|
||||
duration,
|
||||
isStream,
|
||||
}) => {
|
||||
if (!window.aiStats?.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Failsafe for multi sound pausing, some tracks
|
||||
// can be paused before they start due to external
|
||||
// soundManager pausing (see playTrack event in soundProvider.js).
|
||||
if (event === EVENT.PAUSE && position === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.queue.push({
|
||||
event,
|
||||
track_url: trackUrl,
|
||||
// track_name: trackName,
|
||||
track_title: trackTitle,
|
||||
track_artist: trackArtist,
|
||||
playlist_id: parseInt(playerId, 10),
|
||||
timestamp: new Date().getTime(),
|
||||
referrer_url: window.location.href,
|
||||
event_data: {
|
||||
position: Math.floor(position / 1000) ?? null,
|
||||
old_position:
|
||||
oldPosition != null ? Math.floor(oldPosition / 1000) : null,
|
||||
duration: duration ? Math.floor(duration / 1000) : null,
|
||||
},
|
||||
client_fingerprint: this.clientId,
|
||||
is_stream: isStream,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default new AudioIgniterEvents();
|
@ -6,6 +6,8 @@ import SoundCloud from '../utils/soundcloud';
|
||||
import multiSoundDisabled from '../utils/multi-sound-disabled';
|
||||
import { getInitialTrackQueueAndIndex } from '../utils/getInitialTrackIndex';
|
||||
import playerStorage from '../utils/playerStorage';
|
||||
import aiEvents, { EVENT, getEventMeta } from './services/events';
|
||||
import throttle from '../utils/throttle';
|
||||
|
||||
const PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2, 3];
|
||||
|
||||
@ -61,6 +63,9 @@ const soundProvider = (Player, events) => {
|
||||
this.getFinalProps = this.getFinalProps.bind(this);
|
||||
this.onPlaying = this.onPlaying.bind(this);
|
||||
this.onFinishedPlaying = this.onFinishedPlaying.bind(this);
|
||||
this.aiEventTrackThrottled = throttle(options => {
|
||||
aiEvents.eventTrack(options);
|
||||
}, 60 * 1000);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -166,6 +171,14 @@ const soundProvider = (Player, events) => {
|
||||
const { activeIndex } = this.state;
|
||||
const { playerId, rememberLastPosition } = this.props;
|
||||
|
||||
if (position > 60000) {
|
||||
// Only start calling this after 1 minute into the track
|
||||
this.aiEventTrackThrottled({
|
||||
event: EVENT.PLAYING,
|
||||
...getEventMeta(this.state, this.props),
|
||||
});
|
||||
}
|
||||
|
||||
this.setState(
|
||||
() => ({ duration, position }),
|
||||
() => {
|
||||
@ -173,8 +186,12 @@ const soundProvider = (Player, events) => {
|
||||
events.onPlaying(this.getFinalProps());
|
||||
}
|
||||
|
||||
// Store last position every 5 seconds
|
||||
if (playerId && rememberLastPosition && position % 5000 < 300) {
|
||||
if (
|
||||
playerId &&
|
||||
rememberLastPosition &&
|
||||
// Store last position on every 5th second or at the beginning of the track (tiny position num).
|
||||
(position % 5000 < 300 || position < 350)
|
||||
) {
|
||||
playerStorage.set(playerId, {
|
||||
position,
|
||||
activeIndex,
|
||||
@ -187,7 +204,15 @@ const soundProvider = (Player, events) => {
|
||||
onFinishedPlaying() {
|
||||
const { stopOnTrackFinish, delayBetweenTracks = 0 } = this.props;
|
||||
const delayBetweenTracksMs = delayBetweenTracks * 1000;
|
||||
this.setState(() => ({ playStatus: Sound.status.STOPPED }));
|
||||
this.setState(
|
||||
() => ({ playStatus: Sound.status.STOPPED }),
|
||||
() => {
|
||||
aiEvents.eventTrack({
|
||||
event: EVENT.STOP,
|
||||
...getEventMeta(this.state, this.props),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
if (stopOnTrackFinish) {
|
||||
return;
|
||||
@ -228,7 +253,18 @@ const soundProvider = (Player, events) => {
|
||||
}
|
||||
|
||||
setPosition(position) {
|
||||
this.setState(() => ({ position }));
|
||||
const currentPosition = this.state.position;
|
||||
|
||||
this.setState(
|
||||
() => ({ position }),
|
||||
() => {
|
||||
aiEvents.eventTrack({
|
||||
event: EVENT.SEEK,
|
||||
...getEventMeta(this.state, this.props),
|
||||
oldPosition: currentPosition,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setTrackCycling(index, event) {
|
||||
@ -312,19 +348,44 @@ const soundProvider = (Player, events) => {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
const { repeatingTrackIndex, isMultiSoundDisabled } = this.state;
|
||||
const {
|
||||
repeatingTrackIndex,
|
||||
isMultiSoundDisabled,
|
||||
playStatus,
|
||||
} = this.state;
|
||||
|
||||
if (isMultiSoundDisabled) {
|
||||
window.soundManager.pauseAll();
|
||||
}
|
||||
|
||||
this.setState(() => ({
|
||||
activeIndex: index,
|
||||
position: 0,
|
||||
playStatus: Sound.status.PLAYING,
|
||||
}));
|
||||
if (playStatus === Sound.status.PLAYING) {
|
||||
aiEvents.eventTrack({
|
||||
event: EVENT.STOP,
|
||||
...getEventMeta(this.state, this.props),
|
||||
});
|
||||
}
|
||||
|
||||
// Reset repating track index if the track is not the active one.
|
||||
this.setState(
|
||||
() => ({
|
||||
activeIndex: index,
|
||||
position: 0,
|
||||
playStatus: Sound.status.PLAYING,
|
||||
}),
|
||||
() => {
|
||||
aiEvents.eventTrack({
|
||||
event: EVENT.PLAY,
|
||||
...getEventMeta(
|
||||
{
|
||||
...this.state,
|
||||
duration: null,
|
||||
},
|
||||
this.props,
|
||||
),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Reset repeating track index if the track is not the active one.
|
||||
if (index !== repeatingTrackIndex && repeatingTrackIndex != null) {
|
||||
this.setTrackCycling(null);
|
||||
}
|
||||
@ -338,7 +399,15 @@ const soundProvider = (Player, events) => {
|
||||
const { playStatus } = this.state;
|
||||
|
||||
if (playStatus === Sound.status.PLAYING) {
|
||||
this.setState(() => ({ playStatus: Sound.status.PAUSED }));
|
||||
this.setState(
|
||||
() => ({ playStatus: Sound.status.PAUSED }),
|
||||
() => {
|
||||
aiEvents.eventTrack({
|
||||
event: EVENT.PAUSE,
|
||||
...getEventMeta(this.state, this.props),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,18 +423,29 @@ const soundProvider = (Player, events) => {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState(({ playStatus, isMultiSoundDisabled }) => {
|
||||
if (playStatus !== Sound.status.PLAYING && isMultiSoundDisabled) {
|
||||
window.soundManager.pauseAll();
|
||||
}
|
||||
this.setState(
|
||||
({ playStatus, isMultiSoundDisabled }) => {
|
||||
if (playStatus !== Sound.status.PLAYING && isMultiSoundDisabled) {
|
||||
window.soundManager.pauseAll();
|
||||
}
|
||||
|
||||
return {
|
||||
playStatus:
|
||||
playStatus === Sound.status.PLAYING
|
||||
? Sound.status.PAUSED
|
||||
: Sound.status.PLAYING,
|
||||
};
|
||||
});
|
||||
return {
|
||||
playStatus:
|
||||
playStatus === Sound.status.PLAYING
|
||||
? Sound.status.PAUSED
|
||||
: Sound.status.PLAYING,
|
||||
};
|
||||
},
|
||||
() => {
|
||||
aiEvents.eventTrack({
|
||||
event:
|
||||
this.state.playStatus === Sound.status.PLAYING
|
||||
? EVENT.PLAY
|
||||
: EVENT.PAUSE,
|
||||
...getEventMeta(this.state, this.props),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
nextTrack() {
|
||||
|
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Determines whether a given url is that of a stream or not.
|
||||
*
|
||||
* @param {string} url The url.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isStreamTrack = url => {
|
||||
const extensions = ['.mp3', '.flac', '.amr', '.aac', '.oga', '.wav', '.wma'];
|
||||
return !extensions.some(extension => url.includes(extension));
|
||||
};
|
||||
|
||||
export default isStreamTrack;
|
21
wp-content/plugins/audioigniter/player/src/utils/throttle.js
Normal file
21
wp-content/plugins/audioigniter/player/src/utils/throttle.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Simple throttling function.
|
||||
*
|
||||
* @param {Function} fn The function to throttle.
|
||||
* @param {number} limit The limit in milliseconds.
|
||||
* @returns {(function(*): void)|*}
|
||||
*/
|
||||
const throttle = (fn, limit) => {
|
||||
let waiting = false;
|
||||
return function throttleCallback(...args) {
|
||||
if (!waiting) {
|
||||
fn.apply(this, args);
|
||||
waiting = true;
|
||||
setTimeout(() => {
|
||||
waiting = false;
|
||||
}, limit);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default throttle;
|
@ -255,18 +255,6 @@ $screen-xs-max: 320px !default;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-waveform-bar {
|
||||
display: block;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ai-waveform {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ai-track-time {
|
||||
flex: none;
|
||||
font-size: 13px;
|
||||
|
@ -1011,6 +1011,13 @@
|
||||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@fingerprintjs/fingerprintjs@^3.3.6":
|
||||
version "3.3.6"
|
||||
resolved "https://registry.yarnpkg.com/@fingerprintjs/fingerprintjs/-/fingerprintjs-3.3.6.tgz#4c3f1726dc0cb10b915cfce78d9471b38cd809bd"
|
||||
integrity sha512-Inh0OoFVzO2PLvrUF8RZhY9NVDdg9DJHQ5YlvXhrGtQxSPzy2smS3TWzLAi+zlHSJNHSvi+1zYayLen2lGxjdA==
|
||||
dependencies:
|
||||
tslib "^2.0.1"
|
||||
|
||||
"@humanwhocodes/config-array@^0.9.2":
|
||||
version "0.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"
|
||||
@ -5290,7 +5297,7 @@ tsconfig-paths@^3.14.1:
|
||||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@^2.0.3:
|
||||
tslib@^2.0.1, tslib@^2.0.3:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||
|
Reference in New Issue
Block a user