Greasy Fork is available in English.
Added CGDownload and GFXCamp buttons for downloading paid items from Blendermarket.
- // ==UserScript==
- // @name Blendermarket Downloader
- // @description Added CGDownload and GFXCamp buttons for downloading paid items from Blendermarket.
- // @icon https://assets.superhivemarket.com/site_assets/images/black_bee.png
- // @version 1.4
- // @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 observer;
- let buttonCheckInterval;
- let retryCount = 0;
- const MAX_RETRIES = 10;
- const RETRY_DELAY = 500;
- const ICONS = {
- cgdownload: 'https://raw.githubusercontent.com/afkarxyz/misc-scripts/refs/heads/main/blendermarket/cgdownload.png',
- gfxcamp: 'https://raw.githubusercontent.com/afkarxyz/misc-scripts/refs/heads/main/blendermarket/gfxcamp.png'
- };
- function addStyles() {
- const styles = `
- .download-btn {
- background: linear-gradient(120deg, #6800f0, #ff6b00);
- color: white;
- border: none;
- padding: 4px;
- border-radius: 4px;
- cursor: pointer;
- position: absolute;
- transition: all 0.3s ease;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 28px;
- height: 28px;
- }
- .download-btn:hover {
- background: linear-gradient(120deg, #5600c7, #e65d00);
- }
- .download-btn.cgdownload-btn {
- right: 45px;
- bottom: 15px;
- }
- .download-btn.gfxcamp-btn {
- right: 12px;
- bottom: 15px;
- }
- .card-body {
- position: relative;
- }
- .download-btn img {
- width: 16px;
- height: 16px;
- object-fit: contain;
- }
- `;
- const styleSheet = document.createElement('style');
- styleSheet.textContent = styles;
- document.head.appendChild(styleSheet);
- }
- function getProductNameFromURL() {
- const currentURL = window.location.href;
- const match = currentURL.match(/products\/([^/?]+)/);
- return match ? match[1] : '';
- }
- function getProductNameFromLink(cardBody) {
- const cardProduct = cardBody.closest('.card.card-product');
- if (cardProduct) {
- const link = cardProduct.querySelector('a[href*="/products/"]');
- if (link) {
- const match = link.href.match(/products\/([^/?]+)/);
- return match ? match[1] : '';
- }
- }
- const link = cardBody.querySelector('a[href*="/products/"]');
- if (link) {
- const match = link.href.match(/products\/([^/?]+)/);
- return match ? 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('.download-btn, .cgdownload-button, .gfxcamp-button');
- existingButtons.forEach(button => button.remove());
- }
- function createCardButton(type, text, urlCreator, iconUrl, className) {
- const button = document.createElement('button');
- button.className = `download-btn ${className}`;
- const icon = document.createElement('img');
- icon.src = iconUrl;
- icon.alt = text;
- button.appendChild(icon);
- return button;
- }
- function addCardButtons() {
- const cardBodies = document.querySelectorAll('.card.card-product .card-body');
- cardBodies.forEach(cardBody => {
- if (!cardBody.closest('.card.card-product')) return;
- const existingButtons = cardBody.querySelectorAll('.download-btn');
- existingButtons.forEach(button => button.remove());
- const cgDownloadButton = createCardButton(
- 'cgdownload',
- 'CGDownload',
- createCGDownloadURL,
- ICONS.cgdownload,
- 'cgdownload-btn'
- );
- const gfxCampButton = createCardButton(
- 'gfxcamp',
- 'GFXCamp',
- createGFXCampURL,
- ICONS.gfxcamp,
- 'gfxcamp-btn'
- );
- cgDownloadButton.addEventListener('click', (e) => {
- e.preventDefault();
- const productName = getProductNameFromLink(cardBody);
- if (productName) {
- window.open(createCGDownloadURL(productName), '_blank');
- }
- });
- gfxCampButton.addEventListener('click', (e) => {
- e.preventDefault();
- const productName = getProductNameFromLink(cardBody);
- if (productName) {
- window.open(createGFXCampURL(productName), '_blank');
- }
- });
- cardBody.appendChild(cgDownloadButton);
- cardBody.appendChild(gfxCampButton);
- });
- }
- function createButton(className, text, urlCreator, iconUrl) {
- const originalButton = document.querySelector('.button_to input[type="submit"]');
- if (!originalButton) return null;
- const button = document.createElement('button');
- button.className = originalButton.className;
- button.classList.add(className);
- if (originalButton.style.cssText) {
- button.style.cssText = originalButton.style.cssText;
- }
- const contentWrapper = document.createElement('div');
- contentWrapper.style.display = 'flex';
- contentWrapper.style.alignItems = 'center';
- contentWrapper.style.justifyContent = 'center';
- contentWrapper.style.gap = '8px';
- const icon = document.createElement('img');
- icon.src = iconUrl;
- icon.alt = text;
- icon.style.width = '20px';
- icon.style.height = '20px';
- icon.style.objectFit = 'contain';
- const textSpan = document.createElement('span');
- textSpan.textContent = text;
- contentWrapper.appendChild(icon);
- contentWrapper.appendChild(textSpan);
- button.appendChild(contentWrapper);
- button.addEventListener('click', function(e) {
- e.preventDefault();
- const productName = getProductNameFromURL();
- if (productName) {
- const downloadURL = urlCreator(productName);
- window.open(downloadURL, '_blank');
- }
- });
- return button;
- }
- function addProductPageButtons() {
- if (!window.location.href.includes('/products/')) {
- return;
- }
- clearExistingButtons();
- const originalForm = document.querySelector('.button_to');
- if (!originalForm) {
- return;
- }
- if (document.querySelector('.cgdownload-button') && document.querySelector('.gfxcamp-button')) {
- return;
- }
- const priceElement = document.querySelector('.js-price-cart');
- if (priceElement) {
- priceElement.classList.remove('d-none', 'd-md-block');
- priceElement.classList.add('text-center');
- priceElement.style.marginBottom = '1rem';
- priceElement.style.display = 'block';
- priceElement.style.width = '100%';
- originalForm.parentNode.insertBefore(priceElement, originalForm);
- }
- const cgDownloadButton = createButton(
- 'cgdownload-button',
- 'CGDownload',
- createCGDownloadURL,
- ICONS.cgdownload
- );
- const gfxCampButton = createButton(
- 'gfxcamp-button',
- 'GFXCamp',
- createGFXCampURL,
- ICONS.gfxcamp
- );
- if (cgDownloadButton && gfxCampButton) {
- const wrapper = document.createElement('div');
- wrapper.style.marginTop = '0.5rem';
- wrapper.appendChild(cgDownloadButton);
- const wrapper2 = document.createElement('div');
- wrapper2.style.marginTop = '0.5rem';
- wrapper2.appendChild(gfxCampButton);
- originalForm.insertAdjacentElement('afterend', wrapper2);
- originalForm.insertAdjacentElement('afterend', wrapper);
- }
- }
- function addAllButtons() {
- addStyles();
- addProductPageButtons();
- addCardButtons();
- }
- function startButtonCheck() {
- if (buttonCheckInterval) {
- clearInterval(buttonCheckInterval);
- }
- retryCount = 0;
- buttonCheckInterval = setInterval(() => {
- if (addAllButtons() || 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, .card-body') ||
- node.classList.contains('button_to', 'card-body') ||
- node.closest('.button_to, .card-body');
- }
- 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();
- }
- })();