Github Enhancement - High Speed Download (Kentxxq Dynamic Mirror V3 - Fixed)

Dynamically fetches mirror URLs from mirror.kentxxq.com for high-speed downloads on Github.

// ==UserScript==
// @name         Github Enhancement - High Speed Download (Kentxxq Dynamic Mirror V3 - Fixed)
// @name:zh-CN   Github 增强 - 高速下载 (Kentxxq 动态镜像版 V3 - 已修复)
// @version      3.0.2
// @author       X.I.U (Modified for dynamic fetching)
// @description  Dynamically fetches mirror URLs from mirror.kentxxq.com for high-speed downloads on Github.
// @description:zh-CN  从 mirror.kentxxq.com 动态获取镜像地址,实现 Github 文件高速下载、Clone 加速等功能。
// @match        *://github.com/*
// @match        *://hub.whtrys.space/*
// @match        *://dgithub.xyz/*
// @match        *://kkgithub.com/*
// @match        *://github.site/*
// @match        *://github.store/*
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAACEUExURUxpcRgWFhsYGBgWFhcWFh8WFhoYGBgWFiUlJRcVFRkWFhgVFRgWFhgVFRsWFhgWFigeHhkWFv////////////r6+h4eHv///xcVFfLx8SMhIUNCQpSTk/r6+jY0NCknJ97e3ru7u+fn51BOTsPCwqGgoISDg6empmpoaK2srNDQ0FhXV3eXcCcAAAAXdFJOUwCBIZXMGP70BuRH2Ze/LpIMUunHkpQR34sfygAAAVpJREFUOMt1U+magjAMDAVb5BDU3W25b9T1/d9vaYpQKDs/rF9nSNJkArDA9ezQZ8wPbc8FE6eAiQUsOO1o19JolFibKCdHGHC0IJezOMD5snx/yE+KOYYr42fPSufSZyazqDoseTPw4lGJNOu6LBXVUPBG3lqYAOv/5ZwnNUfUifzBt8gkgfgINmjxOpgqUA147QWNaocLniqq3QsSVbQHNp45N/BAwoYQz9oUJEiE4GMGfoBSMj5gjeWRIMMqleD/CAzUHFqTLyjOA5zjNnwa4UCEZ2YK3khEcBXHjVBtEFeIZ6+NxYbPqWp1DLKV42t6Ujn2ydyiPi9nX0TTNAkVVZ/gozsl6FbrktkwaVvL2TRK0C8Ca7Hck7f5OBT6FFbLATkL2ugV0tm0RLM9fedDvhWstl8Wp9AFDjFX7yOY/lJrv8AkYuz7fuP8dv9izCYH+x3/LBnj9fYPBTpJDNzX+7cAAAAASUVORKCYII=
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuMenuCommand
// @grant        GM_openInTab
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_notification
// @grant        GM_setClipboard
// @grant        window.onurlchange
// @connect      mirror.kentxxq.com
// @sandbox      JavaScript
// @license      GPL-3.0 License
// @run-at       document-end
// @namespace    https://github.com/XIU2/UserScript
// @supportURL   https://github.com/XIU2/UserScript/issues
// @homepageURL  https://github.com/XIU2/UserScript
// ==/UserScript==

