小雅爬爬爬

爬取课件url

当前为 2024-04-18 提交的版本,查看 最新版本

// ==UserScript==
// @name        小雅爬爬爬
// @match      *://ccnu.ai-augmented.com/*
// @grant       none
// @description 爬取课件url
// @license MIT
// @author     Yi
// @version    1.0.8
// @namespace  https://gf.qytechs.cn/users/1268039
// ==/UserScript==

'use strict';

let course_resources;

// 附件下载实现细节
function parseContent() {
    console.oldLog("::parseContent");

    var download_url = 'https://ccnu.ai-augmented.com/api/jx-oresource/cloud/file_url/';
    var download_list = document.getElementById("download_list");
    download_list.innerHTML = '<h3 style="color:#fcbb34">课程附件清单</h3>';
    for (let i in course_resources) {
        let file_name = course_resources[i].name;
        if (course_resources[i].mimetype) {
            fetch(download_url + course_resources[i].quote_id).then(function(response) {
                return response.json();
            }).then(function(data) {
                if (data.success) {
                    let file_url = data.data.url;

                    // 创建一个包含链接和勾选框的容器
                    var file_container = document.createElement('div');
                    file_container.style.display = 'flex';
                    file_container.style.alignItems = 'center';

                    // 创建勾选框
                    var checkbox = document.createElement('input');
                    checkbox.type = 'checkbox';
                    checkbox.style.marginRight = '10px';

                    // 创建链接元素
                    var file_info = document.createElement('a');
                    file_info.innerHTML = file_name;
                    file_info.href = file_url;
                    file_info.target = "_blank";
                    file_info.className = 'link-style'; // 应用初始样式
                    file_info.style.textDecoration = 'none'; // 去掉下划线
                    file_info.addEventListener('mouseover', () => {
                        file_info.style.textDecoration = 'underline';
                        file_info.style.color = '#000';
                    });
                    file_info.addEventListener('mouseout', function() {
                        file_info.style.textDecoration = 'none';
                        file_info.style.color = '';
                    });

                    // 将勾选框和链接添加到容器中
                    file_container.appendChild(checkbox);
                    file_container.appendChild(file_info);
                    file_container.style.borderBottom = '1px solid #eee'; // 每个项目间画一条分隔线
                    file_container.style.padding = '5px 10px';
                    file_container.style.justifyContent = 'space-between';
                    file_container.style.alignItems = 'center';
                    file_container.style.display = 'flex';

                    console.oldLog('::parse', file_name, file_url);

                    // 将包含勾选框和链接的容器添加到下载列表
                    download_list.append(file_container);
                }
            }).catch(function(e) {
                console.oldLog('!!error', e);
            });
        }
    }
}

function courseDownload(file_url, file_name) {
    getBlob(file_url, function(blob) {
        saveAs(blob, file_name);
    })
}
function getBlob(file_url,cb) {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', file_url, true);
    xhr.responseType = 'blob';
    xhr.onload = function() {
        if (xhr.status === 200) {
            cb(xhr.response);
        }
    }
    xhr.send();
}
function saveAs(blob, file_name) {
    if (window.navigator.msSaveOrOpenBlob) {
        navigator.msSaveBlob(blob, file_name);
    } else {
        let link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = file_name;
        link.click();
        window.URL.revokeObjectURL(link.href);
    }
}

