// ==UserScript==
// @name 华医网课程自动收藏工具-增强版(批量)V2.5 稳定【赛博朋克风格】
// @namespace http://tampermonkey.net/
// @version 2.5
// @description 华医网课程批量收藏工具!稳定!业内人士专用!
// @author vx:hapens1986 课程代学可咨询!
// @license MIT
// @match https://cme28.91huayi.com/*
// @ICON https://cdn-icons-png.flaticon.com/128/4594/4594681.png
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @grant GM_notification
// @connect 91huayi.com
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(`
#cid-input-container {
position: fixed;
top: 100px;
right: 20px;
z-index: 9999;
background: #0a0a1a;
padding: 15px;
border: 1px solid #00f0ff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 240, 255, 0.3);
font-family: 'Courier New', monospace;
width: 300px;
cursor: move;
color: #00f0ff;
}
#cid-input-container.dragging {
opacity: 0.8;
box-shadow: 0 0 15px rgba(0, 240, 255, 0.5);
}
#cid-input-container-header {
padding: 5px;
margin: -15px -15px 10px -15px;
background: #121230;
border-bottom: 1px solid #00f0ff;
border-radius: 5px 5px 0 0;
font-weight: bold;
user-select: none;
text-shadow: 0 0 5px #00f0ff;
}
#cid-input, #cid-input-batch {
width: 100%;
padding: 8px;
margin: 10px 0 5px;
background: #0f0f1a;
border: 1px solid #ff00aa;
border-radius: 3px;
box-sizing: border-box;
color: #00f0ff;
font-family: 'Courier New', monospace;
}
#cid-input-batch {
height: 300px;
resize: vertical;
}
#collect-btn, #batch-collect-btn {
background: linear-gradient(90deg, #ff00aa, #00f0ff);
color: #0a0a1a;
border: none;
padding: 8px 15px;
border-radius: 3px;
cursor: pointer;
width: 100%;
margin: 5px 0;
font-weight: bold;
transition: all 0.3s;
text-shadow: none;
}
#batch-collect-btn {
background: linear-gradient(90deg, #00f0ff, #ff00aa);
}
#collect-btn:hover, #batch-collect-btn:hover {
box-shadow: 0 0 10px rgba(0, 240, 255, 0.7);
}
#collect-btn:disabled, #batch-collect-btn:disabled {
background: #333344;
color: #666677;
cursor: not-allowed;
box-shadow: none;
}
#status-message {
margin-top: 10px;
font-size: 13px;
line-height: 1.4;
}
.success {
color: #00ff88;
text-shadow: 0 0 3px #00ff88;
}
.error {
color: #ff0055;
text-shadow: 0 0 3px #ff0055;
}
.warning {
color: #ffaa00;
text-shadow: 0 0 3px #ffaa00;
}
.loading {
color: #00f0ff;
text-shadow: 0 0 3px #00f0ff;
}
.course-name {
font-weight: bold;
margin: 5px 0;
}
.hidden-iframe {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.toggle-mode-btn {
background: #333344;
color: #00f0ff;
border: 1px solid #00f0ff;
padding: 6px 12px;
border-radius: 3px;
cursor: pointer;
width: 100%;
margin: 5px 0;
transition: all 0.3s;
font-size: 13px;
}
.toggle-mode-btn:hover {
background: #444455;
box-shadow: 0 0 8px rgba(0, 240, 255, 0.5);
}
.batch-instruction {
font-size: 12px;
color: #6666ff;
margin: 5px 0;
}
.progress-container {
margin: 10px 0;
font-size: 13px;
font-family: 'Courier New', monospace;
}
.progress-text {
font-weight: bold;
color: #00f0ff;
text-shadow: 0 0 3px #00f0ff;
}
.cyber-progress {
height: 20px;
background: #0f0f1a;
position: relative;
overflow: hidden;
border: 1px solid #00f0ff;
box-shadow: 0 0 10px rgba(0, 240, 255, 0.3);
margin-top: 5px;
}
.cyber-bar {
height: 100%;
width: 0%;
background: linear-gradient(90deg, #ff00aa, #00f0ff);
transition: width 0.5s ease;
position: relative;
}
.cyber-glitch {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent 25%,
rgba(255, 255, 255, 0.3) 50%,
transparent 75%
);
background-size: 200% 100%;
animation: glitch 2s linear infinite;
opacity: 0.3;
}
@keyframes glitch {
0% { background-position: 200% 0; }
}
@media (max-width: 400px) {
#cid-input-container {
width: 280px;
right: 10px;
}
}
`);
const container = document.createElement('div');
container.id = 'cid-input-container';
container.innerHTML = `
提示:可以输入多个CID,每行一个或用逗号分隔
`;
document.body.appendChild(container);
// 获取元素
const cidInput = document.getElementById('cid-input');
const cidInputBatch = document.getElementById('cid-input-batch');
const collectBtn = document.getElementById('collect-btn');
const batchCollectBtn = document.getElementById('batch-collect-btn');
const statusMessage = document.getElementById('status-message');
const hiddenIframe = document.getElementById('hidden-iframe');
const singleMode = document.getElementById('single-mode');
const batchMode = document.getElementById('batch-mode');
const modeToggle = document.getElementById('mode-toggle');
const progressText = document.querySelector('.progress-text');
const cyberProgressBar = document.getElementById('cyber-progress-bar');
// 当前模式
let isBatchMode = false;
// 设置状态消息
function setStatus(message, type = '') {
statusMessage.innerHTML = message;
statusMessage.className = type;
}
// 更新进度条
function updateProgress(current, total) {
const percent = Math.round((current / total) * 100);
progressText.textContent = `进度: ${percent}% [${current}/${total}]`;
cyberProgressBar.style.width = `${percent}%`;
}
// 初始化拖动功能
function initDrag() {
const container = document.getElementById('cid-input-container');
const header = document.getElementById('cid-input-container-header');
let isDragging = false;
let offsetX, offsetY;
header.addEventListener('mousedown', function(e) {
isDragging = true;
container.classList.add('dragging');
// 计算鼠标位置与面板位置的偏移量
const rect = container.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
e.preventDefault();
});
document.addEventListener('mousemove', function(e) {
if (!isDragging) return;
// 计算新位置
let left = e.clientX - offsetX;
let top = e.clientY - offsetY;
// 限制在视窗范围内
const maxLeft = window.innerWidth - container.offsetWidth;
const maxTop = window.innerHeight - container.offsetHeight;
left = Math.max(0, Math.min(left, maxLeft));
top = Math.max(0, Math.min(top, maxTop));
// 应用新位置
container.style.left = left + 'px';
container.style.top = top + 'px';
container.style.right = 'auto';
});
document.addEventListener('mouseup', function() {
isDragging = false;
container.classList.remove('dragging');
});
}
// 切换模式
function toggleMode() {
isBatchMode = !isBatchMode;
singleMode.style.display = isBatchMode ? 'none' : 'block';
batchMode.style.display = isBatchMode ? 'block' : 'none';
modeToggle.textContent = isBatchMode ? '切换到单条模式' : '切换到批量模式';
setStatus('');
updateProgress(0, 0);
}
// 获取课程信息
async function getCourseInfo(cid) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: `https://cme28.91huayi.com/pages/course.aspx?cid=${cid}`,
onload: function(response) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(response.responseText, "text/html");
const title = doc.querySelector('h1.course-title')?.textContent?.trim() || `课程 (CID: ${cid})`;
const isCollected = doc.getElementById('btnCollect')?.getAttribute('data-collect') === '1';
resolve({ title, isCollected });
} catch (e) {
reject(`解析课程页面失败: ${e.message}`);
}
},
onerror: function(error) {
reject(`获取课程信息失败: ${error.statusText}`);
},
timeout: 10000
});
});
}
function collectWithIframe(cid, title) {
return new Promise((resolve) => {
hiddenIframe.src = `https://cme28.91huayi.com/pages/course.aspx?cid=${cid}`;
let checkCount = 0;
const maxChecks = 5;
const checkInterval = 1000;
const checkCollection = setInterval(() => {
checkCount++;
try {
const iframeDoc = hiddenIframe.contentDocument || hiddenIframe.contentWindow.document;
const collectBtn = iframeDoc.getElementById('btnCollect');
if (collectBtn) {
clearInterval(checkCollection);
if (collectBtn.getAttribute('data-collect') === '1') {
resolve(true);
} else {
collectBtn.click();
setTimeout(() => {
resolve(true);
}, 1500);
}
} else if (checkCount >= maxChecks) {
clearInterval(checkCollection);
resolve(false);
}
} catch (e) {
if (checkCount >= maxChecks) {
clearInterval(checkCollection);
resolve(false);
}
}
}, checkInterval);
setTimeout(() => {
clearInterval(checkCollection);
resolve(false);
}, (maxChecks + 2) * checkInterval);
});
}
// 收藏单个课程
async function collectSingleCourse(cid) {
try {
// 获取课程信息
const { title, isCollected } = await getCourseInfo(cid);
if (isCollected) {
return { success: false, message: `课程已收藏: ${title}` };
}
// 使用iframe方式收藏
const success = await collectWithIframe(cid, title);
if (success) {
return { success: true, message: `收藏成功: ${title}` };
} else {
GM_openInTab(`https://cme28.91huayi.com/pages/course.aspx?cid=${cid}`, false);
return { success: false, message: `请在新标签页中手动收藏: ${title}` };
}
} catch (error) {
return { success: false, message: `收藏失败: ${error}` };
}
}
async function handleSingleCollect() {
const cid = cidInput.value.trim();
if (!cid) {
setStatus('请输入有效的CID值', 'error');
return;
}
collectBtn.disabled = true;
setStatus('正在处理...', 'loading');
updateProgress(0, 1);
const result = await collectSingleCourse(cid);
updateProgress(1, 1);
if (result.success) {
setStatus(`${result.message}
`, 'success');
GM_notification({
title: '收藏成功',
text: result.message,
timeout: 3000,
highlight: true
});
} else {
setStatus(`${result.message}
`, result.message.includes('手动收藏') ? 'warning' : 'error');
}
collectBtn.disabled = false;
}
async function handleBatchCollect() {
const input = cidInputBatch.value.trim();
if (!input) {
setStatus('请输入有效的CID值', 'error');
return;
}
const cids = input.split(/[\n,]+/).map(cid => cid.trim()).filter(cid => cid);
if (cids.length === 0) {
setStatus('未检测到有效的CID值', 'error');
return;
}
batchCollectBtn.disabled = true;
setStatus(`准备收藏 ${cids.length} 个课程...`, 'loading');
updateProgress(0, cids.length);
let successCount = 0;
let messages = [];
// 逐个处理课程
for (let i = 0; i < cids.length; i++) {
const cid = cids[i];
setStatus(`正在处理第 ${i+1}/${cids.length} 个课程 (CID: ${cid})...`, 'loading');
updateProgress(i, cids.length);
const result = await collectSingleCourse(cid);
if (result.success) {
successCount++;
messages.push(`✓ ${result.message}`);
} else {
messages.push(`✗ ${result.message}`);
}
// 稍微延迟一下,避免请求过于频繁
await new Promise(resolve => setTimeout(resolve, 500));
}
// 显示最终结果
updateProgress(cids.length, cids.length);
setStatus(`
已完成 ${cids.length} 个课程处理
成功收藏: ${successCount} 个
失败: ${cids.length - successCount} 个
${messages.join('
')}
`, successCount === cids.length ? 'success' : (successCount > 0 ? 'warning' : 'error'));
batchCollectBtn.disabled = false;
// 显示桌面通知
if (successCount > 0) {
GM_notification({
title: '批量收藏完成',
text: `成功收藏 ${successCount}/${cids.length} 个课程`,
timeout: 5000,
highlight: true
});
}
}
// 监听收藏按钮点击
collectBtn.addEventListener('click', handleSingleCollect);
batchCollectBtn.addEventListener('click', handleBatchCollect);
modeToggle.addEventListener('click', toggleMode);
// 监听输入框回车键
cidInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
handleSingleCollect();
}
});
// 初始化拖动功能
initDrag();
// 自动填充当前页面CID
function getCIDFromURL() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('cid');
}
const currentCID = getCIDFromURL();
if (currentCID) {
cidInput.value = currentCID;
}
})();