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.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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);
})();