Created
September 10, 2021 06:28
-
-
Save bingogg14/036fed97de801b702f19ff9c7e2db7f2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import $ from "jquery"; | |
window.jQuery = $ | |
window.$ = $ | |
import { Howl } from 'howler'; | |
import axios from "axios"; | |
export function initSoundcloud() { | |
const SoundCloudClientId = 'f17476445ba4b72bc5760aa679820d27' | |
// Cache references to DOM elements. | |
const elmTracks = $('.player[data-track-id]'); | |
const trackIds = []; | |
elmTracks.each(function () { | |
const trackId = $(this).data('trackId'); | |
if (typeof trackId !== 'undefined' && trackId !== '') { | |
if (!trackIds.includes(trackId)) { | |
trackIds.push(trackId) | |
} | |
} | |
}) | |
if (trackIds.length > 0) { | |
const trackObjs = []; | |
trackIds.forEach(function ($value) { | |
trackObjs.push({ | |
trackId: $value, | |
howl: null | |
}) | |
}) | |
// Setup our new audio player class and pass it the playlist. | |
const Player = function (playlist) { | |
this.playlist = playlist; | |
this.trackId = null; | |
this.changeSeek = false | |
this.wasAllPaused = false | |
this.timeout = null | |
this.timeoutStep = null | |
} | |
Player.prototype = { | |
init: function (trackId) { | |
const self = this; | |
trackId = typeof trackId === 'number' ? trackId : self.trackId; | |
const data = $.grep(self.playlist, function (obj) {return obj.trackId === trackId;})[0]; | |
return new Promise((resolve, reject) => { | |
if (data.howl !== null) { | |
resolve(data.howl) | |
} else { | |
axios({ | |
method: 'get', | |
url: 'https://api.soundcloud.com/tracks/'+ trackId +'/streams?format=json&&client_id=' + SoundCloudClientId, | |
}).then((response) => { | |
new Promise((resolve) => { | |
data.howl = new Howl({ | |
src: [response.data['http_mp3_128_url']], | |
html5: true, // Force to HTML5 so that the audio can stream in (best for large files). | |
onplay: function() { | |
// Display the duration. | |
// Show the pause button. | |
$('.player[data-track-id="'+ trackId +'"]').addClass('active').addClass('play').removeClass('loading'); | |
// Start upating the progress of the track. | |
requestAnimationFrame(self.step.bind(self)); | |
}, | |
onload: function () { | |
// $('.player[data-track-id="'+ trackId +'"]').removeClass('loading'); | |
}, | |
onloaderror: function () { | |
$('.player[data-track-id="'+ trackId +'"]').removeClass('active').removeClass('play').removeClass('loading'); | |
alert('Oops! failure loading music! Please try again!') | |
}, | |
onplayerror: function () { | |
$('.player[data-track-id="'+ trackId +'"]').removeClass('active').removeClass('play').removeClass('loading'); | |
alert('Oops! failure play music! Please try again!') | |
}, | |
onseek: function () { | |
}, | |
onpause: function () { | |
$('.player[data-track-id="'+ trackId +'"]').removeClass('active').removeClass('play'); | |
}, | |
onend: function () { | |
$('.player[data-track-id="'+ trackId +'"]').removeClass('active').removeClass('play'); | |
} | |
}); | |
if (data.howl.state() === 'unloaded') { | |
data.howl.load(); | |
} | |
resolve(data.howl) | |
}).then((howl) => { | |
resolve(howl) | |
}) | |
}).catch((error) => { | |
reject(error) | |
}); | |
} | |
}) | |
}, | |
/** | |
* Play a song in the playlist. | |
* @param {Number} trackId Index of the song in the playlist (leave empty to play the first or current). | |
*/ | |
play: function(trackId) { | |
const self = this; | |
trackId = typeof trackId === 'number' ? trackId : self.trackId; | |
player.init(trackId).then((howl) => { | |
const data = $.grep(self.playlist, function (obj) {return obj.trackId === trackId;})[0]; | |
// If we already loaded this track, use the current one. | |
// Otherwise, setup and load a new Howl. | |
// Before start play stop all other sounds | |
this.playlist.forEach(function (value, index) { | |
if (self.playlist[index].howl !== null && self.playlist[index].trackId !== trackId) { | |
player.pause(self.playlist[index].trackId); | |
} | |
}) | |
if (howl.state() === 'loaded') { | |
$('.player[data-track-id="'+ trackId +'"]').addClass('active').addClass('play').removeClass('loading'); | |
} else { | |
//Loading | |
$('.player[data-track-id="'+ trackId +'"]').removeClass('active').removeClass('play').addClass('loading'); | |
} | |
// Begin playing the sound. | |
howl.play(); | |
// Keep track of the index we are currently playing. | |
self.trackId = trackId; | |
}); | |
}, | |
/** | |
* Pause the currently playing track. | |
*/ | |
pause: function(trackId, seek = false) { | |
const self = this; | |
trackId = typeof trackId === 'number' ? trackId : this.trackId; | |
player.init(trackId).then((howl) => { | |
howl.pause(); | |
if (seek) { | |
this.changeSeek = true | |
} | |
// Show the play button. | |
if (this.changeSeek === false) { // Fix with change seek (change icon on play) | |
$('.player[data-track-id="'+ trackId +'"]').removeClass('active').removeClass('play'); | |
} | |
}); | |
}, | |
/** | |
* The step called within requestAnimationFrame to update the playback position. | |
*/ | |
step: function() { | |
const self = this; | |
// Get the Howl we want to manipulate. | |
const data = $.grep(this.playlist, function (obj) {return obj.trackId === self.trackId;})[0]; | |
const sound = data.howl; | |
// Determine our current seek position. | |
const seek = sound.seek() || 0; | |
$('.player[data-track-id="'+ self.trackId +'"] .progress').css({'width': (((seek / sound.duration()) * 100) || 0) + '%'}); | |
// If the sound is still playing, continue stepping. | |
if (sound.playing()) { | |
requestAnimationFrame(self.step.bind(self)); | |
} | |
}, | |
setSeek: function (trackId, percent) { | |
this.changeSeek = true; | |
const self = this; | |
trackId = typeof trackId === 'number' ? trackId : self.trackId; | |
self.trackId = trackId; | |
const data = $.grep(self.playlist, function (obj) {return obj.trackId === trackId;})[0]; | |
if (!self.wasAllPaused) { | |
player.init(trackId).then() | |
//Loading | |
this.playlist.forEach(function (value, index) { | |
if (self.playlist[index].howl !== null && self.playlist[index].trackId !== trackId) { | |
player.pause(self.playlist[index].trackId); | |
} | |
}) | |
self.wasAllPaused = true; | |
} | |
const sound = data.howl; | |
if (sound !== null) { | |
if (sound.state() === 'unloaded' || sound.state() === 'loading') { | |
const el = $('.player[data-track-id="'+ trackId +'"]'); | |
if (!(el.hasClass('loading'))) { | |
el.addClass('loading'); | |
} | |
} | |
if (sound.state() === 'loaded') { | |
const newSeek = ((sound.duration() / 100) * percent) || 0; | |
sound.seek(newSeek); | |
sound.play(); | |
this.changeSeek = false; | |
this.wasAllPaused = false; | |
if (this.timeout !== null) { | |
clearTimeout(this.timeout); | |
this.timeout = null; | |
} | |
} else { | |
if (this.timeout === null) { | |
this.timeout = setInterval(function() { | |
player.setSeek(trackId, percent); | |
}, 1000) | |
} | |
} | |
} else { | |
//Force add loading class | |
const el = $('.player[data-track-id="'+ trackId +'"]'); | |
if (!(el.hasClass('loading'))) { | |
el.addClass('loading'); | |
} | |
if (this.timeout === null) { | |
this.timeout = setInterval(function() { | |
player.setSeek(trackId, percent); | |
}, 1000) | |
} | |
} | |
}, | |
/** | |
* Format the time from seconds to M:SS. | |
* @param {Number} secs Seconds to format. | |
* @return {String} Formatted time. | |
*/ | |
formatTime: function(secs) { | |
var minutes = Math.floor(secs / 60) || 0; | |
var seconds = (secs - minutes * 60) || 0; | |
return minutes + ':' + (seconds < 10 ? '0' : '') + seconds; | |
} | |
}; | |
//Set Player | |
const player = new Player(trackObjs); | |
// Bind our player controls. | |
$('.player[data-track-id] .control-button').on('click', function() { | |
const el = $(this).closest('.player[data-track-id]') | |
if (el.hasClass('active')) { | |
player.pause(el.data('trackId')); | |
} else { | |
player.play(el.data('trackId')); | |
} | |
}); | |
var mouseIsDown = { | |
status: false, | |
el: { | |
track: null, | |
timeline: null, | |
trackId: null | |
}, | |
timelinePercent: 0, | |
}; | |
$('.player[data-track-id] .timeline').on('touchstart mousedown', function(ev) { | |
// ev.stopPropagation(); | |
// ev.preventDefault(); | |
const self = $(this); | |
const el = self.closest('.player[data-track-id]'); | |
mouseIsDown.status = true; | |
mouseIsDown.el.track = el; | |
mouseIsDown.el.timeline = self; | |
mouseIsDown.el.progress = self.children().children(); | |
mouseIsDown.el.timelineCoords = this.getBoundingClientRect(); | |
mouseIsDown.el.trackId = el.data('trackId'); | |
player.pause(mouseIsDown.el.trackId); | |
if (ev.type === 'touchstart') { | |
setProgressWidth(ev.touches[0].clientX); | |
} else { | |
setProgressWidth(ev.clientX); | |
} | |
}) | |
$(document).on('touchmove mousemove', function(ev) { | |
if (ev.type === 'touchmove') { | |
setProgressWidth(ev.touches[0].clientX); | |
} else { | |
setProgressWidth(ev.clientX); | |
} | |
}); | |
$(document).on('touchend mouseup', function(ev) { | |
setPlayerSeek(); | |
}); | |
function setProgressWidth(clientX) { | |
if (mouseIsDown.status) { | |
const offsetX = (clientX- mouseIsDown.el.timelineCoords.left); | |
const percentWidth = 100 * offsetX / mouseIsDown.el.timelineCoords.width; | |
const roundPercent = percentWidth >= 100 ? 100 : percentWidth <= 0 ? 0 : percentWidth | |
mouseIsDown.el.progress.css({'width': ((roundPercent) + '%')}); | |
mouseIsDown.timelinePercent = roundPercent; | |
} | |
} | |
function setPlayerSeek() { | |
if (mouseIsDown.status) { | |
mouseIsDown.status = false; | |
player.setSeek(mouseIsDown.el.trackId, mouseIsDown.timelinePercent); | |
} | |
} | |
//Only active track set seek | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment