Add Free Download buttons to Blendermarket products.
当前为
// ==UserScript==
// @name Free Blendermarket Downloader
// @description Add Free Download buttons to Blendermarket products.
// @icon https://assets.superhivemarket.com/site_assets/images/black_bee.png
// @version 1.2
// @author afkarxyz
// @namespace https://github.com/afkarxyz/misc-scripts/
// @supportURL https://github.com/afkarxyz/misc-scripts/issues
// @license MIT
// @match https://blendermarket.com/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
let lastUrl = location.href;
let observer = null;
let buttonCheckInterval = null;
let retryCount = 0;
const MAX_RETRIES = 10;
const RETRY_DELAY = 500;
function getProductNameFromURL() {
const currentURL = window.location.href;
const match = currentURL.match(/products\/([^/?]+)/);
if (match) {
return match[1];
}
return '';
}
function createCGDownloadURL(productName) {
return `https://cgdownload.ru/?s=${encodeURIComponent(productName.replace(/-/g, ' '))}`;
}
function createGFXCampURL(productName) {
return `https://www.gfxcamp.com/${productName}/`;
}
function clearExistingButtons() {
const existingButtons = document.querySelectorAll('.cgdownload-button, .gfxcamp-button');
existingButtons.forEach(button => button.remove());
}
function createButtonContent(iconUrl, text) {
const container = document.createElement('div');
container.style.display = 'flex';
container.style.alignItems = 'center';
container.style.justifyContent = 'center';
container.style.gap = '8px';
const img = document.createElement('img');
img.src = iconUrl;
img.style.height = '20px';
img.style.width = '20px';
img.alt = `${text} Icon`;
const span = document.createElement('span');
span.textContent = text;
container.appendChild(img);
container.appendChild(span);
return container;
}
function createDownloadButton(className, iconUrl, buttonText, urlCreator) {
const button = document.createElement('button');
button.className = `btn btn-primary d-grid btn-lg shadow w-100 mt-2 ${className}`;
button.appendChild(createButtonContent(iconUrl, buttonText));
button.addEventListener('click', function(e) {
e.preventDefault();
const productName = getProductNameFromURL();
if (productName) {
const downloadURL = urlCreator(productName);
window.open(downloadURL, '_blank');
}
});
return button;
}
function addDownloadButtons() {
if (!window.location.href.includes('/products/')) {
return false;
}
clearExistingButtons();
const originalForm = document.querySelector('.button_to');
if (!originalForm) {
return false;
}
if (document.querySelector('.cgdownload-button') && document.querySelector('.gfxcamp-button')) {
return true;
}
const cgDownloadButton = createDownloadButton(
'cgdownload-button',
'https://cgdownload.ru/wp-content/themes/dark/images/mstile-150x150.png',
'Free Download',
createCGDownloadURL
);
const gfxCampButton = createDownloadButton(
'gfxcamp-button',
'https://www.gfxcamp.com/images/60.jpg',
'Free Download',
createGFXCampURL
);
originalForm.insertAdjacentElement('afterend', gfxCampButton);
originalForm.insertAdjacentElement('afterend', cgDownloadButton);
return true;
}
function startButtonCheck() {
if (buttonCheckInterval) {
clearInterval(buttonCheckInterval);
}
retryCount = 0;
buttonCheckInterval = setInterval(() => {
if (addDownloadButtons() || retryCount >= MAX_RETRIES) {
clearInterval(buttonCheckInterval);
buttonCheckInterval = null;
retryCount = 0;
} else {
retryCount++;
}
}, RETRY_DELAY);
}
function startObserver() {
if (observer) {
observer.disconnect();
}
startButtonCheck();
observer = new MutationObserver((mutations) => {
const hasRelevantChanges = mutations.some(mutation => {
const addedNodes = Array.from(mutation.addedNodes);
return addedNodes.some(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
return node.querySelector('.button_to') ||
node.classList.contains('button_to') ||
node.closest('.button_to');
}
return false;
});
});
if (hasRelevantChanges) {
startButtonCheck();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'style'],
characterData: false
});
}
function setupHistoryListener() {
const pushState = history.pushState;
history.pushState = function() {
pushState.apply(history, arguments);
setTimeout(startObserver, 100);
};
const replaceState = history.replaceState;
history.replaceState = function() {
replaceState.apply(history, arguments);
setTimeout(startObserver, 100);
};
window.addEventListener('popstate', () => setTimeout(startObserver, 100));
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setupHistoryListener();
startObserver();
});
} else {
setupHistoryListener();
startObserver();
}
setInterval(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
setTimeout(startObserver, 100);
}
}, 100);
})();