CurseForge Get

Adds direct "Get" download buttons across CurseForge for all games, categories, and resources, including mods, modpacks, maps, plugins, etc., simplifying access to downloads.

// ==UserScript==
// @name         CurseForge Get
// @namespace    http://tampermonkey.net/
// @version      1.2.0
// @description  Adds direct "Get" download buttons across CurseForge for all games, categories, and resources, including mods, modpacks, maps, plugins, etc., simplifying access to downloads.
// @author       Vulpeep
// @homepage     https://github.com/vulpeep
// @match        https://*.curseforge.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=curseforge.com
// @run-at document-end
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    function addStyles() {
        const style = document.createElement('style');
        style.innerHTML = `
            .btn-get {
              background-color: #1ea7b8!important;
            }
            .btn-get:hover {
              background-color: #3bc0d1!important;
            }
          `;
        document.head.appendChild(style);
    }

    function updateOrAddHeaderGetButton() {
        const projectHeaderActions = document.querySelector('.project-header > .actions > .split-button');
        if (projectHeaderActions) {
            const downloadButton = projectHeaderActions.querySelector('a.download-cta');
            if (downloadButton) {
                const href = downloadButton.getAttribute('href');
                const fileId = href.split('/').pop();
                const newHref = `/api/v1/mods/0/files/${fileId}/download`;

                let customButton = projectHeaderActions.querySelector('a.btn-get');
                if (!customButton) {
                    customButton = document.createElement('a');
                    customButton.setAttribute('class', 'btn-cta btn-get');
                    customButton.innerHTML = '<svg class="smaller-icon"><use href="/images/sprite.svg#icon-download"></use></svg><span>Get</span>';
                    projectHeaderActions.appendChild(customButton);
                }

                customButton.setAttribute('href', newHref);
            }
        }
    }

    function addGetButtonToFileRows(fileRowElement) {
        const fileRowDetails = fileRowElement.querySelector('.file-row-details');
        const href = fileRowDetails.getAttribute('href');
        const fileId = href.split('/').pop();
        const kebabMenu = fileRowElement.querySelector('.kebab-menu');
        const moreOptions = kebabMenu.querySelector('ul');

        if (moreOptions && !moreOptions.querySelector('.btn-get')) {
            const newHref = `/api/v1/mods/0/files/${fileId}/download`;
            const listItem = document.createElement('li');
            listItem.innerHTML = `<a href="${newHref}"><svg class="smaller-icon"><use href="/images/sprite.svg#icon-download"></use></svg>Get</a>`;

            listItem.querySelector('a').addEventListener('click', (event) => {
                kebabMenu.classList.remove('is-open');
                event.stopPropagation();
            });

            moreOptions.appendChild(listItem);
        }
    }

    function addGetButtonToMemberProjectCard(memberProjectCardElement) {
        const actions = memberProjectCardElement.querySelector('.actions .split-button');
        if (actions) {
            const downloadButton = actions.querySelector('a.download-cta');
            if (downloadButton) {
                const href = downloadButton.getAttribute('href');
                const fileId = href.split('/').pop();

                if (fileId === 'download') return;

                const newHref = `/api/v1/mods/0/files/${fileId}/download`;
                const getButton = document.createElement('a');
                getButton.innerHTML = '<svg class="smaller-icon"><use href="/images/sprite.svg#icon-download"></use></svg><span>Get</span>';
                getButton.setAttribute('class', 'btn-cta btn-get');
                getButton.setAttribute('href', newHref);

                getButton.addEventListener('click', (event) => {
                    event.stopPropagation();
                });

                actions.appendChild(getButton);
            }
        }
    }

    function addGetButtonToProjectCard(projectCardElement) {
        const actions = projectCardElement.querySelector('.actions-container .split-button');
        if (actions) {
            const downloadButton = actions.querySelector('a.download-cta');
            if (downloadButton) {
                const href = downloadButton.getAttribute('href');
                const fileId = href.split('/').pop();

                if (fileId === 'download') return;

                const newHref = `/api/v1/mods/0/files/${fileId}/download`;
                const getButton = document.createElement('a');
                getButton.innerHTML = '<svg class="smaller-icon"><use href="/images/sprite.svg#icon-download"></use></svg><span>Get</span>';
                getButton.setAttribute('class', 'btn-cta btn-get');
                getButton.setAttribute('href', newHref);

                getButton.addEventListener('click', (event) => {
                    event.stopPropagation();
                });

                actions.appendChild(getButton);
            }
        }
    }

    function getFileIdFromUrl() {
        const pathParts = window.location.pathname.split('/');
        const fileIdIndex = pathParts.findIndex(part => part === 'files') + 1;
        return fileIdIndex < pathParts.length ? pathParts[fileIdIndex] : null;
    }

    function updateOrAddFileDetailsGetButton(fileDetailsActionsElement) {
        const fileId = getFileIdFromUrl();
        if (!fileId) return;

        const newHref = `/api/v1/mods/0/files/${fileId}/download`;

        let customButton = fileDetailsActionsElement.querySelector('a.btn-get');
        if (!customButton) {
            customButton = document.createElement('a');
            customButton.setAttribute('class', 'btn-cta btn-get');
            customButton.innerHTML = '<svg class="smaller-icon"><use href="/images/sprite.svg#icon-download"></use></svg><span>Get</span>';
            fileDetailsActionsElement.appendChild(customButton);
        }

        customButton.setAttribute('href', newHref);
    }

    function onModalAdded(modalElement) {
        const actionsElement = modalElement.querySelector('.actions');

        const downloadButton = document.createElement('button');
        downloadButton.className = 'btn-primary btn-get';
        downloadButton.textContent = 'Get';

        downloadButton.onclick = function () {
            const fileCard = modalElement.querySelector('a.file-card');
            if (fileCard) {
                const href = fileCard.getAttribute('href');
                const fileId = href.split('/').pop();
                window.location.href = `/api/v1/mods/0/files/${fileId}/download`;
            }
        };

        if (actionsElement) {
            actionsElement.insertBefore(downloadButton, actionsElement.firstChild);
        }
    }

    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            mutation.addedNodes.forEach((node) => {
                if (node.nodeType === 1) {
                    console.log(node.classList);
                    if (node.classList.contains('modal-container') && node.querySelector('.download-details')) {
                        onModalAdded(node);
                    }
                    if (node.classList.contains('file-details')) {
                        updateOrAddFileDetailsGetButton(node.querySelector('.actions .split-button'));
                    }
                    if (node.classList.contains('file-row')) {
                        addGetButtonToFileRows(node);
                    }
                    if (node.classList.contains('results-container')) {
                        node.querySelectorAll('.project-card').forEach(addGetButtonToProjectCard);
                    }
                    if (node.classList.contains('member-projects-cards-list')) {
                        node.querySelectorAll('.member-project-card').forEach(addGetButtonToMemberProjectCard);
                    }
                }
            });
        });
    });

    const config = {
        childList: true,
        subtree: true
    };

    observer.observe(document, config);

    window.addEventListener('popstate', () => {
        updateOrAddFileDetailsGetButton(document.querySelector('.file-details .actions .split-button'));
        updateOrAddHeaderGetButton();
    });

    addStyles();

    setTimeout(() => {
        document.querySelectorAll('.project-card').forEach(addGetButtonToProjectCard);
        document.querySelectorAll('.member-project-card').forEach(addGetButtonToMemberProjectCard);
        updateOrAddHeaderGetButton();
    }, 0);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址