Greasy Fork is available in English.
Adds a button to find the song on Spotify on the LINE Music website.
// ==UserScript==
// @name Add "Find on Spotify" Button to LINE Music Charts
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Adds a button to find the song on Spotify on the LINE Music website.
// @match https://music.line.me/*
// @author Forkinthe.net
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
function createSpotifyButton(songTitle, artistName) {
let btn = document.createElement('button');
btn.innerText = 'Find on Spotify';
btn.style.marginLeft = '10px';
btn.style.backgroundColor = '#00c55e';
btn.style.border = '1px solid #00c55e';
btn.style.color = '#fff';
btn.style.display = 'inline-block';
btn.style.borderRadius = '4px';
btn.style.fontSize = '15px';
btn.style.lineHeight = '20px';
btn.style.verticalAlign = 'top';
btn.style.textAlign = 'center';
btn.style.cursor = 'pointer';
btn.onclick = function() {
let query = encodeURIComponent(`${songTitle} ${artistName}`);
window.open(`https://open.spotify.com/search/${query}`, '_blank');
};
return btn;
}
function addSpotifyButtons() {
let tracklist = document.querySelector('.tracklist');
if (!tracklist) {
console.log('Tracklist not found.');
return;
}
let rows = tracklist.getElementsByTagName('tr');
console.log(`Found ${rows.length} rows.`);
for (let row of rows) {
// Remove existing Spotify button cells
let existingButtonCell = row.querySelector('.spotify-button-cell');
if (existingButtonCell) {
existingButtonCell.remove();
}
let songCell = row.querySelector('.song');
let artistCell = row.querySelector('.artist');
if (songCell && artistCell) {
let songLink = songCell.querySelector('a.link_text');
let songTitle = songLink ? songLink.title.trim() : null;
let artistName = artistCell.title.trim();
if (songTitle && artistName) {
console.log(`Adding button for: ${songTitle} by ${artistName}`);
let spotifyButton = createSpotifyButton(songTitle, artistName);
spotifyButton.classList.add('spotify-button');
// Create a new td element for the button
let spotifyTd = document.createElement('td');
spotifyTd.classList.add('spotify-button-cell');
spotifyTd.appendChild(spotifyButton);
// Append the new td element to the row
row.appendChild(spotifyTd);
} else {
console.log('Song title or artist name not found in row:', row);
}
} else {
console.log('Song or artist cell not found in row:', row);
}
}
}
// Debounce function to limit how often addSpotifyButtons is called
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const debouncedAddSpotifyButtons = debounce(addSpotifyButtons, 500);
let observer;
function initMutationObserver() {
const tracklist = document.querySelector('.tracklist');
if (tracklist) {
console.log('Tracklist found.');
debouncedAddSpotifyButtons();
// Disconnect previous observer if it exists
if (observer) {
observer.disconnect();
}
// Create a new observer to detect changes in the tracklist
observer = new MutationObserver((mutations) => {
let relevantMutation = mutations.some(mutation =>
Array.from(mutation.addedNodes).some(node => node.nodeType === 1 && node.matches('tr')) ||
Array.from(mutation.removedNodes).some(node => node.nodeType === 1 && node.matches('tr'))
);
if (relevantMutation) {
console.log('Tracklist changed.');
debouncedAddSpotifyButtons();
}
});
// Start observing the tracklist for changes
observer.observe(tracklist, { childList: true });
} else {
console.log('Waiting for tracklist...');
setTimeout(initMutationObserver, 500);
}
}
// Function to monitor navigation changes
function monitorNavigation() {
let currentPath = window.location.pathname;
setInterval(() => {
if (window.location.pathname !== currentPath) {
currentPath = window.location.pathname;
console.log('Navigation detected. Re-initializing observer.');
initMutationObserver();
}
}, 1000);
}
// Run the initMutationObserver function after the page is fully loaded
window.addEventListener('load', () => {
console.log('Page loaded.');
initMutationObserver();
monitorNavigation();
});
})();