GitHub Repo File Downloader

Allows you to download individual files directly from GitHub repository pages.

2024-12-09 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         GitHub Repo File Downloader
// @description  Allows you to download individual files directly from GitHub repository pages.
// @icon         https://github.githubassets.com/favicons/favicon-dark.svg
// @version      1.0
// @author       afkarxyz
// @namespace    https://github.com/afkarxyz/misc-scripts/
// @supportURL   https://github.com/afkarxyz/misc-scripts/issues
// @license      MIT
// @match        https://github.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function isIconModified(svg) {
        return svg.dataset.customIcon === 'true';
    }

    function modifySvgContent(svg, newContent, downloadUrl, fileName) {
        if (svg && !isIconModified(svg)) {
            const wrapper = document.createElement('div');
            wrapper.style.cursor = 'pointer';
            wrapper.style.display = 'inline-block';
            wrapper.style.transition = 'transform 0.1s ease-in-out';
            
            const newSvg = svg.cloneNode(true);
            newSvg.innerHTML = newContent;
            newSvg.dataset.customIcon = 'true';
            
            wrapper.appendChild(newSvg);
            wrapper.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                downloadFile(downloadUrl, fileName);
            });
            
            wrapper.addEventListener('mouseenter', () => {
                wrapper.style.transform = 'scale(1.1)';
            });
            wrapper.addEventListener('mouseleave', () => {
                wrapper.style.transform = 'scale(1)';
            });
            
            svg.parentNode.replaceChild(wrapper, svg);
        }
    }

    async function downloadFile(url, fileName) {
        try {
            const response = await fetch(url);
            const blob = await response.blob();
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);
            link.download = fileName;
            link.style.display = 'none';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        } catch (error) {
            console.error('Download failed:', error);
        }
    }

    function updateIcons() {
        const fileRows = document.querySelectorAll('.react-directory-row');
        let modifiedFileCount = 0;

        fileRows.forEach(row => {
            const svgs = row.querySelectorAll('.react-directory-filename-column svg:not([data-custom-icon="true"])');

            svgs.forEach(svg => {
                if (svg.classList.contains('color-fg-muted')) {
                    const fileLink = row.querySelector('a[href]');
                    if (!fileLink) return;
                    
                    const downloadUrl = fileLink.href
                        .replace('github.com', 'raw.githubusercontent.com')
                        .replace('/blob/', '/');
                    
                    const fileName = fileLink.textContent.trim();

                    const newFileContent = `
                        <path d="M14.5,3.4l-2.9-2.9C11.2,0.2,10.8,0,10.3,0H3.8C2.8,0,2,0.8,2,1.8v12.5c0,1,0.8,1.8,1.8,1.8h9.5c1,0,1.8-0.8,1.8-1.8V4.7
                            C15,4.2,14.8,3.8,14.5,3.4z M10.5,1.6L10.5,1.6l2.9,2.9l0,0h-2.7c-0.1,0-0.2-0.1-0.2-0.2V1.6z M13.5,14.2c0,0.1-0.1,0.2-0.2,0.2
                            H3.8c-0.1,0-0.2-0.1-0.2-0.2V1.8c0-0.1,0.1-0.2,0.2-0.2H9v2.8C9,5.2,9.8,6,10.8,6h2.8V14.2z"/>
                        <path d="M9.1,10.6V7.3c0-0.3-0.3-0.6-0.6-0.6S7.9,7,7.9,7.3v3.3L6.5,9.3C6.3,9,5.9,9,5.7,9.3c-0.2,0.2-0.2,0.6,0,0.8l2.4,2.4
                            c0.2,0.2,0.6,0.2,0.8,0h0l2.4-2.4c0.2-0.2,0.2-0.6,0-0.8c-0.2-0.2-0.6-0.2-0.8,0L9.1,10.6z"/>
                    `;
                    modifySvgContent(svg, newFileContent, downloadUrl, fileName);
                    modifiedFileCount++;
                }
            });
        });

        if (modifiedFileCount > 0) {
            console.log(`Modified ${modifiedFileCount} file icons with direct download functionality and hover effects.`);
        }
    }

    function runIconUpdate() {
        function debouncedUpdate() {
            updateIcons();
        }

        debouncedUpdate();

        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    debouncedUpdate();
                }
            });
        });

        observer.observe(document.body, { childList: true, subtree: true });

        console.log('GitHub Repo File Downloader: Script is now running.');
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', runIconUpdate);
    } else {
        runIconUpdate();
    }
})();