Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Spotify - Otwórz link w tle (Na dole menu utworu)
- // @namespace http://tampermonkey.net/
- // @version 2.2
- // @description Dodaje opcję "Otwórz link w tle" na końcu menu kontekstowego utworu Spotify.
- // @author Your Name
- // @match https://open.spotify.com/*
- // @grant GM_openInTab
- // @grant GM_addStyle
- // @run-at document-idle
- // ==/UserScript==
- (function() {
- 'use strict';
- // Optional: Minimal styles if class copying isn't perfect
- GM_addStyle(`
- .custom-background-link-item span { /* Style the inner span if needed */
- /* Example: Add slight padding if text alignment is off */
- /* padding-left: 8px; */
- }
- /* Ensure menu has some padding */
- div[role="menu"], ul[role="menu"] {
- padding-top: 4px !important;
- padding-bottom: 4px !important;
- }
- `);
- // --- Store context data ---
- // Defined outside observer/loops
- let trackContextData = {
- url: null,
- targetElement: null // The element that was right-clicked
- };
- // --- Function to handle the click on the custom menu item ---
- // Defined outside the loop to avoid ESLint warning
- function handleMenuItemClick(urlToOpen, clickEvent) {
- clickEvent.stopPropagation();
- clickEvent.preventDefault(); // Prevent default button actions if any
- console.log('[Spotify Background Link] Opening in background:', urlToOpen);
- if (urlToOpen) {
- GM_openInTab(urlToOpen, { active: false, insert: true });
- }
- // Attempt to close the menu - hiding is usually sufficient
- const menuToClose = clickEvent.target.closest('[role="menu"]');
- if (menuToClose) {
- menuToClose.style.display = 'none';
- // Force removal if hiding doesn't work consistently (might break if parent changes)
- // setTimeout(() => menuToClose.parentElement?.remove(), 50);
- }
- // Clear context data after use (accessing the global is okay here)
- trackContextData = { url: null, targetElement: null };
- }
- // --- Capture right-click event to get track info ---
- document.addEventListener('contextmenu', (e) => {
- // Reset context data before checking the new target
- trackContextData = { url: null, targetElement: null };
- // Find the closest track row ancestor from the clicked element
- const trackRow = e.target.closest('div[data-testid="tracklist-row"], div.tracklist-row'); // Find the track row itself
- if (trackRow) {
- // Find the link within that specific track row
- const linkElement = trackRow.querySelector('a[data-testid="internal-track-link"], a[href*="/track/"]');
- if (linkElement && linkElement.href) {
- trackContextData.url = linkElement.href;
- trackContextData.targetElement = e.target; // Store the element that triggered the context menu
- // console.log('[Spotify Background Link] Context captured:', trackContextData.url);
- } else {
- // console.log('[Spotify Background Link] Track row found, but no track link element within it.');
- }
- } else {
- // console.log('[Spotify Background Link] Right-click was not on or inside a track row.');
- }
- }, true); // Use capture phase
- // --- Observe for context menu appearance ---
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- for (const node of mutation.addedNodes) {
- if (node.nodeType === Node.ELEMENT_NODE) {
- // Find the menu, could be div or ul
- let menu = null;
- if (node.matches && node.matches('[role="menu"]')) {
- menu = node;
- } else if (node.querySelector) {
- // Sometimes the menu is nested deeper in the added node (e.g., portals)
- menu = node.querySelector('[role="menu"]');
- }
- // Proceed only if a menu was found, we have a URL from context, and we haven't modified this menu yet
- // Read the URL from the *current* trackContextData state
- const currentUrl = trackContextData.url;
- if (menu && currentUrl && !menu.dataset.customLinkAdded) {
- // console.log('[Spotify Background Link] Context menu detected:', menu);
- // Find the "Dodaj do playlisty" button *only* to confirm it's the right menu and copy styles
- const menuItems = menu.querySelectorAll('button[role="menuitem"]');
- let referenceButton = null; // Button to copy styles from
- const targetText = "Dodaj do playlisty";
- for (const item of menuItems) {
- // Check text content of the button or its inner span
- const buttonText = (item.textContent || "").trim();
- const spanText = item.querySelector('span')?.textContent?.trim();
- if (buttonText.includes(targetText) || (spanText && spanText.includes(targetText))) {
- referenceButton = item; // Found a good button to copy styles from
- // console.log('[Spotify Background Link] Found reference button:', referenceButton);
- break;
- }
- }
- // We need a reference button to copy styles, even if we append at the end
- if (referenceButton) {
- // Create the new menu item as a button
- const newMenuItemButton = document.createElement('button');
- newMenuItemButton.setAttribute('role', 'menuitem');
- newMenuItemButton.setAttribute('type', 'button'); // Good practice
- newMenuItemButton.setAttribute('tabindex', '0'); // Make it focusable
- // --- Styling: Copy classes from the reference button ---
- newMenuItemButton.className = referenceButton.className;
- newMenuItemButton.classList.add('custom-background-link-item'); // Add our custom class
- // --- Structure: Add text inside a span like Spotify likely does ---
- const textSpan = document.createElement('span');
- textSpan.textContent = "Otwórz link w tle";
- // Optionally copy span classes from referenceButton's span if needed
- const originalSpan = referenceButton.querySelector('span');
- if(originalSpan) {
- // Be careful not to copy classes that might conflict
- // textSpan.className = originalSpan.className;
- }
- newMenuItemButton.appendChild(textSpan);
- // --- Add click listener ---
- // Pass the URL captured *before* this specific listener was added
- newMenuItemButton.addEventListener('click', (clickEvent) => {
- // Call the function defined outside the loop
- handleMenuItemClick(currentUrl, clickEvent);
- });
- // --- Append to the end of the menu ---
- // Append to the menu element itself (div or ul with role="menu")
- menu.appendChild(newMenuItemButton);
- // console.log('[Spotify Background Link] Appended "Otwórz link w tle" item to the end.');
- // Mark this specific menu instance as modified
- menu.dataset.customLinkAdded = 'true';
- } else {
- // console.log('[Spotify Background Link] Reference button ("Dodaj do playlisty") not found in this menu. Cannot add item.');
- }
- }
- }
- }
- }
- });
- // Start observing the body for added nodes (menus)
- observer.observe(document.body, {
- childList: true, // Watch for direct children additions
- subtree: true // Watch for additions in descendants too (important for portals)
- });
- console.log('[Spotify Background Link] Script v2.2 loaded and observing.');
- })();
Advertisement