window.showList = function () {
    var download_list = document.getElementById("download_list");

    // 检查是否已经存在搜索框
    var existingSearchInput = document.getElementById("searchInput");
    if (!existingSearchInput) {
        // 如果不存在,则创建搜索框
        var searchInput = document.createElement("input");
        searchInput.type = "text";
        searchInput.placeholder = "搜索文件名";
        searchInput.id = "searchInput"; // 设置唯一的ID
        searchInput.style.padding = '5px';
        searchInput.style.marginRight = '10px';
        searchInput.style.border = '1px solid #ddd';
        searchInput.style.borderRadius = '4px';
        searchInput.addEventListener("input", function () {
            filterList(this.value);
        });
        download_list.prepend(searchInput);
    }

    // 检查是否已存在全选复选框
    var existingSelectAllCheckbox = document.getElementById("selectAllCheckbox");
    if (!existingSelectAllCheckbox) {
        // 创建全选复选框
        var selectAllCheckbox = document.createElement('input');
        selectAllCheckbox.type = 'checkbox';
        selectAllCheckbox.id = 'selectAllCheckbox';
        selectAllCheckbox.style.marginRight = '10px';

        var selectAllLabel = document.createElement('label');
        selectAllLabel.htmlFor = 'selectAllCheckbox';
        selectAllLabel.textContent = '全选';
        selectAllLabel.style.userSelect = 'none'; // 防止点击标签时选中文字

        var checkboxContainer = document.createElement('div');
        checkboxContainer.appendChild(selectAllCheckbox);
        checkboxContainer.appendChild(selectAllLabel);
        download_list.prepend(checkboxContainer);
        checkboxContainer.style.display = 'flex';
        checkboxContainer.style.alignItems = 'center';
        checkboxContainer.style.justifyContent = 'space-between';
        checkboxContainer.style.padding = '5px 10px';
        checkboxContainer.style.borderBottom = '2px solid #fcbb34'; // 用主题颜色画分隔线
        checkboxContainer.style.marginBottom = '10px';

        // 给全选复选框添加事件监听器,当改变时,全选/取消全选所有当前可见的文件
        selectAllCheckbox.addEventListener('change', function() {
            var checkboxes = document.querySelectorAll("#download_list input[type='checkbox']:not(#selectAllCheckbox)");
            checkboxes.forEach(function(checkbox) {
                // 只改变标记为可见的复选框的状态
                if (checkbox.getAttribute('data-visible') === 'true') {
                    checkbox.checked = selectAllCheckbox.checked;
                }
                checkbox.style.cursor = 'pointer';
            });
        });
    }

    if (download_list.style.display == "none") {
        download_list.style.display = "flex";
        download_list.style.overflowY = "auto"; // 添加垂直滚动条
        download_list.style.maxHeight = "300px"; // 设置最大高度,根据需要调整
    } else {
        download_list.style.display = "none";
    }

    // 检查是否已经创建了批量下载按钮
    var existingbulkDownloadButton = document.getElementById("bulkDownloadButton");
    if (!existingbulkDownloadButton) {
        // 检查是否已经创建了批量下载按钮
        var existingBulkDownloadButton = document.getElementById("bulkDownloadButton");
        if (!existingBulkDownloadButton) {
            // 添加批量下载按钮
            window.bulkDownloadButton = document.createElement('button');
            window.bulkDownloadButton.innerHTML = '批量下载';
            window.bulkDownloadButton.id = "bulkDownloadButton";
            window.bulkDownloadButton.title = '点击下载所选文件'; // 添加提示文本
            window.bulkDownloadButton.style = `
            position: fixed;
            top: 20px;
            right: 20px;
            background-color: #fcbb34;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
            transition: background-color 0.3s, transform 0.3s;
        `;

            window.bulkDownloadButton.addEventListener('mouseover', function() {
                this.style.transform = 'scale(1.05)';
                this.style.backgroundColor = '#ffd564';
            });

            window.bulkDownloadButton.addEventListener('mouseout', function() {
                this.style.transform = 'scale(1)';
                this.style.backgroundColor = '#fcbb34';
            });
        }
        window.bulkDownloadButton.addEventListener('click', function() {
            // 获取所有勾选框
            var checkboxes = document.querySelectorAll("#download_list input[type='checkbox']:checked");

            // 获取选中链接的文件名和链接
            var selected_files = [];
            checkboxes.forEach(function(checkbox) {
                var container = checkbox.parentElement;
                var link = container.querySelector('a');
                var file_name = link.innerHTML;
                var file_url = link.href;
                selected_files.push({ name: file_name, url: file_url });
            });

            // 执行批量下载
            selected_files.forEach(function(file) {
                courseDownload(file.url, file.name);
            });
        });

        // 将批量下载按钮添加到下载列表
        download_list.appendChild(window.bulkDownloadButton);
    }
}

function filterList(keyword) {
    keyword = keyword.toLowerCase();
    var files = document.querySelectorAll("#download_list a");
    files.forEach(function(file) {
        var container = file.parentElement; // 获取父容器
        var fileName = file.innerHTML.toLowerCase();
        var checkbox = file.previousSibling; // 假设复选框在链接之前
        if (fileName.includes(keyword)) {
            container.style.display = ""; // 容器可见
            checkbox.style.display = ""; // 勾选框可见
            checkbox.setAttribute('data-visible', 'true'); // 标记为可见
        } else {
            container.style.display = "none"; // 容器不可见
            checkbox.style.display = "none"; // 勾选框不可见
            checkbox.setAttribute('data-visible', 'false'); // 标记为不可见
        }
    });
}

