Fetch Download Links on FreeSound.org

Adds download links on freesound.org. Works with a button press, or automatically.

目前為 2024-04-23 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Fetch Download Links on FreeSound.org
// @namespace    Violentmonkey Scripts
// @version      1.0
// @description  Adds download links on freesound.org. Works with a button press, or automatically.
// @author       Jupiter Liar
// @license      CC BY
// @match        https://freesound.org/*
// @description  4/23/2024, 2:30:00 AM
// @grant        none
// ==/UserScript==

// Function to handle grabbing HTML from linked page and replacing Grab button
function grabHtmlAndReplaceButton(button) {
  button.textContent = "Fetching...";
    // Find the parent search result element
    const searchResult = button.closest('.bw-search__result');

    // Find the first <a> element within the search result if available
    var link;
    if (searchResult) {
        link = searchResult.querySelector('a');
    } else {
        // If searchResult is not found, traverse up the DOM tree until we find an ancestor containing .bw-player
        var ancestor = button.parentElement;
        while (ancestor && !(ancestor.querySelector('.bw-player') && ancestor.querySelector('a'))) {
            ancestor = ancestor.parentElement;
        }
        // Once we find the ancestor containing .bw-player, find the first <a> element within it
        if (ancestor) {
            link = ancestor.querySelector('a');
        }
    }

    // Check if a link is found
    if (link) {
        // Fetch the linked page
        fetch(link.href)
            .then(response => response.text())
            .then(html => {
                // Create a temporary element to hold the fetched HTML
                const tempElement = document.createElement('div');
                tempElement.innerHTML = html;
                // Find the element with class "sound-download-button"
                const soundDownloadButton = tempElement.querySelector('.sound-download-button');
                const sidebarDownloadButton = tempElement.querySelector('.bw-sound__sidebar .btn-primary');
                // Replace the Grab button with the sound-download-button element
                if (soundDownloadButton) {
                    // Replace the Grab button with the sound-download-button element
                    button.parentNode.replaceChild(soundDownloadButton, button);
                } else if (sidebarDownloadButton) {
                    button.parentNode.replaceChild(sidebarDownloadButton, button);
                } else {
                    console.log('No download button found on the linked page');
                    // Update the button text content if no download button is found
                    button.innerText = "No button found";
                }
            })
            .catch(error => {
                console.error('Error fetching linked page:', error);
                button.textContent = "Error fetching page";
            });
    } else {
        console.log('No link found in search result');
    }
}

// Function to create and append grab buttons
function createGrabButtons(parentElement, grabMode, additionalClass) {
    // If grabMode is true, insert grab buttons
    if (grabMode) {
        const grabberDiv = document.createElement('div');
        grabberDiv.classList.add('grabber');
        if (additionalClass) {
            grabberDiv.classList.add(additionalClass); // Add additional class if it exists
        }
        // grabberDiv.classList.add('in-row'); // Add in-row class
        // Create the Grab button
        const grabButton = document.createElement('button');
        grabButton.classList.add('grab-button');
        grabButton.textContent = 'Get download button';

        // Add event listener to the Grab button
        grabButton.addEventListener('click', function () {
            grabHtmlAndReplaceButton(this);
        });

        // Append the Grab button to the grabber div
        grabberDiv.appendChild(grabButton);

        // Append the grabber div to the parent element
        parentElement.appendChild(grabberDiv);
    }
}

function createInsertAllButton(topLevelElement) {
  // Create the container for "Insert all download buttons" button
        const insertAllContainer = document.createElement('div');
        insertAllContainer.classList.add('show-all-download-buttons');

        // Create the "Insert all download buttons" button
        const insertAllButton = document.createElement('button');
        insertAllButton.classList.add('btn-primary', 'w-100', 'insert-all-download-buttons');
        insertAllButton.textContent = 'Insert all download buttons';

        // Add event listener to the "Insert all download buttons" button
        insertAllButton.addEventListener('click', function () {
            clickAllGrabButtons();
        });

        // Append the button to the container
        insertAllContainer.appendChild(insertAllButton);

        // Insert the container before the very first search result
        topLevelElement.parentNode.insertBefore(insertAllContainer, topLevelElement);

        // Create the div for auto-insert option
        const autoInsertDiv = document.createElement('div');
        autoInsertDiv.classList.add('auto-insert-div');

        // Create the checkbox for auto-insert option
        const autoInsertCheckbox = document.createElement('input');
        autoInsertCheckbox.type = 'checkbox';
        autoInsertCheckbox.checked = localStorage.getItem('autoInsertCheckbox') === 'true'; // Check local storage for value

        // Add event listener to the auto-insert checkbox
        autoInsertCheckbox.addEventListener('change', handleAutoInsertChange);

        // Create the span for auto-insert option
        const autoInsertSpan = document.createElement('span');
        autoInsertSpan.textContent = 'Do this automatically';

        // Append checkbox and span to auto-insert div
        autoInsertDiv.appendChild(autoInsertCheckbox);
        autoInsertDiv.appendChild(autoInsertSpan);

        // Append auto-insert div to show-all-download-buttons container
        insertAllContainer.appendChild(autoInsertDiv);

        // Check if the checkbox is checked
        if (autoInsertCheckbox.checked) {
            clickAllGrabButtons();
        }
}


// Check if the current URL includes freesound.org/search
if (window.location.href.includes("freesound.org/search")) {
    console.log('URL matched: freesound.org/search');

    // Find all instances of '.bw-search__result' within the 'main' element
    const searchResults = document.querySelectorAll('main .bw-search__result');

    // Loop through each search result
    searchResults.forEach(result => {
        // Find the first <a> element within the search result
        const firstLink = result.querySelector('a');

        // Check if a <a> element is found
        if (firstLink) {
            // Print the href attribute of the first <a> element to the console
            console.log('First link href:', firstLink.href);

            // If grabMode is true, insert a new div at the end of the search result
            createGrabButtons(result, true, '');
        } else {
            console.log('No link found in search result');
        }
    });

    // Find the very first search result element
    const firstSearchResult = document.querySelector('main .bw-search__result:first-child');

    // Check if the first search result element is found
    if (firstSearchResult) {
        createInsertAllButton(firstSearchResult);
    } else {
        console.log('No search results found');
    }
} else {
    console.log('URL did not match: freesound.org/search');

    // Check if the page includes one or more instances of .bw-player
    const bwPlayers = document.querySelectorAll('.bw-player');
    if (bwPlayers.length >= 1) {
        console.log('One or more instances of .bw-player were found:', bwPlayers.length);

      // Initialize an array to store common ancestors
        var commonAncestors = [];

        // Loop through each .bw-player instance
        bwPlayers.forEach(player => {
          // Initialize an array to store ancestors of the current .bw-player instance
            var ancestors = [];

            // Find the parent element of the .bw-player instance
            const parentElement = player.parentNode;
            // Create and append grab buttons
            createGrabButtons(parentElement, true, 'in-row');

// Traverse up the DOM tree and store ancestors until reaching the document body
            var ancestor = player.parentNode;
            while (ancestor !== document.body) {
                ancestors.push(ancestor);
                ancestor = ancestor.parentNode;
            }

            // Add ancestors of the current .bw-player instance to the common ancestors array
            commonAncestors = commonAncestors.length === 0 ? ancestors : commonAncestors.filter(ancestor => ancestors.includes(ancestor));
        });

// Find the lowest-level common ancestor
      console.log("commonAncestors: ");
      commonAncestors.forEach(ancestor => {
            console.log(ancestor);
        });

function findLowestLevelCommonAncestor(commonAncestors) {
    var lowestCommonAncestor = commonAncestors[0]; // Initialize with the first element

    // Iterate through the common ancestors array
    for (let i = 1; i < commonAncestors.length; i++) {
        const currentAncestor = commonAncestors[i];
        // Check if the current ancestor is deeper in the hierarchy than the current lowest common ancestor
        if (lowestCommonAncestor.contains(currentAncestor)) {
            lowestCommonAncestor = currentAncestor;
        }
    }

    return lowestCommonAncestor;
}

        const lowestCommonAncestor = findLowestLevelCommonAncestor(commonAncestors);
        console.log('Lowest level common ancestor:', lowestCommonAncestor);

        // Pass the lowest-level common ancestor to the createInsertAllButton function
        createInsertAllButton(lowestCommonAncestor);
    } else {
        console.log('No instances of .bw-player were found');
    }
}

// Function to handle auto-insert checkbox change
function handleAutoInsertChange() {
    // Get the auto-insert checkbox
    const autoInsertCheckbox = document.querySelector('.auto-insert-div input[type="checkbox"]');

    // Update the local storage value
    localStorage.setItem('autoInsertCheckbox', autoInsertCheckbox.checked);

    // Check if the checkbox is checked
    if (autoInsertCheckbox.checked) {
        clickAllGrabButtons();
    }
}

// Function to click all grab buttons
function clickAllGrabButtons() {
    const grabButtons = document.querySelectorAll('.grab-button');
    grabButtons.forEach(button => {
        grabHtmlAndReplaceButton(button);
    });
}

// Add stylesheet to the page head
const style = document.createElement('style');
style.id = 'new-script-css';
style.innerHTML = `
    .grabber {
        width: 100%;
        margin: 0 0 12px;
        text-align: center;
    }

    .grabber .grab-button {
        background: #CCC;
        padding: 4px 8px;
        border: 1px solid black;
        border-radius: 1em;
    }

    .grabber.in-row {
        margin-top: 12px;
    }

    .grabber.in-row .grab-button {
        font-size: .75em;
    }

    .grabber.in-row .btn-primary {
      padding: 9px 21px;
    }

    .show-all-download-buttons {
        margin-bottom: 32px;
        text-align: center;
        --sadb-border-style: 1px solid hsla(0, 0%, 0%, .5);
        border-bottom: var(--sadb-border-style);
        border-top: var(--sadb-border-style);
    }

    .insert-all-download-buttons {
        margin: 12px 0 6px;
    }

    .auto-insert-div {
        margin-bottom: 12px;
        justify-content: center;
        align-items: center;
        display: flex;
    }

    .auto-insert-div * {
        margin: 0 .35em;
    }
`;
document.head.appendChild(style);