SheridanCoriana6

Spotify- Open link in background

Apr 12th, 2025
655
0
Never
7
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 8.82 KB | Source Code | 0 0
  1. // ==UserScript==
  2. // @name         Spotify - Otwórz link w tle (Na dole menu utworu)
  3. // @namespace    http://tampermonkey.net/
  4. // @version      2.2
  5. // @description  Dodaje opcję "Otwórz link w tle" na końcu menu kontekstowego utworu Spotify.
  6. // @author       Your Name
  7. // @match        https://open.spotify.com/*
  8. // @grant        GM_openInTab
  9. // @grant        GM_addStyle
  10. // @run-at       document-idle
  11. // ==/UserScript==
  12.  
  13. (function() {
  14.     'use strict';
  15.  
  16.     // Optional: Minimal styles if class copying isn't perfect
  17.     GM_addStyle(`
  18.         .custom-background-link-item span { /* Style the inner span if needed */
  19.             /* Example: Add slight padding if text alignment is off */
  20.              /* padding-left: 8px; */
  21.         }
  22.          /* Ensure menu has some padding */
  23.         div[role="menu"], ul[role="menu"] {
  24.              padding-top: 4px !important;
  25.              padding-bottom: 4px !important;
  26.         }
  27.     `);
  28.  
  29.     // --- Store context data ---
  30.     // Defined outside observer/loops
  31.     let trackContextData = {
  32.         url: null,
  33.         targetElement: null // The element that was right-clicked
  34.     };
  35.  
  36.     // --- Function to handle the click on the custom menu item ---
  37.     // Defined outside the loop to avoid ESLint warning
  38.     function handleMenuItemClick(urlToOpen, clickEvent) {
  39.         clickEvent.stopPropagation();
  40.         clickEvent.preventDefault(); // Prevent default button actions if any
  41.         console.log('[Spotify Background Link] Opening in background:', urlToOpen);
  42.         if (urlToOpen) {
  43.             GM_openInTab(urlToOpen, { active: false, insert: true });
  44.         }
  45.         // Attempt to close the menu - hiding is usually sufficient
  46.         const menuToClose = clickEvent.target.closest('[role="menu"]');
  47.         if (menuToClose) {
  48.              menuToClose.style.display = 'none';
  49.              // Force removal if hiding doesn't work consistently (might break if parent changes)
  50.              // setTimeout(() => menuToClose.parentElement?.remove(), 50);
  51.         }
  52.         // Clear context data after use (accessing the global is okay here)
  53.         trackContextData = { url: null, targetElement: null };
  54.     }
  55.  
  56.  
  57.     // --- Capture right-click event to get track info ---
  58.     document.addEventListener('contextmenu', (e) => {
  59.         // Reset context data before checking the new target
  60.         trackContextData = { url: null, targetElement: null };
  61.  
  62.         // Find the closest track row ancestor from the clicked element
  63.         const trackRow = e.target.closest('div[data-testid="tracklist-row"], div.tracklist-row'); // Find the track row itself
  64.         if (trackRow) {
  65.             // Find the link within that specific track row
  66.             const linkElement = trackRow.querySelector('a[data-testid="internal-track-link"], a[href*="/track/"]');
  67.             if (linkElement && linkElement.href) {
  68.                 trackContextData.url = linkElement.href;
  69.                 trackContextData.targetElement = e.target; // Store the element that triggered the context menu
  70.                 // console.log('[Spotify Background Link] Context captured:', trackContextData.url);
  71.             } else {
  72.                 // console.log('[Spotify Background Link] Track row found, but no track link element within it.');
  73.             }
  74.         } else {
  75.             // console.log('[Spotify Background Link] Right-click was not on or inside a track row.');
  76.         }
  77.     }, true); // Use capture phase
  78.  
  79.     // --- Observe for context menu appearance ---
  80.     const observer = new MutationObserver((mutations) => {
  81.         for (const mutation of mutations) {
  82.             for (const node of mutation.addedNodes) {
  83.                 if (node.nodeType === Node.ELEMENT_NODE) {
  84.                     // Find the menu, could be div or ul
  85.                     let menu = null;
  86.                     if (node.matches && node.matches('[role="menu"]')) {
  87.                          menu = node;
  88.                     } else if (node.querySelector) {
  89.                          // Sometimes the menu is nested deeper in the added node (e.g., portals)
  90.                          menu = node.querySelector('[role="menu"]');
  91.                     }
  92.  
  93.                     // Proceed only if a menu was found, we have a URL from context, and we haven't modified this menu yet
  94.                     // Read the URL from the *current* trackContextData state
  95.                     const currentUrl = trackContextData.url;
  96.  
  97.                     if (menu && currentUrl && !menu.dataset.customLinkAdded) {
  98.                         // console.log('[Spotify Background Link] Context menu detected:', menu);
  99.  
  100.                         // Find the "Dodaj do playlisty" button *only* to confirm it's the right menu and copy styles
  101.                         const menuItems = menu.querySelectorAll('button[role="menuitem"]');
  102.                         let referenceButton = null; // Button to copy styles from
  103.                         const targetText = "Dodaj do playlisty";
  104.  
  105.                         for (const item of menuItems) {
  106.                              // Check text content of the button or its inner span
  107.                             const buttonText = (item.textContent || "").trim();
  108.                             const spanText = item.querySelector('span')?.textContent?.trim();
  109.  
  110.                             if (buttonText.includes(targetText) || (spanText && spanText.includes(targetText))) {
  111.                                 referenceButton = item; // Found a good button to copy styles from
  112.                                 // console.log('[Spotify Background Link] Found reference button:', referenceButton);
  113.                                 break;
  114.                             }
  115.                         }
  116.  
  117.                         // We need a reference button to copy styles, even if we append at the end
  118.                         if (referenceButton) {
  119.                             // Create the new menu item as a button
  120.                             const newMenuItemButton = document.createElement('button');
  121.                             newMenuItemButton.setAttribute('role', 'menuitem');
  122.                             newMenuItemButton.setAttribute('type', 'button'); // Good practice
  123.                             newMenuItemButton.setAttribute('tabindex', '0'); // Make it focusable
  124.  
  125.                             // --- Styling: Copy classes from the reference button ---
  126.                             newMenuItemButton.className = referenceButton.className;
  127.                             newMenuItemButton.classList.add('custom-background-link-item'); // Add our custom class
  128.  
  129.                             // --- Structure: Add text inside a span like Spotify likely does ---
  130.                             const textSpan = document.createElement('span');
  131.                             textSpan.textContent = "Otwórz link w tle";
  132.                              // Optionally copy span classes from referenceButton's span if needed
  133.                              const originalSpan = referenceButton.querySelector('span');
  134.                              if(originalSpan) {
  135.                                 // Be careful not to copy classes that might conflict
  136.                                 // textSpan.className = originalSpan.className;
  137.                              }
  138.                             newMenuItemButton.appendChild(textSpan);
  139.  
  140.                             // --- Add click listener ---
  141.                             // Pass the URL captured *before* this specific listener was added
  142.                             newMenuItemButton.addEventListener('click', (clickEvent) => {
  143.                                 // Call the function defined outside the loop
  144.                                 handleMenuItemClick(currentUrl, clickEvent);
  145.                             });
  146.  
  147.                             // --- Append to the end of the menu ---
  148.                             // Append to the menu element itself (div or ul with role="menu")
  149.                             menu.appendChild(newMenuItemButton);
  150.                             // console.log('[Spotify Background Link] Appended "Otwórz link w tle" item to the end.');
  151.  
  152.                             // Mark this specific menu instance as modified
  153.                             menu.dataset.customLinkAdded = 'true';
  154.  
  155.                         } else {
  156.                              // console.log('[Spotify Background Link] Reference button ("Dodaj do playlisty") not found in this menu. Cannot add item.');
  157.                         }
  158.                     }
  159.                  }
  160.             }
  161.         }
  162.     });
  163.  
  164.     // Start observing the body for added nodes (menus)
  165.     observer.observe(document.body, {
  166.         childList: true, // Watch for direct children additions
  167.         subtree: true // Watch for additions in descendants too (important for portals)
  168.     });
  169.  
  170.     console.log('[Spotify Background Link] Script v2.2 loaded and observing.');
  171.  
  172. })();
Tags: tampermonkey
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • Fexkokar
    46 days
    Comment was deleted
  • Korpotan
    39 days
    Comment was deleted
Add Comment
Please, Sign In to add comment