function add_download_button() {
    // 全局变量用于保存批量下载按钮
    window.bulkDownloadButton = null;
    var down_button = document.createElement('div');
    down_button.innerHTML = `
<div id="download_list" style="z-index:999;backdrop-filter: blur(10px);border: 2px solid #fcbb34;border-radius: 5px;display: none;padding: 20px;flex-direction: column;align-items: flex-start;">
    <h3 style="color:#fcbb34">课程附件清单</h3>
</div>
<svg onclick="showList()" class="icon download-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="60px" height="60px" fill="#fcbb34" title="双击展开列表">
    <title>展开列表</title>
    <path d="M19,9h-4V3H9v6H5l7,7L19,9z M5,18v2h14v-2H5z"/>
</svg>
`;

    // 添加下载图标的样式
    var downloadIconStyle = document.createElement('style');
    downloadIconStyle.innerHTML = `
  .download-icon {
  padding: 2px;
  margin: -20px;
  background-color: rgba(255, 255, 255, 0);
  border-radius: 10px;
}
    transition: transform 0.3s ease;
    cursor: pointer;
  }

  .download-icon:hover {
  background-color: rgba(255, 255, 255, 0.3);
    transform: scale(1.1);
  }
`;
    document.head.appendChild(downloadIconStyle);

    down_button.style.cssText = `
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 9000;
  cursor: pointer;
`;

    // 确保已经有这样的代码来设置图标的悬停动画
    var existingStyles = document.getElementById('download-icon-styles');
    if (!existingStyles) {
        document.head.appendChild(downloadIconStyle);
    }
    document.body.appendChild(down_button);
}

window.onload = ()=> {
    console.oldLog = console.log;
    console.log = (...data) =>{
        if (data[0] == 'nodesToData')
        {
            course_resources = data[1];
            console.oldLog('::', course_resources);
            parseContent();
        }
    };

    // 创建一个 MutationObserver 实例
    const observer = new MutationObserver(function(mutationsList, observer) {
        // 在每次发生 DOM 变化时触发这个回调函数
        for(let mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.target.id === 'download_list') {
                // 如果发生了子节点的变化,并且变化的目标是下载列表
                // 重新添加搜索框和批量下载按钮
                window.showList();
                break; // 处理完毕后退出循环
            }
        }
    });

    // 配置需要观察的目标节点和观察的类型
    observer.observe(document.body, { childList: true, subtree: true });

    // 添加下载按钮并延迟显示
    setTimeout(() => {
        add_download_button();
    }, 2000);
};


// 定义要抓取的后缀名
var extensions = [".doc", ".pdf", ".docx", ".ppt", ".pptx", ".xls", ".xlsx"];

// 创建一个元素,用于显示抓取到的 URL
var list = document.createElement("div");
initializeListStyles(list);


// 监听 xhr 请求,检查响应的 URL 是否符合条件
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
    this.addEventListener("load", function() {
        // 如果 URL 包含指定的后缀名之一
        for (var i = 0; i < extensions.length; i++) {
            if (url.includes(extensions[i])) {
                // 发送一个新的 xhr 请求,获取真正的下载地址
                handleXhrResponse(url);
                break;
            }
        }
    });
    open.call(this, method, url, async, user, pass);
};

// 初始化列表样式
function initializeListStyles(element) {
    element.style.position = "fixed";
    element.style.top = "10px";
    element.style.right = "0";
    element.style.width = "350px";
    element.style.height = "10%";
    element.style.overflow = "auto";
    element.style.zIndex = "9999";
    element.style.padding = "10px";
    // 添加闪烁动画样式
    var style = document.createElement("style");
    style.textContent = `
        @keyframes blink {
            25% {
                opacity: 0;
            }
        }

        .blink-animation {
            animation: blink 1s 3 alternate; /* 持续时间1秒,总共3次,alternate表示交替进行 */
        }
    `;
    document.head.appendChild(style);
    // 为元素添加渐变背景色
    element.style.background = "linear-gradient(270deg, #ffc700, #ff8c00, #ff6500)";
    element.style.backgroundSize = "400% 400%";
    // 添加动态背景动画的 CSS 规则
    var animStyle = document.createElement("style");
    animStyle.textContent = `
@keyframes gradientBgAnimation {
    0% {
        background-position: 0% 50%;
    }
    50% {
        background-position: 100% 50%;
    }
    100% {
        background-position: 0% 50%;
    }
}

.dynamic-gradient {
    animation: gradientBgAnimation 15s ease infinite;
}
`;
    document.head.appendChild(animStyle);

    // 为元素添加动态渐变背景的类
    element.classList.add("dynamic-gradient");
    // 为元素添加阴影效果
    element.style.boxShadow = "0 4px 8px 0 rgba(0, 0, 0, 0.2)";
    // 为元素添加圆角效果
    element.style.borderRadius = "10px";
    // 为元素添加动画效果,悬停时放大
    element.style.transition = "transform 0.3s";
    element.addEventListener("mouseover", function() {
        element.style.transform = "scale(1.1)";
    });
    element.addEventListener("mouseout", function() {
        element.style.transform = "scale(1)";
    });
    element.innerHTML = "<h3><span style=\"font-family: '微软雅黑', 'Microsoft YaHei', sans-serif; font-weight: bold; font-style: italic; font-size: 16px;\">抓取到的课件</span></h3>";
    // 添加 draggable 属性,可拖动
    element.setAttribute("draggable", "true");
    // 添加 resize 属性,可调整大小
    element.style.resize = "both";
    // 添加拖动事件监听器
    element.addEventListener("dragstart", function(e) {
        // 设置拖动元素的透明度
        e.target.style.opacity = "0.5";
        // 设置拖动元素的 id
        e.dataTransfer.setData("text/plain", e.target.id);
        // 记录拖动元素的初始位置和鼠标的初始位置
        e.target.startX = e.clientX;
        e.target.startY = e.clientY;
        e.target.offsetX = e.target.offsetLeft;
        e.target.offsetY = e.target.offsetTop;
    });
    element.addEventListener("drag", function(e) {
        // 如果鼠标的位置有效,根据鼠标的移动距离,更新拖动元素的位置
        if (e.clientX > 0 && e.clientY > 0) {
            e.target.style.left = e.target.offsetX + e.clientX - e.target.startX + "px";
            e.target.style.top = e.target.offsetY + e.clientY - e.target.startY + "px";
        }
    });
    element.addEventListener("dragend", function(e) {
        // 恢复拖动元素的透明度
        e.target.style.opacity = "1";
    });
    document.body.appendChild(element);
}