(function() {
    'use strict';

    // 当动态抓取失败时,使用的备用静态地址列表
    const fallbackMirrors = [
        ['https://cors.isteed.cc/https://github.com', 'cors.isteed.cc', '备用镜像'],
        ['https://gh.aptv.app/https://github.com', 'gh.aptv.app', '备用镜像'],
        ['https://ghfast.top/https://github.com', 'ghfast.top', '备用镜像'],
        ['https://ghproxy.cn/https://github.com', 'ghproxy.cn', '备用镜像'],
        ['https://gh-proxy.com/https://github.com', 'gh-proxy.com', '备用镜像'],
        ['https://ghproxy.net/https://github.com', 'ghproxy.net', '备用镜像'],
        ['https://wget.la/https://github.com', 'wget.la', '备用镜像']
    ];

    /**
     * 初始化脚本的主函数
     * @param {Array} mirrors - 从镜像站抓取或备用的镜像地址列表
     */
    function initializeScript(mirrors) {
        console.log("执行 initializeScript,使用的镜像列表:", mirrors);
        if (!mirrors || mirrors.length === 0) {
            console.error("初始化失败:镜像列表为空!");
            return;
        }

        const download_url_us = mirrors;

        // --- 以下是原脚本的完整核心逻辑 ---
        var menu_rawFast = GM_getValue('xiu2_menu_raw_fast'), menu_rawFast_ID, menu_rawDownLink_ID, menu_gitClone_ID, menu_customUrl_ID, menu_feedBack_ID;
        const download_url = [];
        const clone_url = [ ['https://gitclone.com', '国内', '[中国 国内]...'], ['https://kkgithub.com', '香港', '[中国香港、日本、新加坡等]...'], ];
        const clone_ssh_url = [ ['ssh://[email protected]:443/', 'Github 原生', '[日本、新加坡等]...'], ];
        const raw_url = [ ['https://raw.githubusercontent.com', 'Github 原生', '[日本 东京]...'], ['https://raw.kkgithub.com', '香港 1', '[中国香港、日本、新加坡等]...'], ['https://wget.la/https://raw.githubusercontent.com', '香港 2', '[中国香港、中国台湾、日本、美国等]...'], ['https://ghfast.top/https://raw.githubusercontent.com', '韩国', '[日本、韩国、新加坡、美国、德国等]...'], ['https://ghproxy.net/https://raw.githubusercontent.com', '日本 1', '[日本 大阪]...'], ];
        const svg = [ '<svg class="octicon octicon-cloud-download" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path d="M9 12h2l-3 3-3-3h2V7h2v5zm3-8c0-.44-.91-3-4.5-3C5.08 1 3 2.92 3 5 1.02 5 0 6.52 0 8c0 1.53 1 3 3 3h3V9.7H3C1.38 9.7 1.3 8.28 1.3 8c0-.17.05-1.7 1.7-1.7h1.3V5c0-1.39 1.56-2.7 3.2-2.7 2.55 0 3.13 1.55 3.2 1.8v1.2H12c.81 0 2.7.22 2.7 2.2 0 2.09-2.25 2.2-2.7 2.2h-2V11h2c2.08 0 4-1.16 4-3.5C16 5.06 14.08 4 12 4z"></path></svg>' ];
        const style = ['padding:0 6px; margin-right: -1px; border-radius: 2px; background-color: var(--XIU2-background-color); border-color: var(--borderColor-default); font-size: 11px; color: var(--XIU2-font-color);'];

        if (menu_rawFast == null){menu_rawFast = 1; GM_setValue('xiu2_menu_raw_fast', 1)};
        if (GM_getValue('menu_rawDownLink') == null){GM_setValue('menu_rawDownLink', true)};
        if (GM_getValue('menu_gitClone') == null){GM_setValue('menu_gitClone', true)};
        if (GM_getValue('custom_raw_url')) {raw_url.splice(1, 0, [GM_getValue('custom_raw_url'), '自定义', '[由你自定义的 Raw 加速源]...'])};
        if (GM_getValue('custom_clone_url')) {clone_url.unshift([GM_getValue('custom_clone_url'), '自定义', '[由你自定义的 Git Clone 加速源]...'])};
        registerMenuCommand();
        function registerMenuCommand() { if (menu_feedBack_ID) {GM_unregisterMenuCommand(menu_rawFast_ID); GM_unregisterMenuCommand(menu_rawDownLink_ID); GM_unregisterMenuCommand(menu_gitClone_ID); GM_unregisterMenuCommand(menu_customUrl_ID); GM_unregisterMenuCommand(menu_feedBack_ID); menu_rawFast = GM_getValue('xiu2_menu_raw_fast');} if (menu_rawFast > raw_url.length - 1) menu_rawFast = 0; if (GM_getValue('menu_rawDownLink')) menu_rawFast_ID = GM_registerMenuCommand(`${['0️⃣','1️⃣','2️⃣','3️⃣','4️⃣','5️⃣','6️⃣','7️⃣','8️⃣','9️⃣','🔟'][menu_rawFast]} [ ${raw_url[menu_rawFast][1]} ] 加速源 (☁) - 点击切换`, menu_toggle_raw_fast); menu_rawDownLink_ID = GM_registerMenuCommand(`${GM_getValue('menu_rawDownLink')?'✅':'❌'} 项目列表单文件快捷下载 (☁)`, function(){if (GM_getValue('menu_rawDownLink') == true) {GM_setValue('menu_rawDownLink', false);} else {GM_setValue('menu_rawDownLink', true);} location.reload();}); menu_gitClone_ID = GM_registerMenuCommand(`${GM_getValue('menu_gitClone')?'✅':'❌'} 添加 git clone 命令`, function(){if (GM_getValue('menu_gitClone') == true) {GM_setValue('menu_gitClone', false);} else {GM_setValue('menu_gitClone', true);} location.reload();}); menu_customUrl_ID = GM_registerMenuCommand(`#️⃣ 自定义加速源`, function () { const customKeys = [ { key: 'custom_raw_url', desc: 'Raw 加速源', placeholder: 'https://e.g./https://raw.githubusercontent.com' }, { key: 'custom_clone_url', desc: 'Git Clone 加速源', placeholder: 'https://e.g./https://github.com' }, { key: 'custom_download_url', desc: 'Release/Code(ZIP) 加速源', placeholder: 'https://e.g./https://github.com' } ]; function promptCustomUrl(index = 0) { if (index >= customKeys.length) {location.reload(); return;} const { key, desc, placeholder } = customKeys[index]; let current = GM_getValue(key, ''); let input = prompt(`请输入自定义${desc}地址:\n- 当前:${current || '(未设置)'}\n- 示例:${placeholder}\n- 留空为不设置`,current); if (input !== null) {GM_setValue(key, input.trim()); promptCustomUrl(index + 1);} } promptCustomUrl(); }); menu_feedBack_ID = GM_registerMenuCommand('💬 反馈 & 建议', function () {window.GM_openInTab('https://github.com/XIU2/UserScript/issues');}); }
        function menu_toggle_raw_fast() { if (menu_rawFast >= raw_url.length - 1) {menu_rawFast = 0;} else {menu_rawFast += 1;} GM_setValue('xiu2_menu_raw_fast', menu_rawFast); delRawDownLink(); addRawDownLink(); GM_notification({text: "已切换加速源为:" + raw_url[menu_rawFast][1], timeout: 3000}); registerMenuCommand(); };
        colorMode();
        setTimeout(addRawFile, 1000);
        setTimeout(addRawDownLink, 2000);
        if (window.onurlchange === undefined) addUrlChangeEvent();
        window.addEventListener('urlchange', function() { colorMode(); if (location.pathname.indexOf('/releases')) addRelease(); setTimeout(addRawFile, 1000); setTimeout(addRawDownLink, 2000); setTimeout(addRawDownLink_, 1000); });
        const callback = (mutationsList, observer) => { if (location.pathname.indexOf('/releases') > -1) { for (const mutation of mutationsList) { for (const target of mutation.addedNodes) { if (target.nodeType !== 1) return; if (target.tagName === 'DIV' && target.dataset.viewComponent === 'true' && target.classList[0] === 'Box') addRelease(); } } } else if (document.querySelector('#repository-container-header:not([hidden])')) { for (const mutation of mutationsList) { for (const target of mutation.addedNodes) { if (target.nodeType !== 1) return; if (target.tagName === 'DIV' && target.parentElement && target.parentElement.id === '__primerPortalRoot__') { addDownloadZIP(target); addGitClone(target); addGitCloneSSH(target); } else if (target.tagName === 'DIV' && target.className.indexOf('Box-sc-') != -1) { if (target.querySelector('input[value^="https:"]')) { addGitCloneClear('.XIU2-GCS'); addGitClone(target); } else if (target.querySelector('input[value^="git@"]')) { addGitCloneClear('.XIU2-GC'); addGitCloneSSH(target); } else if (target.querySelector('input[value^="gh "]')) { addGitCloneClear('.XIU2-GC, .XIU2-GCS'); } } } } } };
        const observer = new MutationObserver(callback);
        observer.observe(document, { childList: true, subtree: true });
        function get_New_download_url() { let shuffled = download_url_us.slice(0), i = download_url_us.length, temp, index; while (i--) { index = Math.floor((i + 1) * Math.random()); temp = shuffled[index]; shuffled[index] = shuffled[i]; shuffled[i] = temp; } if (GM_getValue('custom_download_url')) {return [[GM_getValue('custom_download_url'), '自定义', '...']].concat(shuffled);} return shuffled; }
        function addRelease() { let html = document.querySelectorAll('.Box-footer'); if (html.length == 0 || location.pathname.indexOf('/releases') == -1) return; let divDisplay = 'margin-left: -90px;', new_download_url = get_New_download_url(); if (document.documentElement.clientWidth > 755) {divDisplay = 'margin-top: -3px;margin-left: 8px;display: inherit;';}; html[0].appendChild(document.createElement('style')).textContent = '@media (min-width: 768px) {.Box-footer li.Box-row>div>span.color-fg-muted {min-width: 27px !important;}}'; for (const current of html) { if (current.querySelector('.XIU2-RS')) continue; current.querySelectorAll('li.Box-row a').forEach(function (_this) { let href = _this.href.split(location.host), url = '', _html = `<div class="XIU2-RS" style="${divDisplay}">`; for (let i=0;i<new_download_url.length;i++) { url = new_download_url[i][0] + href[1]; _html += `<a style="${style[0]}" class="btn" href="${url}" target="_blank" title="${new_download_url[i][2]}" rel="noreferrer noopener nofollow">${new_download_url[i][1]}</a>` } _this.parentElement.nextElementSibling.insertAdjacentHTML('beforeend', _html + '</div>'); }); } }
        function addDownloadZIP(target) { let html = target.querySelector('ul[class^=prc-ActionList-ActionList-]>li:last-child');if (!html) return; let href_script = document.querySelector('react-partial[partial-name=repos-overview]>script[data-target="react-partial.embeddedData"]'), href_slice = href_script.textContent.slice(href_script.textContent.indexOf('"zipballUrl":"')+14), href = href_slice.slice(0, href_slice.indexOf('"')), url = '', _html = '', new_download_url = get_New_download_url(); let html_clone = html.cloneNode(true), html_clone_a = html_clone.querySelector('a[href$=".zip"]'), html_clone_span = html_clone.querySelector('span[id]'); for (let i=0;i<new_download_url.length;i++) { url = new_download_url[i][0] + href; html_clone_a.href = url; html_clone_a.setAttribute('title', new_download_url[i][2].replaceAll('&#10;','\n')); html_clone_a.setAttribute('target', '_blank'); html_clone_a.setAttribute('rel', 'noreferrer noopener nofollow'); html_clone_span.textContent = 'Download ZIP ' + new_download_url[i][1]; _html += html_clone.outerHTML; } html.insertAdjacentHTML('afterend', _html); }
        function addGitCloneClear(css) { document.querySelectorAll(css).forEach((e)=>{e.remove()}); }
        function addGitClone(target) { let html = target.querySelector('input[value^="https:"]:not([title])');if (!html) return; let href_split = html.value.split(location.host)[1], html_parent = '<div style="margin-top: 4px;" class="XIU2-GC ' + html.parentElement.className + '">', url = '', _html = '', _gitClone = ''; if (html.nextElementSibling) html.nextElementSibling.hidden = true; if (html.parentElement.nextElementSibling.tagName === 'SPAN'){ html.parentElement.nextElementSibling.textContent += ' (↑点击上面文字可复制)'; } if (GM_getValue('menu_gitClone')) {_gitClone='git clone '; html.value = _gitClone + html.value; html.setAttribute('value', html.value);} let html_clone = html.cloneNode(true); for (let i=0;i<clone_url.length;i++) { url = (clone_url[i][0] === 'https://gitclone.com' ? clone_url[i][0] + '/github.com' + href_split : clone_url[i][0] + href_split); html_clone.title = `${url}\n\n${clone_url[i][2].replaceAll('&#10;','\n')}`; html_clone.setAttribute('value', _gitClone + url); _html += html_parent + html_clone.outerHTML + '</div>'; } html.parentElement.insertAdjacentHTML('afterend', _html); if (html.parentElement.parentElement.className.indexOf('XIU2-GCP') === -1){ html.parentElement.parentElement.classList.add('XIU2-GCP'); html.parentElement.parentElement.addEventListener('click', (e)=>{if (e.target.tagName === 'INPUT') {GM_setClipboard(e.target.value);}}); } }
        function addGitCloneSSH(target) { let html = target.querySelector('input[value^="git@"]:not([title])');if (!html) return; let href_split = html.value.split(':')[1], html_parent = '<div style="margin-top: 4px;" class="XIU2-GCS ' + html.parentElement.className + '">', url = '', _html = '', _gitClone = ''; html.nextElementSibling.hidden = true; if (html.parentElement.nextElementSibling.tagName === 'SPAN'){ html.parentElement.nextElementSibling.textContent += ' (↑点击文字可复制)'; } if (GM_getValue('menu_gitClone')) {_gitClone='git clone '; html.value = _gitClone + html.value; html.setAttribute('value', html.value);} let html_clone = html.cloneNode(true); for (let i=0;i<clone_ssh_url.length;i++) { url = clone_ssh_url[i][0] + href_split; html_clone.title = `${url}\n\n${clone_ssh_url[i][2].replaceAll('&#10;','\n')}`; html_clone.setAttribute('value', _gitClone + url); _html += html_parent + html_clone.outerHTML + '</div>'; } html.parentElement.insertAdjacentHTML('afterend', _html); if (html.parentElement.parentElement.className.indexOf('XIU2-GCP') === -1){ html.parentElement.parentElement.classList.add('XIU2-GCP'); html.parentElement.parentElement.addEventListener('click', (e)=>{if (e.target.tagName === 'INPUT') {GM_setClipboard(e.target.value);}}); } }
        function addRawFile() { let html = document.querySelector('a[data-testid="raw-button"]');if (!html) return; let href = location.href.replace(`https://${location.host}`,''), href2 = href.replace('/blob/','/'), url = '', _html = ''; for (let i=1;i<raw_url.length;i++) { url = ((raw_url[i][0].indexOf('/gh') + 3 === raw_url[i][0].length) && raw_url[i][0].indexOf('cdn.staticaly.com') === -1) ? raw_url[i][0] + href.replace('/blob/','@') : raw_url[i][0] + href2; _html += `<a href="${url}" title="${raw_url[i][2]}" target="_blank" role="button" rel="noreferrer noopener nofollow" data-size="small" data-variant="default" class="${html.className} XIU2-RF" style="border-radius: 0;margin-left: -1px;">${raw_url[i][1].replace(/ \d/,'')}</a>`; } if (document.querySelector('.XIU2-RF')) document.querySelectorAll('.XIU2-RF').forEach((e)=>{e.remove()}); html.insertAdjacentHTML('afterend', _html); }
        function addRawDownLink() { if (!GM_getValue('menu_rawDownLink')) return; let files = document.querySelectorAll('div.Box-row svg.octicon.octicon-file, .react-directory-filename-column>svg.color-fg-muted');if(files.length === 0 || location.pathname.indexOf('/tags') > -1 || document.querySelectorAll('a.fileDownLink').length > 0) return; var mouseOverHandler = function(evt) { let elem = evt.currentTarget; elem.querySelectorAll('.fileDownLink').forEach(el=>{el.style.display = 'inline'}); elem.querySelectorAll('svg.octicon.octicon-file, svg.color-fg-muted').forEach(el=>{el.style.display = 'none'}); }; var mouseOutHandler = function(evt) { let elem = evt.currentTarget; elem.querySelectorAll('.fileDownLink').forEach(el=>{el.style.display = 'none'}); elem.querySelectorAll('svg.octicon.octicon-file, svg.color-fg-muted').forEach(el=>{el.style.display = 'inline'}); }; files.forEach(function(fileElm) { let trElm = fileElm.parentNode.parentNode, cntElm_a = trElm.querySelector('[role="rowheader"] > .css-truncate.css-truncate-target.d-block.width-fit > a, .react-directory-truncate>a'), Name = cntElm_a.innerText, href = cntElm_a.getAttribute('href'), href2 = href.replace('/blob/','/'), url = ''; url = ((raw_url[menu_rawFast][0].indexOf('/gh') + 3 === raw_url[menu_rawFast][0].length) && raw_url[menu_rawFast][0].indexOf('cdn.staticaly.com') === -1) ? raw_url[menu_rawFast][0] + href.replace('/blob/','@') : raw_url[menu_rawFast][0] + href2; fileElm.insertAdjacentHTML('afterend', `<a href="${url}" download="${Name}" target="_blank" rel="noreferrer noopener nofollow" class="fileDownLink" style="display: none;" title="「${raw_url[menu_rawFast][1]}」&#10;...">${svg[0]}</a>`); trElm.onmouseover = mouseOverHandler; trElm.onmouseout = mouseOutHandler; }); }
        function delRawDownLink() { if (!GM_getValue('menu_rawDownLink')) return; document.querySelectorAll('.fileDownLink').forEach(function(fileElm) {fileElm.remove();}); }
        function addRawDownLink_() { if (!GM_getValue('menu_rawDownLink')) return; let files = document.querySelectorAll('div.Box-row svg.octicon.octicon-file, .react-directory-filename-column>svg.color-fg-muted');if(files.length === 0 || document.querySelectorAll('a.fileDownLink').length === 0) return; var mouseOverHandler = function(evt) { let elem = evt.currentTarget; elem.querySelectorAll('.fileDownLink').forEach(el=>{el.style.display = 'inline'}); elem.querySelectorAll('svg.octicon.octicon-file, svg.color-fg-muted').forEach(el=>{el.style.display = 'none'}); }; var mouseOutHandler = function(evt) { let elem = evt.currentTarget; elem.querySelectorAll('.fileDownLink').forEach(el=>{el.style.display = 'none'}); elem.querySelectorAll('svg.octicon.octicon-file, svg.color-fg-muted').forEach(el=>{el.style.display = 'inline'}); }; files.forEach(function(fileElm) { let trElm = fileElm.parentNode.parentNode; trElm.onmouseover = mouseOverHandler; trElm.onmouseout = mouseOutHandler; }); }
        function colorMode() { let style_Add = document.getElementById('XIU2-Github') || document.createElement('style'); style_Add.id = 'XIU2-Github'; style_Add.type = 'text/css'; let backColor = '#ffffff', fontColor = '#888888'; if (document.lastElementChild.dataset.colorMode === 'dark') { backColor = document.lastElementChild.dataset.darkTheme === 'dark_dimmed' ? '#272e37' : '#161a21'; fontColor = document.lastElementChild.dataset.darkTheme === 'dark_dimmed' ? '#768390' : '#97a0aa'; } else if (document.lastElementChild.dataset.colorMode === 'auto') { if (window.matchMedia('(prefers-color-scheme: dark)').matches || document.lastElementChild.dataset.lightTheme.indexOf('dark') > -1) { if (document.lastElementChild.dataset.darkTheme === 'dark_dimmed') { backColor = '#272e37'; fontColor = '#768390'; } else if (document.lastElementChild.dataset.darkTheme.indexOf('light') == -1) { backColor = '#161a21'; fontColor = '#97a0aa'; } } } document.lastElementChild.appendChild(style_Add).textContent = `.XIU2-RS a {--XIU2-background-color: ${backColor}; --XIU2-font-color: ${fontColor};}`; }
        function addUrlChangeEvent() { history.pushState = ( f => function pushState(){ var ret = f.apply(this, arguments); window.dispatchEvent(new Event('pushstate')); window.dispatchEvent(new Event('urlchange')); return ret; })(history.pushState); history.replaceState = ( f => function replaceState(){ var ret = f.apply(this, arguments); window.dispatchEvent(new Event('replacestate')); window.dispatchEvent(new Event('urlchange')); return ret; })(history.replaceState); window.addEventListener('popstate',()=>{ window.dispatchEvent(new Event('urlchange')); }); }
    }

    /**
     * 抓取、解析镜像地址并启动脚本
     */
    function fetchMirrorsAndRun() {
        console.log("Github 加速脚本: 开始从 mirror.kentxxq.com 动态抓取镜像...");
        GM_xmlhttpRequest({
            method: "GET",
            url: "https://mirror.kentxxq.com/github",
            onload: function(response) {
                try {
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, "text/html");

                    // !!! 关键选择器 !!!
                    // 这个选择器是根据 mirror.kentxxq.com 当前的页面结构编写的。
                    // 它会选择页面主要内容区域(<main>标签)里的所有<h2>标题。
                    const selector = 'main h2'; // <--- 已更新为正确的选择器
                    const mirrorElements = doc.querySelectorAll(selector);
                    console.log(`Github 加速脚本: 使用选择器 "${selector}" 找到了 ${mirrorElements.length} 个元素。`);


                    if (mirrorElements.length === 0) {
                        console.error("Github 加速脚本: 未能从镜像站找到任何地址 (选择器可能已失效),将使用内置的备用地址。");
                        GM_notification({ text: 'Github 加速脚本:\n动态抓取镜像失败, 已使用备用地址', timeout: 5000 });
                        initializeScript(fallbackMirrors);
                        return;
                    }

                    // 提取域名文本,并只取前7个
                    const extractedDomains = Array.from(mirrorElements)
                        .map(el => el.textContent.trim())
                        .slice(0, 7);

                    // 格式化为脚本需要的数组结构
                    const formattedMirrors = extractedDomains.map(domain => ([
                        `https://${domain}/https://github.com`,
                        domain,
                        `镜像由 kentxxq.com 提供`
                    ]));

                    console.log("Github 加速脚本: 成功抓取并加载了最新的镜像地址。");
                    GM_notification({ text: 'Github 加速脚本:\n成功加载最新镜像地址', timeout: 3500 });
                    initializeScript(formattedMirrors);

                } catch (e) {
                    console.error("Github 加速脚本: 解析镜像页面时出错,将使用内置的备用地址。", e);
                    GM_notification({ text: 'Github 加速脚本:\n解析镜像页面出错, 已使用备用地址', timeout: 5000 });
                    initializeScript(fallbackMirrors);
                }
            },
            onerror: function(error) {
                console.error("Github 加速脚本: 无法访问镜像站点,将使用内置的备用地址。", error);
                GM_notification({ text: 'Github 加速脚本:\n无法访问镜像站点, 已使用备用地址', timeout: 5000 });
                initializeScript(fallbackMirrors);
            },
            ontimeout: function() {
                console.error("Github 加速脚本: 访问镜像站点超时,将使用内置的备用地址。");
                GM_notification({ text: 'Github 加速脚本:\n访问镜像站点超时, 已使用备用地址', timeout: 5000 });
                initializeScript(fallbackMirrors);
            }
        });
    }

    // 启动脚本
    fetchMirrorsAndRun();

})();