// ==UserScript==
// @name 小雅爬爬爬
// @match *://ccnu.ai-augmented.com/*
// @grant none
// @description 爬取课件url
// @license MIT
// @author Yi
// @version 1.1.0
// @namespace https://gf.qytechs.cn/users/1268039
// ==/UserScript==
(function() {
'use strict';
// 动态导入dotlottie-player模块
const script = document.createElement('script');
script.src = 'https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs';
script.type = 'module'; // 指定导入类型为module
document.head.appendChild(script); // 将脚本添加到<head>中
// 监听脚本的加载事件以确认导入成功
script.onload = () => {
console.log('dotlottie-player模块已导入成功!');
// 在此处添加脚本加载后需要执行的任何代码
};
// 监听错误事件以确认导入失败
script.onerror = () => {
console.error('无法导入dotlottie-player模块!');
};
})();
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; font-weight:bold;">课程附件清单</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';
checkbox.setAttribute('data-visible', 'true'); // 默认设置为可见
// 创建链接元素
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.style.fontWeight = "bold";
});
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.fontWeight = "bold";
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);
});
}
}
}
// 在文档中创建一个容器来放置所有下载进度条
let downloadsContainer = document.getElementById('downloadsContainer');
if (!downloadsContainer) {
downloadsContainer = document.createElement('div');
downloadsContainer.id = 'downloadsContainer';
downloadsContainer.style.position = 'fixed';
downloadsContainer.style.bottom = '10px';
downloadsContainer.style.left = '50%';
downloadsContainer.style.transform = 'translateX(-50%)';
downloadsContainer.style.zIndex = '9999';
downloadsContainer.style.backgroundColor = '#FFF3E0';
downloadsContainer.style.boxShadow = '0 4px 6px rgba(255, 165, 0, 0.3)';
downloadsContainer.style.borderRadius = '8px';
downloadsContainer.style.padding = '15px';
downloadsContainer.style.width = 'auto';
downloadsContainer.style.maxWidth = '500px';
downloadsContainer.style.boxSizing = 'border-box';
downloadsContainer.style.margin = '0 auto';
downloadsContainer.style.border = '1px solid #FFD180';
// 设置最大高度限制和溢出滚动
downloadsContainer.style.maxHeight = '400px'; // 根据实际需求调整此值
downloadsContainer.style.overflowY = 'auto'; // 当内容超出容器时显示垂直滚动条
document.body.appendChild(downloadsContainer);
// 创建 dotlottie-player 容器
let lottieContainer = document.createElement('div');
lottieContainer.style.position = 'absolute';
lottieContainer.style.top = '0';
lottieContainer.style.left = '0';
lottieContainer.style.width = '100%';
lottieContainer.style.height = '100%';
lottieContainer.style.zIndex = '-1'; // 确保 lottie 在底层
lottieContainer.innerHTML = `
<dotlottie-player src="https://lottie.host/21dfc4c5-2d42-4f18-b67b-e3c8e50a2ce5/YYdHUeyfW2.json"
background="transparent" speed="1"
style="width: 100%; height: 100%;" loop autoplay>
</dotlottie-player>`;
// 将 dotlottie-player 容器添加到 downloadsContainer 的顶部
downloadsContainer.prepend(lottieContainer);
}
// 确保 downloadsContainer 被添加到文档中
if (!document.body.contains(downloadsContainer)) {
document.body.appendChild(downloadsContainer);
}
// 初始化拖动功能的变量
let isDragging = false;
let dragStartX = 0;
let dragStartY = 0;
// 函数:开始拖动
function dragStart(e) {
// 记录初始拖动位置
isDragging = true;
dragStartX = e.clientX - downloadsContainer.getBoundingClientRect().left;
dragStartY = e.clientY - downloadsContainer.getBoundingClientRect().top;
document.addEventListener('mousemove', dragMove);
document.addEventListener('mouseup', dragEnd);
}
// 函数:拖动时调用
function dragMove(e) {
if (!isDragging) return; // 如果不是拖动状态,则退出
// 计算容器新的位置
let leftPos = e.clientX - dragStartX;
let topPos = e.clientY - dragStartY;
// 更新容器位置
downloadsContainer.style.left = `${leftPos}px`;
downloadsContainer.style.top = `${topPos}px`;
downloadsContainer.style.bottom = 'auto'; // 当开始拖动时,移除bottom样式以允许自由定位
downloadsContainer.style.transform = 'none'; // 移除transform样式,因为已经改为绝对定位
}
// 函数:停止拖动时调用
function dragEnd() {
// 更新状态
isDragging = false;
document.removeEventListener('mousemove', dragMove);
document.removeEventListener('mouseup', dragEnd);
}
// 添加鼠标按下时的事件监听器来初始化拖拽
downloadsContainer.addEventListener('mousedown', dragStart);
function updateDownloadsContainerVisibility() {
downloadsContainer.style.display = downloadsContainer.children.length > 0 ? 'block' : 'none';
}
function courseDownload(file_url, file_name) {
const downloadsContainer = document.getElementById('downloadsContainer');
downloadsContainer.style.display = 'block';
// 创建进度条相关元素。
let progressText = document.createElement('span');
let progressBar = document.createElement('div');
let progressBarContainer = document.createElement('div');
let progressContainer = document.createElement('div');
// 设置文本。
progressText.innerText = `正在下载: ${file_name}`;
progressText.style.color = '#FF9800';
progressText.style.fontWeight = 'bold';
progressText.style.fontSize = '16px';
progressText.style.fontFamily = 'Microsoft YaHei';
progressText.style.textShadow = '1px 1px 2px rgba(0, 0, 0, 0.1)';
progressText.style.padding = '5px 0';
progressText.style.borderRadius = '4px';
// 设置progressBar。
progressBar.style.height = '18px';
progressBar.style.width = '0%';
progressBar.style.borderRadius = '8px';
progressBar.className = 'progressBar'
// 设置progressBarContainer。
progressBarContainer.style.background = '#E0E0E0';
progressBarContainer.style.borderRadius = '8px';
progressBarContainer.style.height = '18px';
progressBarContainer.style.width = '100%';
progressBarContainer.style.overflow = 'hidden';
progressBarContainer.appendChild(progressBar);
// 设置 progressContainer。
progressContainer.style.display = 'flex';
progressContainer.style.flexDirection = 'column'; // 子元素垂直排列
progressContainer.style.alignItems = 'center'; // 子元素在交叉轴上居中对齐
progressContainer.style.justifyContent = 'space-around'; // 子元素沿着主轴均匀分布
progressContainer.appendChild(progressText);
// 创建一个用来显示下载百分比的span元素
let progressPercentText = document.createElement('span');
progressPercentText.style.fontFamily = 'Arial Rounded MT Bold, Helvetica Rounded, Arial, sans-serif';
progressPercentText.style.fontSize = '16px';
progressPercentText.style.marginLeft = '10px'; // 与进度条保持一定距离
progressPercentText.style.position = 'absolute';
progressPercentText.style.left = '0';
progressPercentText.style.top = '0';
progressPercentText.style.width = '100%';
progressPercentText.style.textAlign = 'center';
progressPercentText.style.lineHeight = '18px';
progressPercentText.style.zIndex = '1';
// 调整字体粗细
progressPercentText.style.fontWeight = 'bold';
// 设置 progressBarContainer 的样式并添加到 progressContainer
progressContainer.appendChild(progressBarContainer);
// 插入用于显示百分比的 DOM 元素到 progressContainer
progressBarContainer.style.position = 'relative'; // 相对定位,以便内部的百分比文本能正确定位
progressBarContainer.appendChild(progressPercentText);
// 添加 progressContainer 到 downloadsContainer
downloadsContainer.appendChild(progressContainer);
const controller = new AbortController();
const signal = controller.signal;
window.abortControllers = window.abortControllers || {};
window.abortControllers[file_name] = controller;
// 添加文件大小和停止按钮到一个新的横向排列的容器
let controlContainer = document.createElement('div');
controlContainer.style.display = 'flex';
controlContainer.style.justifyContent = 'space-between'; // 在主轴线上实现两端对齐
controlContainer.style.alignItems = 'center'; // 在交叉轴上实现居中对齐
controlContainer.style.width = '100%'; // 确保容器宽度与进度条相匹配
// 创建文件大小显示元素
let fileSizeSpan = document.createElement('span');
// 设置样式为微软雅黑和圆润
fileSizeSpan.style.fontFamily = 'Microsoft YaHei';
fileSizeSpan.style.fontSize = '14px';
fileSizeSpan.style.marginRight = 'auto';
fileSizeSpan.style.fontWeight = 'bold';
let stopButton = document.createElement('button');
stopButton.textContent = '停止';
stopButton.style.padding = '5px 10px';
stopButton.style.border = 'none';
stopButton.style.borderRadius = '5px';
stopButton.style.backgroundColor = '#FF4136';
stopButton.style.color = 'white';
stopButton.style.cursor = 'pointer';
stopButton.style.fontSize = '14px';
stopButton.style.fontWeight = 'bold';
stopButton.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
stopButton.style.transition = 'transform 0.3s ease';
stopButton.style.marginTop = '10px'; // 增加顶部外边距以调整与进度条的距离
stopButton.onmouseover = function() {
stopButton.style.transform = 'scale(1.2)';
};
stopButton.onmouseout = function() {
stopButton.style.transform = 'scale(1)';
};
stopButton.onclick = function() {
console.log(file_name + ' 的下载已经停止了');
controller.abort();
progressContainer.remove();
delete window.abortControllers[file_name];
updateDownloadsContainerVisibility();
};
controlContainer.appendChild(fileSizeSpan);
controlContainer.appendChild(stopButton);
progressContainer.appendChild(controlContainer);
let styleElement = document.getElementById('progress-bar-styles');
if (!styleElement) {
let styles = document.createElement('style');
styles.id = 'progress-bar-styles';
styles.textContent = `
.progressBar {
background-color: #FF9800;
background-image: repeating-linear-gradient(
45deg,
rgba(255, 255, 255, 0.15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.15) 75%,
transparent 75%,
transparent
);
background-size: 40px 40px;
animation: moveBackground 2s linear infinite;
}
@keyframes moveBackground {
from { background-position: 0 0; }
to { background-position: 40px 0; }
}
`;
document.head.appendChild(styles);
}
function bytesToSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) return '0 Byte';
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
}
// 使用fetch API开始下载文件
fetch(file_url, { signal })
.then(response => {
// 获取文件大小信息
const contentLength = response.headers.get('Content-Length');
if (contentLength) {
// 更新文件大小显示
const fileSize = bytesToSize(contentLength);
fileSizeSpan.innerText = `文件大小: ${fileSize}`;
} else {
// 如果无法获取文件大小,则显示消息
fileSizeSpan.innerText = `无法获取文件大小`;
}
const reader = response.body.getReader();
let receivedBytes = 0;
let chunks = [];
reader.read().then(function processResult(result) {
if (result.done) {
// 下载完成后的处理
const blob = new Blob(chunks, { type: 'application/octet-stream' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = file_name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(downloadUrl);
downloadsContainer.removeChild(progressContainer);
updateDownloadsContainerVisibility(); // 确保定义了这个函数来更新下载容器元素的显示
return;
}
// 存储接收到的数据块
chunks.push(result.value);
receivedBytes += result.value.length;
// 计算下载的百分比并更新UI
let percentComplete = (receivedBytes / contentLength) * 100;
progressBar.style.width = `${percentComplete.toFixed(2)}%`; // 更新进度条的宽度
progressPercentText.innerText = `${percentComplete.toFixed(2)}%`;
// 读取下一部分数据
reader.read().then(processResult);
})
.catch(e => {
// 下载失败或被中止时的处理
if (e.name === 'AbortError') {
console.error(`${file_name} 的下载被用户取消了`);
} else {
console.error(e);
}
progressContainer.remove();
updateDownloadsContainerVisibility(); // 确保定义了这个函数来更新下载容器元素的显示
});
});
}
window.showList = function () {
var download_list = document.getElementById("download_list");
if (!document.getElementById("lottie-animation-container")) {
var lottieContainer = document.createElement("div");
lottieContainer.id = "lottie-animation-container";
lottieContainer.innerHTML = `
<dotlottie-player src="https://lottie.host/f6cfdc36-5c9a-4dac-bb71-149cdf2e7d92/VRIhn9vXE5.json"
background="transparent" speed="1"
style="width: 300px; height: 300px; transform: scaleX(-1);" loop autoplay>
</dotlottie-player>`; // 使用 transform: scaleX(-1); 实现水平翻转
lottieContainer.style.position = "absolute";
lottieContainer.style.top = "50%";
lottieContainer.style.right = "0";
lottieContainer.style.transform = "translateY(-50%)"; // 垂直居中
lottieContainer.style.zIndex = "-1"; // 确保它在内容之后作为背景
download_list.style.position = "relative"; // 确保下载列表具有相对位置
download_list.appendChild(lottieContainer); // 将动画容器插入为子元素
}
// 创建或获取容器,用于并排放置搜索框和快速筛选下拉框
var container = document.getElementById("searchAndFilterContainer");
if (!container) {
container = document.createElement("div");
container.id = "searchAndFilterContainer";
container.style.display = "flex"; // 应用 Flexbox 布局
container.style.marginBottom = '10px'; // 添加一点底部外边距
download_list.prepend(container); // 将容器添加到下载列表的最前面
}
// 检查是否已经存在搜索框
var searchInput = document.getElementById("searchInput");
if (!searchInput) {
// 如果不存在,则创建搜索框
searchInput = document.createElement("input");
searchInput.type = "text";
searchInput.placeholder = "搜索文件名";
searchInput.id = "searchInput";
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);
});
container.appendChild(searchInput); // 将搜索框添加到容器中
}
var quickFilterSelect = document.getElementById("quickFilterSelect");
if (!quickFilterSelect) {
// 如果还没有快速筛选下拉框,则创建
quickFilterSelect = document.createElement("select");
quickFilterSelect.id = "quickFilterSelect";
quickFilterSelect.style.padding = '5px';
quickFilterSelect.style.marginRight = '10px';
quickFilterSelect.style.border = '1px solid #ddd';
quickFilterSelect.style.borderRadius = '4px';
window.quickFilters.forEach(function (filter) {
var option = document.createElement("option");
option.value = filter.value;
option.text = filter.label;
quickFilterSelect.appendChild(option);
});
quickFilterSelect.addEventListener("change", function () {
filterListByCategory(this.value);
});
container.appendChild(quickFilterSelect); // 将快速筛选下拉框添加到容器中
}
// 检查是否已存在全选复选框
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;
// 手动触发 change 事件
var event = new Event('change', {
'bubbles': true, // 使事件冒泡
'cancelable': true // 使事件可以被取消
});
checkbox.dispatchEvent(event);
}
});
});
}
if (download_list.style.display == "none") {
download_list.style.display = "flex";
download_list.style.overflowY = "auto"; // 添加垂直滚动条
download_list.style.backgroundColor = "#ffffff";
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");
checkboxes.forEach(function(checkbox) {
// 检查是否真的勾选了复选框,并且复选框是可见的
if (checkbox.checked && checkbox.getAttribute('data-visible') === 'true') {
var container = checkbox.closest('div');
var link = container.querySelector('a');
var file_name = link.textContent;
var file_url = link.href;
// 调用下载函数
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.textContent.toLowerCase();
var checkbox = container.querySelector("input[type='checkbox']");
if (fileName.includes(keyword)) {
container.style.display = "";
checkbox.setAttribute('data-visible', 'true');
} else {
container.style.display = "none";
checkbox.setAttribute('data-visible', 'false'); // 当文件被过滤掉时,设置复选框为不可见
}
});
}
function filterListByCategory(categoryValue) {
var files = document.querySelectorAll("#download_list a");
var extensions = categoryValue.split(',').map(ext => ext.trim()); // 通过逗号分隔并移除空格得到后缀名数组
files.forEach(function(file) {
var container = file.parentElement;
var checkbox = container.querySelector("input[type='checkbox']");
// 提取文件扩展名
var fileName = file.textContent.toLowerCase();
var fileExtension = fileName.slice(((fileName.lastIndexOf(".") - 1) >>> 0) + 2);
// 检查文件扩展名是否在分类中
var isVisible = categoryValue === "" || extensions.includes(fileExtension);
container.style.display = isVisible ? "" : "none";
checkbox.setAttribute('data-visible', isVisible.toString());
});
// 如果快速筛选后,全选复选框仍然选中,且不对应任何选项时,取消其勾选状态
var selectAllCheckbox = document.getElementById('selectAllCheckbox');
if (selectAllCheckbox && selectAllCheckbox.checked) {
var anyCheckboxVisible = Array.from(document.querySelectorAll("#download_list input[type='checkbox']")).some(checkbox => checkbox.getAttribute('data-visible') === 'true');
if (!anyCheckboxVisible) {
selectAllCheckbox.checked = 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; font-weight:bold;">课程附件清单</h3>
</div>
<dotlottie-player src="https://lottie.host/67403f9b-b050-4414-9307-1096cb6088f2/lb6v8cv81d.json" background="transparent" speed="1" style="width: 100px; height: 100px; margin: -15px;" loop autoplay onclick="showList()"></dotlottie-player>
`;
// 添加下载图标的样式
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: -5px;
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();
}
};
// 定义快速筛选的类别选项
window.quickFilters = [
{ label: "全部", value: "" },
{ label: "文档", value: "doc,docx,pdf,txt,odt,rtf,html,htm,xls,xlsx,ppt,pptx,odp" },
{ label: "图片", value: "jpg,jpeg,png,gif,bmp,tiff,svg,webp" },
{ label: "压缩包", value: "zip,rar,7z,gz,bz2,tar" },
// 可以继续添加其他类别
];
window.abortControllers = {};
// 创建一个 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",".txt",".odt",".rtf",".jpg",".jpeg",".png",".gif",".bmp",".tiff",".svg",".webp",".zip",".rar",".7z",".tar",".gz",".bz2",".xz"];
// 创建一个元素,用于显示抓取到的 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 = "9997";
element.style.padding = "10px";
// 为元素添加渐变背景色
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";
// 创建一个容器来存放Lottie动画
var lottieContainer = document.createElement('div');
lottieContainer.style.position = "absolute";
lottieContainer.style.top = "-20px";
lottieContainer.style.left = "95px";
lottieContainer.style.width = "120%";
lottieContainer.style.height = "120%";
lottieContainer.style.zIndex = "9998";
lottieContainer.style.pointerEvents = "none"; // 确保动画不会拦截鼠标事件
// 创建并配置Lottie播放器
var lottiePlayer = document.createElement('dotlottie-player');
lottiePlayer.setAttribute('src', "https://lottie.host/995b71c8-b7aa-45b0-bb77-94b850da5d5d/dyezqbvtia.json");
lottiePlayer.setAttribute('background', "transparent");
lottiePlayer.setAttribute('speed', "1");
lottiePlayer.setAttribute('style', "width: 100%; height: 100%;");
lottiePlayer.setAttribute('loop', "");
lottiePlayer.setAttribute('autoplay', "");
// 将Lottie播放器添加到动画容器中
lottieContainer.appendChild(lottiePlayer);
// 将动画容器添加为列表元素的子元素
element.appendChild(lottieContainer);
// 添加拖动事件监听器
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";
// 悬停时的样式变化
previewLink.addEventListener('mouseover', () => {
previewLink.style.backgroundColor = '#ffffff'; // 更改背景色
});
previewLink.addEventListener('mouseout', () => {
previewLink.style.backgroundColor = '#06566f'; // 恢复背景色
});
// 将预览链接添加到列表中
list.appendChild(previewLink);
list.appendChild(document.createElement("br"));
}
// 更新预览链接的属性
previewLink.href = match[0];
previewLink.target = "_blank";
previewLink.textContent = content;
// 添加点击事件监听器,在点击时进行下载
previewLink.addEventListener("click", function (event) {
event.preventDefault(); // 阻止默认的点击行为
// 直接使用 courseDownload 函数进行下载
courseDownload(match[0], content);
});
}
isDownloading = false;
// 将新创建的元素插入到列表的最前面
var titleElement = list.querySelector("h3");
if (titleElement) {
list.insertBefore(previewLink, titleElement.nextSibling);
} else {
list.appendChild(previewLink);
}
}
xhr.send();
}