// 全局变量,用于存储唯一的预览链接元素
var previewLink;
// 全局变量,用于标志是否有异步操作正在进行中
var isDownloading = false;

function handleXhrResponse(url) {
    if (isDownloading) {
        return; // 如果已经有下载在进行中,则跳过
    }

    isDownloading = true;

    // 清除之前的预览链接元素
    if (previewLink) {
        previewLink.parentNode.removeChild(previewLink);
        previewLink = null;
    }
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onload = function () {
        // 如果响应的文本中包含一个以 http 或 https 开头的 URL,将其添加到列表中
        // 在此之前,先将响应的文本中的 "}}" 和引号替换为空字符串,去掉多余的符号
        var text = xhr.responseText.replace("}}", "").replace(/"/g, "");
        var match = text.match(/(http|https):\/\/\S+/);
        var titleBannerElement = document.querySelector('.common_node_content_banner h5.title');
        var content;

        if (titleBannerElement && titleBannerElement.getAttribute('title')) {
            content = titleBannerElement.getAttribute('title').trim();
        } else {
            content = titleBannerElement.textContent.trim();
        }

        if (match) {
            // 如果预览链接不存在,则创建
            if (!previewLink) {
                previewLink = document.createElement("a");
                previewLink.style.background = "#06566f";
                // 使用背景剪裁
                previewLink.style.webkitBackgroundClip = "text";
                previewLink.style.backgroundClip = "text";
                // 设置文字颜色为透明以使渐变可见
                previewLink.style.color = "transparent";
                previewLink.style.fontFamily = "'微软雅黑', 'Microsoft YaHei', sans-serif";
                previewLink.style.fontWeight = "bold";
                // 将预览链接添加到列表中
                list.appendChild(previewLink);
                list.appendChild(document.createElement("br"));
            }
            // 更新预览链接的属性
            previewLink.href = match[0];
            previewLink.target = "_blank";
            previewLink.textContent = content;

            // 添加闪烁动画效果
            list.classList.add("blink-animation");
            // 设置定时器,在3秒后清除动画
            setTimeout(function() {
                list.classList.remove("blink-animation");
            }, 3000);

            // 添加点击事件监听器,在点击时进行下载
            previewLink.addEventListener("click", function (event) {
                event.preventDefault(); // 阻止默认的点击行为
                // 异步获取 Blob 对象
                getBlob(match[0], function(blob) {
                    // 使用自定义文件名进行下载
                    saveAs(blob, content);
                });
            });
        }
        isDownloading = false;

        // 将新创建的元素插入到列表的最前面
        var titleElement = list.querySelector("h3");
        if (titleElement) {
            list.insertBefore(previewLink, titleElement.nextSibling);
        } else {
            list.appendChild(previewLink);
        }
    }
    xhr.send();
}

// 异步获取 Blob 对象的函数
function get1Blob(url) {
    return new Promise(resolve => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.responseType = "blob"; // 请求类型是 blob 类型
        xhr.crossOrigin = "*"; // 解决跨域问题
        xhr.onload = () => {
            if (xhr.status === 200) {
                resolve(xhr.response);
            }
        };
        xhr.send();
    });
}

// 下载文件并重新命名的函数
function save1As(blob, content) {
    if (window.navigator.msSaveOrOpenBlob) {
        navigator.msSaveBlob(blob, content);
    } else {
        const link = document.createElement("a");
        const body = document.querySelector("body");
        link.href = window.URL.createObjectURL(blob);
        link.download = content; // 修改文件名
        link.style.display = "none";
        body.appendChild(link);
        link.click();
        body.removeChild(link);
        window.URL.revokeObjectURL(link.href);
    }
}

QingJ © 2025

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