// ==UserScript==
// @name Lucida Downloader
// @description Download music from Spotify, Qobuz, Tidal, SoundCloud & Amazon Music via Lucida.
// @icon https://raw.githubusercontent.com/afkarxyz/userscripts/refs/heads/main/assets/lucida/lucida.png
// @version 2.4
// @author afkarxyz
// @namespace https://github.com/afkarxyz/userscripts/
// @supportURL https://github.com/afkarxyz/userscripts/issues
// @license MIT
// @match https://open.spotify.com/*
// @match https://listen.tidal.com/*
// @match https://music.amazon.com/*
// @match https://soundcloud.com/*
// @match https://www.qobuz.com/*
// @match https://lucida.to/*
// @match https://lucida.su/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @downloadURL https://update.greasyfork.icu/scripts/522465/Lucida%20Downloader.user.js
// @updateURL https://update.greasyfork.icu/scripts/522465/Lucida%20Downloader.meta.js
// ==/UserScript==
(function() {
'use strict';
const DOMAINS = ['lucida.to', 'lucida.su'];
const BASE_URL = 'https://raw.githubusercontent.com/afkarxyz/userscripts/refs/heads/main/assets/lucida/';
const SERVICES = {
'tidal': {
name: 'Tidal',
icon: `${BASE_URL}tidal.png`
},
'soundcloud': {
name: 'Soundcloud',
icon: `${BASE_URL}soundcloud.png`
},
'deezer': {
name: 'Deezer',
icon: `${BASE_URL}deezer.png`
},
'amazon': {
name: 'Amazon Music',
icon: `${BASE_URL}amazon.png`
}
};
const fontLink = document.createElement('link');
fontLink.rel = 'stylesheet';
fontLink.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap';
document.head.appendChild(fontLink);
GM_addStyle(`
.floating-settings-button {
position: fixed;
top: 10%;
right: 0;
width: 2.5rem;
height: 2.5rem;
border-radius: 1.25rem 0 0 1.25rem;
background: rgba(60, 60, 60, 0.9);
border: none;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
transition: all 0.3s ease;
font-family: 'Inter', sans-serif;
}
.floating-settings-button .lucida-icon {
width: 1.75rem;
height: 1.75rem;
object-fit: contain;
transition: all 0.3s ease;
}
.floating-settings-button:hover .button-menu {
opacity: 1;
visibility: visible;
}
.button-menu {
position: absolute;
right: 0;
top: calc(100% + 1rem);
display: flex;
flex-direction: column;
background: rgba(60, 60, 60, 0.9);
border-radius: 0.75rem 0 0 0.75rem;
padding: 0.5rem 0;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.menu-item {
width: 2.5rem;
height: 2.5rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
color: white;
font-family: 'Inter', sans-serif;
font-size: 0.75rem;
font-weight: 500;
}
.menu-item:hover {
background: rgba(255, 255, 255, 0.1);
}
.menu-item svg {
width: 1.25rem;
height: 1.25rem;
fill: white;
}
.domain-option {
width: 2.5rem;
height: 2rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
color: white;
font-family: 'Inter', sans-serif;
font-size: 0.75rem;
font-weight: 500;
}
.domain-option:hover {
background: rgba(255, 255, 255, 0.1);
}
.floating-settings-button .lucida-icon.disabled {
filter: grayscale(100%);
cursor: not-allowed;
opacity: 0.3;
}
.lucida-modal-container {
position: fixed;
top: calc(10% - 0.25rem);
right: 3.5rem;
display: block;
z-index: 10000;
pointer-events: none;
}
.lucida-modal-container.show {
pointer-events: auto;
}
.lucida-modal {
background: #222;
color: #fff;
border-radius: 0.75rem;
width: 20rem;
max-width: 90vw;
box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.3);
opacity: 0;
transform: translateX(2rem);
transition: opacity 0.3s ease, transform 0.3s ease;
pointer-events: none;
overflow: hidden;
font-family: 'Inter', sans-serif;
}
.lucida-modal-container.show .lucida-modal {
opacity: 1;
transform: translateX(0);
pointer-events: auto;
}
.lucida-modal * {
font-family: 'Inter', sans-serif;
box-sizing: border-box;
}
.lucida-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.lucida-modal-header h2 {
margin: 0;
font-size: 1.125rem;
font-weight: 600;
color: #f42e8d;
}
.lucida-modal-content {
padding: 1rem;
}
.preference-group {
margin-bottom: 1.25rem;
}
.preference-group:last-child {
margin-bottom: 0;
}
.lucida-modal label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
font-size: 0.875rem;
color: #ccc;
}
.lucida-modal select {
width: 100%;
padding: 0.5rem;
background: #333;
border: 1px solid #444;
border-radius: 0.25rem;
color: #fff;
font-size: 0.875rem;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ccc' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: calc(100% - 0.75rem) center;
font-family: 'Inter', sans-serif;
cursor: pointer;
transition: border-color 0.2s ease, background-color 0.2s ease;
}
.lucida-modal select:hover {
border-color: #f42e8d;
background-color: #3a3a3a;
}
.lucida-modal select:focus {
outline: none;
border-color: #f42e8d;
}
.service-select-wrapper {
position: relative;
margin-bottom: 1rem;
}
.custom-select {
width: 100%;
padding: 0.5rem;
background: #333;
border: 1px solid #444;
border-radius: 0.25rem;
color: #fff;
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ccc' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: calc(100% - 0.75rem) center;
font-family: 'Inter', sans-serif;
transition: border-color 0.2s ease, background-color 0.2s ease;
}
.custom-select:hover {
border-color: #f42e8d;
background-color: #3a3a3a;
}
.custom-options {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: #333;
border: 1px solid #444;
border-radius: 0.25rem;
margin-top: 0.25rem;
max-height: 12.5rem;
overflow-y: auto;
z-index: 1000;
display: none;
box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.3);
}
.custom-options.show {
display: block;
}
.service-option {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
cursor: pointer;
transition: background-color 0.2s ease;
color: #fff;
font-family: 'Inter', sans-serif;
}
.service-option:hover {
background-color: #444;
}
.service-option img,
.custom-select img {
width: 1rem;
height: 1rem;
object-fit: contain;
}
[role='grid'] {
margin-left: 3.125rem;
}
[data-testid="tracklist-row"] {
position: relative;
}
[role="presentation"] > * {
contain: unset;
}
.btn {
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
border: 0;
position: relative;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 0.125rem 0.3125rem rgba(0,0,0,0.2);
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #f42e8d, #b91c68);
}
.btn:hover {
transform: scale(1.1);
box-shadow: 0 0.25rem 0.5rem rgba(0,0,0,0.3);
}
.btn .icon {
width: 50%;
height: 50%;
background-position: center;
background-repeat: no-repeat;
background-size: contain;
background-image: url('data:image/svg+xml;utf8,');
}
[data-testid="tracklist-row"] .btn {
position: absolute;
top: 50%;
right: 100%;
margin-top: -1.25rem;
margin-right: 0.625rem;
}
.N7GZp8IuWPJvCPz_7dOg .btn {
width: 1.5rem;
height: 1.5rem;
transform-origin: center;
position: absolute;
top: 50%;
right: 100%;
margin-top: -0.75rem !important;
margin-right: 0.625rem;
}
.N7GZp8IuWPJvCPz_7dOg .btn .icon {
transform: scale(0.85);
width: 65%;
height: 65%;
}
`);
function createServiceOption(value, service) {
const option = document.createElement('div');
option.className = 'service-option';
option.dataset.value = value;
if (service.icon) {
const img = document.createElement('img');
img.src = service.icon;
img.alt = service.name;
img.style.display = 'none';
img.onload = () => {
img.style.display = 'inline';
};
option.appendChild(img);
}
const span = document.createElement('span');
span.textContent = service.name;
option.appendChild(span);
return option;
}
function updateCustomSelect(customSelect, value) {
const service = SERVICES[value];
let content = `${service.name}`;
if (service.icon) {
const img = new Image();
img.src = service.icon;
img.style.display = 'none';
img.onload = () => {
img.style.display = 'inline';
customSelect.querySelector('img')?.style.setProperty('display', 'inline');
};
content = `
${service.name}`;
}
customSelect.innerHTML = content;
}
function createFloatingButton() {
const button = document.createElement('div');
button.className = 'floating-settings-button';
const lucidaIcon = document.createElement('img');
lucidaIcon.src = 'https://raw.githubusercontent.com/afkarxyz/userscripts/refs/heads/main/assets/lucida/lucida.png';
lucidaIcon.alt = 'Lucida';
lucidaIcon.className = 'lucida-icon';
const buttonMenu = document.createElement('div');
buttonMenu.className = 'button-menu';
const settingsMenuItem = document.createElement('div');
settingsMenuItem.className = 'menu-item';
settingsMenuItem.innerHTML = ``;
const toOption = document.createElement('div');
toOption.className = 'domain-option';
toOption.textContent = '.to';
const suOption = document.createElement('div');
suOption.className = 'domain-option';
suOption.textContent = '.su';
buttonMenu.appendChild(settingsMenuItem);
buttonMenu.appendChild(toOption);
buttonMenu.appendChild(suOption);
button.appendChild(lucidaIcon);
button.appendChild(buttonMenu);
document.body.appendChild(button);
const modalContainer = document.createElement('div');
modalContainer.className = 'lucida-modal-container';
document.body.appendChild(modalContainer);
lucidaIcon.addEventListener('click', (e) => {
if (lucidaIcon.classList.contains('disabled')) {
e.preventDefault();
e.stopPropagation();
return false;
}
if (!lucidaIcon.classList.contains('disabled')) {
const currentUrl = window.location.href;
const domain = GM_getValue('domainPreference', 'random') === 'random'
? DOMAINS[Math.floor(Math.random() * DOMAINS.length)]
: GM_getValue('domainPreference');
window.open(`https://${domain}/?url=${encodeURIComponent(currentUrl)}&country=auto`, '_blank');
}
e.stopPropagation();
});
settingsMenuItem.addEventListener('click', (e) => {
createPreferencesModal(modalContainer);
requestAnimationFrame(() => {
modalContainer.classList.add('show');
});
e.preventDefault();
e.stopPropagation();
return false;
});
toOption.addEventListener('click', (e) => {
window.open('https://lucida.to/stats', '_blank');
e.preventDefault();
e.stopPropagation();
return false;
});
suOption.addEventListener('click', (e) => {
window.open('https://lucida.su/stats', '_blank');
e.preventDefault();
e.stopPropagation();
return false;
});
if (window.location.hostname === 'open.spotify.com') {
lucidaIcon.classList.add('disabled');
lucidaIcon.addEventListener('click', (e) => {
if (lucidaIcon.classList.contains('disabled')) {
e.preventDefault();
e.stopPropagation();
return false;
}
}, true);
}
if (window.location.hostname.includes('lucida.')) {
button.style.display = 'none';
}
document.addEventListener('click', (e) => {
if (!e.target.closest('.lucida-modal') && !e.target.closest('.menu-item') && modalContainer.classList.contains('show')) {
modalContainer.classList.remove('show');
}
});
return button;
}
function createPreferencesModal(modalContainer) {
modalContainer.innerHTML = '';
const modal = document.createElement('div');
modal.className = 'lucida-modal';
const modalHeader = document.createElement('div');
modalHeader.className = 'lucida-modal-header';
const modalTitle = document.createElement('h2');
modalTitle.textContent = 'Lucida Preferences';
const closeButton = document.createElement('div');
closeButton.innerHTML = ``;
closeButton.style.cursor = 'pointer';
closeButton.style.color = '#ccc';
closeButton.addEventListener('click', () => {
modalContainer.classList.remove('show');
});
modalHeader.appendChild(modalTitle);
modalHeader.appendChild(closeButton);
const modalContent = document.createElement('div');
modalContent.className = 'lucida-modal-content';
const domainGroup = document.createElement('div');
domainGroup.className = 'preference-group';
const domainLabel = document.createElement('label');
domainLabel.textContent = 'Domain';
domainLabel.setAttribute('for', 'domain-select');
const domainSelect = document.createElement('select');
domainSelect.id = 'domain-select';
domainSelect.innerHTML = `
`;
domainGroup.appendChild(domainLabel);
domainGroup.appendChild(domainSelect);
const serviceGroup = document.createElement('div');
serviceGroup.className = 'preference-group';
const serviceLabel = document.createElement('label');
serviceLabel.textContent = 'Spotify Service Resolver';
const serviceSelectWrapper = document.createElement('div');
serviceSelectWrapper.className = 'service-select-wrapper';
const customSelect = document.createElement('div');
customSelect.className = 'custom-select';
customSelect.id = 'custom-service-select';
customSelect.innerHTML = 'Select a service';
const customOptions = document.createElement('div');
customOptions.className = 'custom-options';
const serviceInput = document.createElement('input');
serviceInput.type = 'hidden';
serviceInput.id = 'service-select';
serviceSelectWrapper.appendChild(customSelect);
serviceSelectWrapper.appendChild(customOptions);
serviceSelectWrapper.appendChild(serviceInput);
serviceGroup.appendChild(serviceLabel);
serviceGroup.appendChild(serviceSelectWrapper);
const formatGroup = document.createElement('div');
formatGroup.className = 'preference-group';
const formatLabel = document.createElement('label');
formatLabel.textContent = 'Download Format';
formatLabel.setAttribute('for', 'format-select');
const formatSelect = document.createElement('select');
formatSelect.id = 'format-select';
formatSelect.innerHTML = `
`;
formatGroup.appendChild(formatLabel);
formatGroup.appendChild(formatSelect);
const qualityContainer = document.createElement('div');
qualityContainer.id = 'quality-settings-container';
qualityContainer.style.display = 'none';
qualityContainer.style.marginTop = '1rem';
const qualityLabel = document.createElement('label');
qualityLabel.textContent = 'Quality Settings';
qualityLabel.setAttribute('for', 'quality-select');
qualityLabel.style.marginTop = '0';
const qualitySelect = document.createElement('select');
qualitySelect.id = 'quality-select';
qualityContainer.appendChild(qualityLabel);
qualityContainer.appendChild(qualitySelect);
formatGroup.appendChild(qualityContainer);
const autoDownloadGroup = document.createElement('div');
autoDownloadGroup.className = 'preference-group';
const autoDownloadLabel = document.createElement('label');
autoDownloadLabel.textContent = 'Auto Download';
autoDownloadLabel.setAttribute('for', 'auto-download-select');
const autoDownloadSelect = document.createElement('select');
autoDownloadSelect.id = 'auto-download-select';
autoDownloadSelect.innerHTML = `
`;
autoDownloadGroup.appendChild(autoDownloadLabel);
autoDownloadGroup.appendChild(autoDownloadSelect);
modalContent.appendChild(domainGroup);
modalContent.appendChild(serviceGroup);
modalContent.appendChild(formatGroup);
modalContent.appendChild(autoDownloadGroup);
modal.appendChild(modalHeader);
modal.appendChild(modalContent);
modalContainer.appendChild(modal);
domainSelect.value = GM_getValue('domainPreference', 'random');
formatSelect.value = GM_getValue('formatPreference', 'original');
autoDownloadSelect.value = GM_getValue('autoDownloadEnabled', 'enabled');
Object.entries(SERVICES).forEach(([value, service]) => {
const option = createServiceOption(value, service);
customOptions.appendChild(option);
option.addEventListener('click', () => {
serviceInput.value = value;
GM_setValue('targetService', value);
updateCustomSelect(customSelect, value);
customOptions.classList.remove('show');
});
});
const savedService = GM_getValue('targetService', 'tidal');
if (SERVICES[savedService]) {
updateCustomSelect(customSelect, savedService);
serviceInput.value = savedService;
} else {
updateCustomSelect(customSelect, 'tidal');
serviceInput.value = 'tidal';
}
customSelect.addEventListener('click', () => {
customOptions.classList.toggle('show');
});
function updateQualityOptions(format) {
qualitySelect.innerHTML = '';
switch(format) {
case 'flac':
qualitySelect.innerHTML = '';
qualityContainer.style.display = 'block';
GM_setValue('qualityPreference', '16');
break;
case 'mp3':
case 'ogg-vorbis':
case 'm4a-aac':
qualitySelect.innerHTML = `
`;
qualityContainer.style.display = 'block';
GM_setValue('qualityPreference', '320');
break;
case 'opus':
qualitySelect.innerHTML = `
`;
qualityContainer.style.display = 'block';
GM_setValue('qualityPreference', '320');
break;
default:
qualityContainer.style.display = 'none';
GM_setValue('qualityPreference', null);
}
}
updateQualityOptions(formatSelect.value);
if (qualitySelect) {
qualitySelect.value = GM_getValue('qualityPreference', '320');
qualitySelect.addEventListener('change', () => {
GM_setValue('qualityPreference', qualitySelect.value);
});
}
domainSelect.addEventListener('change', () => {
GM_setValue('domainPreference', domainSelect.value);
});
formatSelect.addEventListener('change', () => {
GM_setValue('formatPreference', formatSelect.value);
updateQualityOptions(formatSelect.value);
});
autoDownloadSelect.addEventListener('change', () => {
GM_setValue('autoDownloadEnabled', autoDownloadSelect.value);
});
document.addEventListener('click', (e) => {
if (!e.target.closest('.service-select-wrapper')) {
customOptions.classList.remove('show');
}
});
}
function autoSelectFormat() {
if (!window.location.hostname.includes('lucida.')) return;
const selectFormatAndQuality = () => {
const convertSelect = document.getElementById('convert');
if (!convertSelect) return;
const format = GM_getValue('formatPreference', 'original');
const quality = GM_getValue('qualityPreference', '320');
convertSelect.value = format;
convertSelect.dispatchEvent(new Event('change', { bubbles: true }));
const observer = new MutationObserver((mutations, obs) => {
const downsettingSelect = document.getElementById('downsetting');
if (downsettingSelect) {
downsettingSelect.value = quality;
downsettingSelect.dispatchEvent(new Event('change', { bubbles: true }));
obs.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
};
if (document.getElementById('convert')) {
selectFormatAndQuality();
}
const pageObserver = new MutationObserver(() => {
if (document.getElementById('convert')) {
selectFormatAndQuality();
}
});
pageObserver.observe(document.body, { childList: true, subtree: true });
}
function autoDownload() {
if (!window.location.hostname.includes('lucida.')) return;
if (GM_getValue('autoDownloadEnabled', 'enabled') !== 'enabled') return;
const clickDownloadButton = () => {
const button = document.querySelector('.d1-track button') ||
document.querySelector('button[class*="download-button"]');
if (button) {
button.click();
}
};
const observer = new MutationObserver(() => {
clickDownloadButton();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
clickDownloadButton();
}
function openInLucida(trackUrl) {
const currentUrl = encodeURIComponent(trackUrl || window.location.href);
const prefs = getPreferences();
let domain = prefs.domainPreference === 'random'
? DOMAINS[Math.floor(Math.random() * DOMAINS.length)]
: prefs.domainPreference;
let url = `https://${domain}/?url=${currentUrl}&country=auto`;
if (prefs.targetService) {
url += `&to=${prefs.targetService}`;
}
window.open(url, '_blank');
}
const getPreferences = () => ({
targetService: GM_getValue('targetService', 'tidal'),
domainPreference: GM_getValue('domainPreference', 'random')
});
function addButton(el) {
const button = document.createElement('button');
button.className = 'btn';
const icon = document.createElement('div');
icon.className = 'icon';
button.appendChild(icon);
el.appendChild(button);
return button;
}
function addNowPlayingButton() {
const downloadButton = document.createElement('button');
downloadButton.className = 'Lucida-Button-sc-1dqy6lx-0 dmdXQN';
downloadButton.innerHTML = '';
downloadButton.style.cssText = 'background:transparent;border:none;color:#f42e8d;cursor:pointer;padding:8px;margin:0 4px;transition:transform .2s ease';
downloadButton.onmouseover = () => downloadButton.style.transform = 'scale(1.1)';
downloadButton.onmouseout = () => downloadButton.style.transform = 'scale(1)';
downloadButton.onclick = () => {
const link = document.querySelector('a[href*="spotify:track:"]');
if (link) {
const match = link.getAttribute('href').match(/spotify:track:([a-zA-Z0-9]+)/);
if (match) {
const trackUrl = `https://open.spotify.com/track/${match[1]}`;
openInLucida(trackUrl);
}
}
};
const container = document.querySelector('.snFK6_ei0caqvFI6As9Q')?.querySelector('.deomraqfhIAoSB3SgXpu');
if (container && !container.querySelector('.Lucida-Button-sc-1dqy6lx-0')) {
container.appendChild(downloadButton);
}
}
function animate() {
const currentUrl = window.location.href;
const urlParts = currentUrl.split('/');
const type = urlParts[3];
addNowPlayingButton();
if (type === 'track') {
const actionBarRow = document.querySelector('.eSg4ntPU2KQLfpLGXAww[data-testid="action-bar-row"]');
if (actionBarRow && !actionBarRow.hasButtons) {
const downloadButton = addButton(actionBarRow);
downloadButton.onclick = function() {
const spotifyId = urlParts[4].split('?')[0];
openInLucida(`https://open.spotify.com/track/${spotifyId}`);
}
actionBarRow.hasButtons = true;
}
}
if (type === 'artist') {
const tracks = document.querySelectorAll('[role="gridcell"]');
tracks.forEach(track => {
if (!track.hasButtons) {
const downloadButton = addButton(track);
downloadButton.onclick = function() {
const btn = track.querySelector('[data-testid="more-button"]');
if (btn) {
btn.click();
setTimeout(() => {
const highlightEl = document.querySelector('#context-menu a[href*="highlight"]');
if (highlightEl) {
const highlight = highlightEl.href.match(/highlight=(.+)/)[1];
document.dispatchEvent(new MouseEvent('mousedown'));
const spotifyId = highlight.split(':')[2];
openInLucida(`https://open.spotify.com/track/${spotifyId}`);
}
}, 1);
}
}
track.hasButtons = true;
}
});
}
if (type === 'album' || type === 'playlist' || type === 'track') {
const tracks = document.querySelectorAll('[data-testid="tracklist-row"]');
tracks.forEach(track => {
if (!track.hasButtons) {
const downloadButton = addButton(track);
downloadButton.onclick = function() {
const trackLink = track.querySelector('a[href^="/track"]');
if (trackLink) {
openInLucida(trackLink.href);
} else {
const btn = track.querySelector('[data-testid="more-button"]');
if (btn) {
btn.click();
setTimeout(() => {
const highlightEl = document.querySelector('#context-menu a[href*="highlight"]');
if (highlightEl) {
const highlight = highlightEl.href.match(/highlight=(.+)/)[1];
document.dispatchEvent(new MouseEvent('mousedown'));
const spotifyId = highlight.split(':')[2];
openInLucida(`https://open.spotify.com/track/${spotifyId}`);
}
}, 1);
}
}
}
track.hasButtons = true;
}
});
}
}
function animateLoop() {
if (window.location.hostname === 'open.spotify.com') {
animate();
}
requestAnimationFrame(animateLoop);
}
function initialize() {
const floatingButton = createFloatingButton();
requestAnimationFrame(animateLoop);
autoSelectFormat();
autoDownload();
const isLucidaDomain = window.location.hostname.includes('lucida.');
if (GM_getValue('floatIconEnabled', 'enabled') === 'disabled' || isLucidaDomain) {
floatingButton.style.display = 'none';
}
}
initialize();
})();