Skip to content

Instantly share code, notes, and snippets.

@mickeydarrenlau
Last active February 23, 2024 08:19
Show Gist options
  • Save mickeydarrenlau/d5368198b1ba577f9aca5a5def59458e to your computer and use it in GitHub Desktop.
Save mickeydarrenlau/d5368198b1ba577f9aca5a5def59458e to your computer and use it in GitHub Desktop.
My Spotify Widget
exempt cssclasses
dv.span('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512" fill="currentColor" width="16px" height="16px"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. --><path d="M248 8C111.1 8 0 119.1 0 256s111.1 248 248 248 248-111.1 248-248S384.9 8 248 8zm100.7 364.9c-4.2 0-6.8-1.3-10.7-3.6-62.4-37.6-135-39.2-206.7-24.5-3.9 1-9 2.6-11.9 2.6-9.7 0-15.8-7.7-15.8-15.8 0-10.3 6.1-15.2 13.6-16.8 81.9-18.1 165.6-16.5 237 26.2 6.1 3.9 9.7 7.4 9.7 16.5s-7.1 15.4-15.2 15.4zm26.9-65.6c-5.2 0-8.7-2.3-12.3-4.2-62.5-37-155.7-51.9-238.6-29.4-4.8 1.3-7.4 2.6-11.9 2.6-10.7 0-19.4-8.7-19.4-19.4s5.2-17.8 15.5-20.7c27.8-7.8 56.2-13.6 97.8-13.6 64.9 0 127.6 16.1 177 45.5 8.1 4.8 11.3 11 11.3 19.7-.1 10.8-8.5 19.5-19.4 19.5zm31-76.2c-5.2 0-8.4-1.3-12.9-3.9-71.2-42.5-198.5-52.7-280.9-29.7-3.6 1-8.1 2.6-12.9 2.6-13.2 0-23.3-10.3-23.3-23.6 0-13.6 8.4-21.3 17.4-23.9 35.2-10.3 74.6-15.2 117.5-15.2 73 0 149.5 15.2 205.4 47.8 7.8 4.5 12.9 10.7 12.9 22.6 0 13.6-11 23.3-23.2 23.3z"></path></svg>')
dv.span(" Spotify")
try {
dv.span("")
const {SpotifyMediaControls} = customJS
const this2 = this
await SpotifyMediaControls.display({this2,dv})
} catch(error) {
 dv.span("We are working hard to load the controls")
}
dv.span('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512" fill="currentColor" width="16px" height="16px"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. --><path d="M248 8C111.1 8 0 119.1 0 256s111.1 248 248 248 248-111.1 248-248S384.9 8 248 8zm100.7 364.9c-4.2 0-6.8-1.3-10.7-3.6-62.4-37.6-135-39.2-206.7-24.5-3.9 1-9 2.6-11.9 2.6-9.7 0-15.8-7.7-15.8-15.8 0-10.3 6.1-15.2 13.6-16.8 81.9-18.1 165.6-16.5 237 26.2 6.1 3.9 9.7 7.4 9.7 16.5s-7.1 15.4-15.2 15.4zm26.9-65.6c-5.2 0-8.7-2.3-12.3-4.2-62.5-37-155.7-51.9-238.6-29.4-4.8 1.3-7.4 2.6-11.9 2.6-10.7 0-19.4-8.7-19.4-19.4s5.2-17.8 15.5-20.7c27.8-7.8 56.2-13.6 97.8-13.6 64.9 0 127.6 16.1 177 45.5 8.1 4.8 11.3 11 11.3 19.7-.1 10.8-8.5 19.5-19.4 19.5zm31-76.2c-5.2 0-8.4-1.3-12.9-3.9-71.2-42.5-198.5-52.7-280.9-29.7-3.6 1-8.1 2.6-12.9 2.6-13.2 0-23.3-10.3-23.3-23.6 0-13.6 8.4-21.3 17.4-23.9 35.2-10.3 74.6-15.2 117.5-15.2 73 0 149.5 15.2 205.4 47.8 7.8 4.5 12.9 10.7 12.9 22.6 0 13.6-11 23.3-23.2 23.3z"></path></svg>')
dv.span(" Spotify")
try {
dv.span("<br>")
dv.span("<br>")
let { createButton } = app.plugins.plugins["buttons"]
let { SpotifySearch } = customJS;
let {search} = SpotifySearch
let this2 = this

dv.span("Search: ")
let searchfield = dv.span('<input type="text" id="fname" name="fname">')
searchfield.children[0].childNodes[0].value = window.spotifysearchq
createButton({

  

  

 app,

  

el: this2.container,

  

  

args: { name: "🔎 Search", id: "button-spotify-search"},

  

  

clickOverride: { click: await search, params: [[dv,searchfield]] },

  

  

})


await SpotifySearch.display({dv,this2,createButton})
} catch(error) {
dv.span("We are working hard to load the controls")
}


