Greasy Fork

Greasy Fork is available in English.

爱给网音效下载

免登录下载音效,优化性能和日志,兼容stay

// ==UserScript==
// @name         爱给网音效下载
// @namespace    原作者blue
// @version      2025.09.25.04
// @description  免登录下载音效,优化性能和日志,兼容stay
// @author       karteous
// @match        https://www.aigei.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // 等待依赖加载
    function waitForDependencies(callback, maxAttempts = 20, interval = 500) {
        let attempts = 0;
        const check = setInterval(() => {
            if (window.$ && window.AudioPlayerManager && window.AudioPlayerStatus) {
                clearInterval(check);
                console.log('依赖加载成功:jQuery, AudioPlayerManager, AudioPlayerStatus');
                callback();
            } else if (attempts >= maxAttempts) {
                clearInterval(check);
                console.error('依赖未加载:', {
                    jQuery: !!window.$,
                    AudioPlayerManager: !!window.AudioPlayerManager,
                    AudioPlayerStatus: !!window.AudioPlayerStatus,
                    fileGet: !!window.fileGet
                });
                alert('脚本无法运行,网站可能已更新,请检查开发者工具控制台。');
            }
            attempts++;
        }, interval);
    }

    // 下载文件函数
    const downloadFile = (url, fileName) => {
        if (!url || !url.startsWith('http')) {
            console.error('无效的下载 URL:', url);
            alert('无法获取有效下载链接,请先播放音频或检查控制台。');
            return;
        }
        console.log('尝试下载:', { url, fileName });
        fetch(url, {
            method: 'GET',
            headers: {
                'Referer': window.location.href,
                'User-Agent': navigator.userAgent,
                'Accept': 'audio/mpeg, */*'
            }
        })
            .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP 错误: ${response.status} ${response.statusText}`);
                }
                console.log('Fetch 成功:', response);
                return response.blob();
            })
            .then(blob => {
                const link = document.createElement('a');
                const blobUrl = URL.createObjectURL(blob);
                link.href = blobUrl;
                link.download = fileName || 'audio.mp3';
                link.click();
                link.remove();
                URL.revokeObjectURL(blobUrl);
                console.log('下载触发:', fileName);
            })
            .catch(error => {
                console.error('Fetch 下载失败:', error);
                console.log('尝试使用 <audio> 元素下载...');
                const audio = document.createElement('audio');
                audio.src = url;
                const link = document.createElement('a');
                link.href = url;
                link.download = fileName || 'audio.mp3';
                link.click();
                link.remove();
                console.log('Audio 元素下载触发:', fileName);
            });
    };

    // 处理单个音频项
    const processItem = (item) => {
        if (!item || !item.containerEl || !item.id) {
            console.warn('无效的音频项:', item);
            return;
        }
        const $container = window.$(item.containerEl).find('.audio-download-box');
        if ($container.find(`#freedown_${item.id}`).length > 0) {
            // console.debug('按钮已存在,跳过:', item.id); // 改为 debug 或注释掉以减少日志
            return;
        }

        console.log('处理音频项:', item.id, item.itemName);
        window.$(`<a id="freedown_${item.id}" class="audio-down-btn btn btn-default" style="color: #3ba36f;" title="点击免费下载"><i class="gei-icon-font-download"></i>免费下载</a>`)
            .click(() => {
                const itemName = item?.itemName?.replace(/<[^>]+>/g, "") || 'unknown';
                const url = item?.audioSound?.sound?.url;
                console.log('点击下载按钮:', item.id, 'URL:', url);

                if (!url) {
                    if (!window.fileGet) {
                        console.error('fileGet 函数不可用');
                        alert('未获取到链接,请先播放一次音频。');
                        return;
                    }
                    console.log('URL 不可用,尝试触发播放获取 URL');
                    const $elem = window.$('#itemInfoToken_audio_mp3_' + item.id);
                    if (!$elem.length) {
                        console.error('未找到音频元素:', '#itemInfoToken_audio_mp3_' + item.id);
                        alert('未找到音频元素,请检查页面结构。');
                        return;
                    }
                    const ocbk = $elem.attr('cbk');
                    $elem.attr('cbk', 'callBackAudioFile_Custom');
                    window.fileGet.call(window.AudioPlayerManager, $elem.get(0), 'play', null, null, null, null, item.containerEl);
                    $elem.attr('cbk', ocbk);
                    return;
                }
                downloadFile(url, `${itemName}.mp3`);
            })
            .appendTo($container);
    };

    // 自定义回调函数
    window.callBackAudioFile_Custom = (id, url, unk, obj) => {
        console.log('callBackAudioFile_Custom 调用:', { id, url, obj });
        const item = window.AudioPlayerManager.getByEl(obj.customData);
        if (!item) {
            console.error('无法获取音频项:', id);
            alert('无法获取音频项,请检查控制台。');
            return;
        }
        const itemName = item?.itemName?.replace(/<[^>]+>/g, "") || 'unknown';
        item.audioSound.init(item.containerEl, url);
        item.urlRequestStatus = window.AudioPlayerStatus.UrlRequestStatus.succ;
        downloadFile(url, `${itemName}.mp3`);
    };

    // 去抖函数
    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    // 主逻辑
    waitForDependencies(() => {
        console.log('开始初始化脚本');
        const NATIVE_create = window.AudioPlayerManager.create.bind(window.AudioPlayerManager);
        const NATIVE_getByEl = window.AudioPlayerManager.getByEl.bind(window.AudioPlayerManager);

        // 覆盖 create 方法
        window.AudioPlayerManager.create = (elem) => {
            NATIVE_create(elem);
            const item = NATIVE_getByEl(elem);
            processItem(item);
        };

        // 处理现有音频项
        window.AudioPlayerManager.each(item => {
            processItem(item);
        });

        // 监听动态加载的元素(添加去抖)
        const debouncedProcess = debounce(() => {
            console.log('检测到 DOM 变化,重新处理音频项');
            window.AudioPlayerManager.each(item => {
                processItem(item);
            });
        }, 500);

        const observer = new MutationObserver(debouncedProcess);
        observer.observe(document.body, { childList: true, subtree: true });
    });
})();