小雅爬爬爬

爬取课件url

目前為 2024-03-12 提交的版本,檢視 最新版本

// ==UserScript==
// @name        小雅爬爬爬
// @match      *://ccnu.ai-augmented.com/*
// @grant       none
// @description 爬取课件url
// @license MIT
// @author     Yi
// @version    1.0.7
// @namespace  https://greasyfork.org/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.addEventListener('mouseover', () => {
                        file_info.style.color = '#000';
                    });
                    file_info.addEventListener('mouseout', function() {
                        file_info.style.color = '';
                    });

                    // 将勾选框和链接添加到容器中
                    file_container.appendChild(checkbox);
                    file_container.appendChild(file_info);

                    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.addEventListener("input", function () {
            filterList(this.value);
        });
        download_list.prepend(searchInput);
    }

    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) {
        // 添加批量下载按钮
        window.bulkDownloadButton = document.createElement('button');
        window.bulkDownloadButton.innerHTML = '批量下载';
        window.bulkDownloadButton.id = "bulkDownloadButton";
        window.bulkDownloadButton.style.position = 'fixed'; // 固定在右上角
        window.bulkDownloadButton.style.top = '20px'; // 调整上方位置
        window.bulkDownloadButton.style.right = '20px'; // 调整右侧位置
        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 fileName = file.innerHTML.toLowerCase();
        var checkbox = file.previousSibling; // Assuming the checkbox is before the link
        if (fileName.includes(keyword)) {
            file.style.display = "block"; // 匹配到的文件显示
            checkbox.style.display = "inline-block"; // 显示勾选框
        } else {
            file.style.display = "none"; // 不匹配的文件隐藏
            checkbox.style.display = "none"; // 隐藏勾选框
        }
    });
}

function add_download_button() {
    // 全局变量用于保存批量下载按钮
    window.bulkDownloadButton = null;
    var down_button = document.createElement('div');
    down_button.innerHTML = '<svg ondblclick="showList()" t="1680053155014" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8308" width="60px" height="60px"><path d="M389.842311 369.275391c0 0-54.207599-52.138474-127.760802-9.55461-65.813938 40.649815-54.193273 115.050316-54.193273 115.050316S61.690526 503.173984 61.690526 652.223648c3.281743 148.84091 158.777213 150.336984 158.777213 150.336984l309.799812-0.467651L386.531915 672.168909l79.646991 0.864694 0.013303-157.420309 102.108562-0.694825 0.732687 157.684322 77.813227 1.106194L530.266527 802.560632l262.197654 0c0 0 144.715963 0.148379 165.04087-141.442406 9.680477-154.855904-139.875724-185.378058-139.875724-185.378058s17.017582-229.233891-193.000666-255.367085C444.611705 201.987341 389.842311 369.275391 389.842311 369.275391z" fill="#fcbb34" p-id="8309"></path></svg><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></div>'; // 您的按钮HTML代码
    down_button.style.position = 'fixed';
    down_button.style.left = '80px';
    down_button.style.bottom = '50px';
    down_button.style.zIndex = '9000';
    down_button.setAttribute('draggable', 'true'); // 设置按钮可拖动
    var isDragging = false; // 用于标记是否正在拖动按钮
    down_button.onmousedown = function(event) {
        isDragging = true; // 当鼠标按下时开始拖动
        var shiftX = event.clientX - down_button.getBoundingClientRect().left;
        var shiftY = event.clientY - down_button.getBoundingClientRect().top;

        down_button.style.position = 'absolute';
        down_button.style.zIndex = 1000;

        function moveAt(pageX, pageY) {
            down_button.style.left = pageX - shiftX + 'px';
            down_button.style.top = pageY - shiftY + 'px';
        }

        function onMouseMove(event) {
            if (isDragging) { // 只有在拖动时才移动按钮
                moveAt(event.pageX, event.pageY);
            }
        }

        document.addEventListener('mousemove', onMouseMove);

        down_button.addEventListener('mouseup', function() {
            isDragging = false; // 当鼠标松开时停止拖动
            document.removeEventListener('mousemove', onMouseMove);
        }, {once: true}); // 使用 {once: true} 使事件监听器只触发一次,避免重复绑定事件
    }

    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 = "300px";
  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(to right bottom, #ffc700, #ffa500)";
  // 为元素添加阴影效果
  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.color = "#40a9ff";
        previewLink.style.fontFamily = "'微软雅黑', 'Microsoft YaHei', sans-serif";
        previewLink.style.fontStyle = "italic";
        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);
  }
}