^006715

class SpotifyMediaControls {
async setrepeat(args) {
let {this2,repeatState} = args
if(!(window.spotifyproduct === "premium")) {
new Notice("❌ Non premium users can't change playback state", 5000)
} else {
repeatState.toggleRepeatState()
await spotifysdk.player.setRepeatMode(repeatState.getRepeatState())
}
}
async toggleplayback(args) {
let {this2,playbutton} = args
if(!(window.spotifyproduct === "premium")) {
new Notice("❌ Non premium users can't change playback state", 5000)
} else {
let data = await spotifysdk.player.getPlaybackState()
if(data.is_playing) {
await spotifysdk.player.pausePlayback()
} else {
await spotifysdk.player.startResumePlayback()
}
}
}
async nextplayback(args) {
if(!(window.spotifyproduct === "premium")) {
new Notice("❌ Non premium users can't change playback state", 5000)
} else {
await spotifysdk.player.skipToNext()
}
}
async previousplayback(args) {
if(!(window.spotifyproduct === "premium")) {
new Notice("❌ Non premium users can't change playback state", 5000)
} else {
await spotifysdk.player.skipToPrevious()
}
}
async switchdevice(args) {
if(!(window.spotifyproduct === "premium")) {
new Notice("❌ Non premium users can't change playback state", 5000)
} else {
const devices = await spotifysdk.player.getAvailableDevices()
let dlist = {}
for(let device_c of devices.devices){
dlist[device_c["name"]] = device_c["id"]
}
const system = await app.plugins.getPlugin('templater-obsidian').templater.functions_generator.internal_functions.modules_array.find(x => x.name === "system").static_object
const selection = await system.suggester(Object.keys(dlist), Object.keys(dlist))
await spotifysdk.player.transferPlayback([dlist[selection]])
}
}
async display(args){
let accountdata = await spotifysdk.currentUser.profile()
window.spotifyproduct = accountdata.product
window.currentr = 0
window.spotifycontrolwidgetplaypointcurrentlychanging = false
window.spotifycurrentplayingsilderfocused = false
let {this2,dv} = args
let {SpotifyRepeatState, SpotifyMediaUpdate, SpotifyMediaControls} = customJS
let RepeatStateLib = SpotifyRepeatState.load();
let repeatState = new RepeatStateLib;
await SpotifyMediaUpdate.run()
let { createButton } = app.plugins.plugins["buttons"]
let img = dv.span("<img style='max-width: 150px; max-height: 150px; margin-left: 0px; margin-right: 0px;' src=''><img>")
let songname = dv.span("")
dv.table(["Currently Playing",""], [[img,songname]])
img.style = 'max-width: 150px; max-height: 150px; margin-left: 0px; margin-right: 0px;'
img.lastChild.style = 'max-width: 150px; max-height: 150px; margin-left: 0px; margin-right: 0px;'
let image = img.lastChild.childNodes
dv.span("<br>")
let previousbutton = createButton({
app,
el: this2.container,
args: { name: "⏮ Previous", id: "button-spotify-skiptoprevious"},
clickOverride: { click: await SpotifyMediaControls.previousplayback, params: [{this2}] },
})
dv.span(" ")
let playbutton = createButton({
app,
el: this2.container,
args: { name: "⏸ Pause", id: "button-spotify-changeplaystate"},
clickOverride: { click: await SpotifyMediaControls.toggleplayback, params: [{this2}] },
})
dv.span(" ")
let nextbutton = createButton({
app,
el: this2.container,
args: { name: "Next ⏭", id: "button-spotify-skiptonext"},
clickOverride: { click: await SpotifyMediaControls.nextplayback, params: [{this2}] },
})
dv.span(" ")
let repeatbutton = createButton({
app,
el: this2.container,
args: { name: "❌ Repeat Off", id: "button-spotify-repeatbutton"},
clickOverride: { click: await SpotifyMediaControls.setrepeat, params: [{this2,repeatState}] },
})
dv.span("<br>")
dv.span("Volume: 0% ")
let volumesilder = dv.span('<input id="spotify-range-input" type="range" min="1" max="100" value="0" width="max">')
let volumetext = dv.span(" 0%")
dv.span("<br>")
dv.span("Playing on ")
let playingon = dv.span("none")
dv.span(" ")
let switchdevicebutton = createButton({
app,
el: this2.container,
args: { name: "🖥 Change Device", id: "button-spotify-switch-device"},
clickOverride: { click: await SpotifyMediaControls.switchdevice, params: [{this2}] },
})
dv.span("<br>")
const totallength = dv.span("00:00 ")
let currentplayingsilder = dv.span('<input id="spotify-range-input" style="width: 48%" type="range" min="1" max="100" value="0" width="max">')
let currentplayinglengthtext = dv.span(" 00:00")
let spotifycurrentplayingsilder = currentplayingsilder.lastChild.lastChild
let spotifyvolumesilder = volumesilder.lastChild.lastChild
spotifyvolumesilder.addEventListener('input', async function(event) {
window.spotifycontrolwidgetvolumecurrentlychanging = true
const tempSliderValue = event.target.value;
const progress = (tempSliderValue / spotifyvolumesilder.max) * 100;
spotifyvolumesilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
});
if (!(Capacitor.getPlatform() == "web")) {
spotifyvolumesilder.classList.add('focused');
spotifycurrentplayingsilder.classList.add('focused');
}
spotifycurrentplayingsilder.addEventListener('mouseenter', function () {
// Add a class or apply styles to the thumb when the slider is focused
spotifycurrentplayingsilder.classList.add('focused');
let progress = window.spotifycurrentplayingsilderprogress
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #1DB954 ${progress}%, #4d4d4d ${progress}%)`;
window.spotifycurrentplayingsilderfocused = true
});
spotifycurrentplayingsilder.addEventListener('mouseleave', function () {
// Remove the class or reset styles when the slider loses focus
spotifycurrentplayingsilder.classList.remove('focused');
let progress = window.spotifycurrentplayingsilderprogress
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
window.spotifycurrentplayingsilderfocused = false
});
spotifyvolumesilder.addEventListener('mouseenter', function () {
// Add a class or apply styles to the thumb when the slider is focused
spotifyvolumesilder.classList.add('focused');
let progress = window.spotifyvolumesilderprogress
spotifyvolumesilder.style.background = `linear-gradient(to right, #1DB954 ${progress}% , #4d4d4d ${progress}% )`;
window.spotifyvolumesilderfocused = true
});
spotifyvolumesilder.addEventListener('mouseleave', function () {
// Remove the class or reset styles when the slider loses focus
spotifyvolumesilder.classList.remove('focused');
let progress = window.spotifyvolumesilderprogress
spotifyvolumesilder.style.background = `linear-gradient(to right, #fff ${progress}% , #4d4d4d ${progress}% )`;
window.spotifyvolumesilderfocused = false
});
spotifyvolumesilder.addEventListener('change', async function(event) {
if(!(window.spotifyproduct === "premium")) {
new Notice("❌ Non premium users can't change playback state", 5000)
window.spotifycontrolwidgetvolumecurrentlychanging = false
} else {
let sliderValue = event.target.value;
window.spotifycontrolwidgetvolumejustset = true
await spotifysdk.player.setPlaybackVolume(sliderValue)
volumetext.innerText = " " + event.target.value + "%"
const tempSliderValue = event.target.value;
const progress = (tempSliderValue / spotifyvolumesilder.max) * 100;
if(window.spotifyvolumesilderfocused) {
spotifyvolumesilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
} else {
spotifyvolumesilder.style.background = `linear-gradient(to right, #1DB954 ${progress}%, #4d4d4d ${progress}%)`;
}
window.spotifycontrolwidgetvolumecurrentlychanging = false
}
});
spotifycurrentplayingsilder.addEventListener('input', async function(event) {
window.spotifycontrolwidgetplaypointcurrentlychanging = true
const tempSliderValue = event.target.value;
const progress = (tempSliderValue / spotifycurrentplayingsilder.max) * 100;
if(!window.spotifycurrentplayingsilderfocused) {
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
} else {
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #1DB954 ${progress}%, #4d4d4d ${progress}%)`;
}
});
spotifycurrentplayingsilder.addEventListener('change', async function(event) {
if(!(window.spotifyproduct === "premium")) {
new Notice("❌ Non premium users can't change playback state", 5000)
window.spotifycontrolwidgetplaypointcurrentlychanging = false
} else {
let sliderValue = event.target.value
window.spotifycontrolwidgetplaypointjustset = true
await spotifysdk.player.seekToPosition(sliderValue)
const tempSliderValue = event.target.value;
const progress = (tempSliderValue / spotifycurrentplayingsilder.max) * 100;
if(!window.spotifycurrentplayingsilderfocused) {
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
} else {
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #1DB954 ${progress}%, #4d4d4d ${progress}%)`;
}
window.spotifycontrolwidgetplaypointcurrentlychanging = false
}
});
document.addEventListener('spotifyliveupdate', () => {
try {
let data = event.detail
if(!window.spotifycontrolwidgetplaypointcurrentlychanging) {
if(window.spotifycontrolwidgetplaypointjustset) {
setTimeout( async () => {
data = await spotifysdk.player.getPlaybackState()
spotifycurrentplayingsilder.max = data.item.duration_ms
spotifycurrentplayingsilder.value = data.progress_ms
const tempSliderValue = data.progress_ms;
const progress = (tempSliderValue / spotifycurrentplayingsilder.max) * 100;
if(!window.spotifycurrentplayingsilderfocused) {
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
} else {
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #1DB954 ${progress}%, #4d4d4d ${progress}%)`;
}
currentplayinglengthtext.innerText = " " + new Date(data.progress_ms).toISOString().slice(11, 19).replace("00:" , '');
totallength.innerText = " " + new Date(data.item.duration_ms).toISOString().slice(11, 19).replace("00:" , '');
window.spotifycontrolwidgetplaypointjustset = false
}, 500)
} else {
spotifycurrentplayingsilder.max = data.item.duration_ms
spotifycurrentplayingsilder.value = data.progress_ms
const tempSliderValue = data.progress_ms;
const progress = (tempSliderValue / spotifycurrentplayingsilder.max) * 100;
window.spotifycurrentplayingsilderprogress = progress
if(!window.spotifycurrentplayingsilderfocused) {
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
} else {
spotifycurrentplayingsilder.style.background = `linear-gradient(to right, #1DB954 ${progress}%, #4d4d4d ${progress}%)`;
}
currentplayinglengthtext.innerText = " " + new Date(data.progress_ms).toISOString().slice(11, 19).replace("00:" , '');
totallength.innerText = new Date(data.item.duration_ms).toISOString().slice(11, 19).replace("00:" , '') + " "
}
} else {
currentplayinglengthtext.innerText = " " + new Date(data.progress_ms).toISOString().slice(11, 19).replace("00:" , '');
totallength.innerText = new Date(data.item.duration_ms).toISOString().slice(11, 19).replace("00:" , '') + " "
}
if(data.is_playing) {
playbutton.innerText = "⏸ Pause"
} else {
playbutton.innerText = "▶ Play"
}
if(!window.spotifycontrolwidgetvolumecurrentlychanging) {
if(window.spotifycontrolwidgetvolumejustset){
setTimeout(async () => {
data = await spotifysdk.player.getPlaybackState()
volumetext.innerText = " " + data.device.volume_percent + "%"
spotifyvolumesilder.value = data.device.volume_percent
window.spotifycontrolwidgetvolumejustset = false
}, 500)
} else {
spotifyvolumesilder.value = data.device.volume_percent
const tempSliderValue = data.device.volume_percent;
volumetext.innerText = " " + data.device.volume_percent + "%"
const progress = (tempSliderValue / spotifyvolumesilder.max) * 100;
window.spotifyvolumesilderprogress = progress
if(!window.spotifyvolumesilderfocused) {
spotifyvolumesilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
} else {
spotifyvolumesilder.style.background = `linear-gradient(to right, #1DB954 ${progress}%, #4d4d4d ${progress}%)`;
}
} } else {
volumetext.innerText = " " + data.device.volume_percent + "%"
const tempSliderValue = data.device.volume_percent;
const progress = (tempSliderValue / spotifyvolumesilder.max) * 100;
if(!window.spotifyvolumesilderfocused) {
spotifyvolumesilder.style.background = `linear-gradient(to right, #fff ${progress}%, #4d4d4d ${progress}%)`;
} else {
spotifyvolumesilder.style.background = `linear-gradient(to right, #1DB954 ${progress}%, #4d4d4d ${progress}%)`;
}
}
if(data.repeat_state) {
repeatState.setRepeatState(data.repeat_state)
let state = repeatState.getRepeatState()
if(state==="off"){
repeatbutton.innerText = "❌ Repeat Off"
}
if(state==="track"){
repeatbutton.innerText = "🔂 Repeat current song"
}
if(state==="context"){
repeatbutton.innerText = "🔁 Repeat whole album or playlist"
}
} else {
repeatState.setRepeatState("off")
repeatbutton.innerText = "❌ Repeat Off"
}
image[0].src = data.item.album.images[1].url
playingon.innerText = data.device.name + " "
let artistname = ''
for(let i in data.item.artists){
let artist = data.item.artists[i]
artistname = artistname + artist.name + ", "
}
songname.innerText = "Currently playing " + data.item.name + " from " + data.item.album.name + " by " + artistname.slice(0, -2)
window.spotifycurrentchangingsliders = false
window.currentr = window.currentr + 1
} catch(error) {
}
});
}
}
class SpotifyMediaUpdate{
async run() {
if(!window.spotifycontrolwidgetalreadyrun) {
console.log("started - spotify")
window.notshowing = false
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
window.notshowing = true
} else {
window.notshowing = false
}
});
setInterval(async () => {
try {
let whitelist = ['Widgets/Spotify Media Controls.md', 'Readme.md']
if(!(window.notshowing)) {
let data = await spotifysdk.player.getPlaybackState()
let event = new CustomEvent("spotifyliveupdate", { detail: data });
document.dispatchEvent(event)
} }catch {}
},'1000')
window.spotifycontrolwidgetalreadyrun = true
}
}
}
class SpotifyRepeatState {
load() {
class SpotifyRepeatState {
constructor() {
this.state = "off"; // Initialize to "off" by default
}
// Methods to set and get the repeat state:
setRepeatState(newState) {
if (newState === "track" || newState === "context" || newState === "off") {
this.state = newState;
} else {
console.warn("Invalid repeat state:", newState);
}
}
getRepeatState() {
return this.state;
}
// Method to toggle between states:
toggleRepeatState() {
if (this.state === "off") {
this.setRepeatState("track");
} else if (this.state === "track") {
this.setRepeatState("context");
} else {
this.setRepeatState("off");
}
}
}
return SpotifyRepeatState
}
}
class SpotifySearch {
async search(args) {
window.spotifysearchq = args[1].children[0].childNodes[0].value
window.spotifysearchoffset = 0
await app.vault.adapter.writeBinary("Widgets/Spotify Search.md", await app.vault.adapter.readBinary("Widgets/Spotify Search.md"))
}
async search_next(args) {
if(!window.spotifysearchoffset) {
window.spotifysearchoffset = 0
}
window.spotifysearchoffset = Number(window.spotifysearchoffset) + 4
await app.vault.adapter.writeBinary("Widgets/Spotify Search.md", await app.vault.adapter.readBinary("Widgets/Spotify Search.md"))
}
async search_prev(args) {
if(!window.spotifysearchoffset) {
window.spotifysearchoffset = 0
}
if(window.spotifysearchoffset === 0) {
new Notice("❌ Your on the first page!")
} else {
window.spotifysearchoffset = Number(window.spotifysearchoffset) -4
await app.vault.adapter.writeBinary("Widgets/Spotify Search.md", await app.vault.adapter.readBinary("Widgets/Spotify Search.md"))
}
}
async display(args) {
const {dv,this2,createButton} = args;
const {SpotifySearch} = customJS;
const {search_next,search_prev} = SpotifySearch;
dv.span("<br>")
if((window.spotifysearchq || '')) {
let data = await spotifysdk.search((window.spotifysearchq || ''),"album","ES", 4,window.spotifysearchoffset)
let songs = data.albums.items
let songtable = []
async function play(args) {
let data = await spotifysdk.currentUser.profile()
let product = data.product
if(!(product==="premium")) {
new Notice("❌ Non premium users can't change playback state", 5000)
} else {
await spotifysdk.player.startResumePlayback(args[0], args[1])
}
}
for (let i of songs) {
let image = "![songimg](" + i.images[2].url + ")"
let name = i.name
let artist = i.artists[0].name
songtable.push([image,name,artist,createButton({
app,
el: this2.container,
args: { name: "▶ Play", id: "button-spotify-play-" + i.uri},
clickOverride: { click: await play, params: [['',i.uri]] },
})])
}
dv.table(["Image", "Song Name", "Artist", "Play"],songtable)
dv.span("<br>")
createButton({
app,
el: this2.container,
args: { name: "⏮ Previous", id: "button-spotify-search-prev"},
clickOverride: { click: await search_prev, params: [[dv]] },
})
createButton({
app,
el: this2.container,
args: { name: "⏭ Next", id: "button-spotify-search-next"},
clickOverride: { click: await search_next, params: [[dv]] },
})
} else {
dv.span("❌ No search query")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment