Fetch Download Links on FreeSound.org

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Fetch Download Links on FreeSound.org
// @namespace    Violentmonkey Scripts
// @version      1.0.1
// @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, 10: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
		if (parentElement.classList.contains('bw-search__result')) {
			// If parentElement has class bw-search__result, append the grabberDiv as the second to last element
			const lastChild = parentElement.lastElementChild;
			parentElement.insertBefore(grabberDiv, lastChild);
		} else {
			// Else, appendChild normally
			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);