// ==UserScript==
// @name 电信网上大学超级学习
// @namespace remain_true_to_our_original_aspiration
// @version 1.5.0
// @description 更快、更全、更好用的电信网上大学(知学云zhixueyun)学习工具。
// @author Ghost River
// @match https://*.zhixueyun.com/*
// @icon data:image/gif;base64,R0lGODlhIAAgAPcAAAAAAAAAMwAAZgAAmQAAzAAA/wArAAArMwArZgArmQArzAAr/wBVAABVMwBVZgBVmQBVzABV/wCAAACAMwCAZgCAmQCAzACA/wCqAACqMwCqZgCqmQCqzACq/wDVAADVMwDVZgDVmQDVzADV/wD/AAD/MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMrADMrMzMrZjMrmTMrzDMr/zNVADNVMzNVZjNVmTNVzDNV/zOAADOAMzOAZjOAmTOAzDOA/zOqADOqMzOqZjOqmTOqzDOq/zPVADPVMzPVZjPVmTPVzDPV/zP/ADP/MzP/ZjP/mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2YrAGYrM2YrZmYrmWYrzGYr/2ZVAGZVM2ZVZmZVmWZVzGZV/2aAAGaAM2aAZmaAmWaAzGaA/2aqAGaqM2aqZmaqmWaqzGaq/2bVAGbVM2bVZmbVmWbVzGbV/2b/AGb/M2b/Zmb/mWb/zGb//5kAAJkAM5kAZpkAmZkAzJkA/5krAJkrM5krZpkrmZkrzJkr/5lVAJlVM5lVZplVmZlVzJlV/5mAAJmAM5mAZpmAmZmAzJmA/5mqAJmqM5mqZpmqmZmqzJmq/5nVAJnVM5nVZpnVmZnVzJnV/5n/AJn/M5n/Zpn/mZn/zJn//8wAAMwAM8wAZswAmcwAzMwA/8wrAMwrM8wrZswrmcwrzMwr/8xVAMxVM8xVZsxVmcxVzMxV/8yAAMyAM8yAZsyAmcyAzMyA/8yqAMyqM8yqZsyqmcyqzMyq/8zVAMzVM8zVZszVmczVzMzV/8z/AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8Amf8AzP8A//8rAP8rM/8rZv8rmf8rzP8r//9VAP9VM/9VZv9Vmf9VzP9V//+AAP+AM/+AZv+Amf+AzP+A//+qAP+qM/+qZv+qmf+qzP+q///VAP/VM//VZv/Vmf/VzP/V////AP//M///Zv//mf//zP///wAAAAAAAAAAAAAAACH5BAEAAPwALAAAAAAgACAAAAj/APcJHEiw4D5i9AwqXLiwnR9UD3nVY8iQXsKFxGr5eXWRIsFCfgrSs1XrFS93+2z54eVx4LBXqAqiKsSrly2QLDe23DfzIU2BtQbqQ/XqlcBXhDxqTGjrVch9fughpPfn51GjC3v5mbiPECquKmfSc5VqGEE/vRbaGuiwo621+3oRKtRxn0OGL/38cbWRZS+W+4rGLPgKbkGVtdwFDexnWDunM18VMkjvKeG1vP4E3ZjX1jCVdQf6aWfwMy/O9ZwWdKzW8EB6Md1F5UXIFkqplfcNa7x6cMFCw1wVmomqskaOm1G53pfbIC9Uvcz6Wcv5YNFCy5lbJuiu48WtApXNrSStcLtHVPqG9SpZEKHA5gYz+rFM6E9MrQV5ASbme2AviETB1RQqZg0FmECpAMbLYgM9R0haBM1kWCGpECJZZAhCSJCGZ2H13kaFIKWcdjsJRIxCnxVmVkoMltiSMuC5uNOILSVkkY0HMUfYZDthtg9La7mSEkEauWjLIhOl1Ys+xNQkWioyrhXUWrbUY5ZRp7W400XK0LMkc/QM9wd5MopE4Xy1rFgmZY6FtlBAADs=
// @license GPLv3
// @grant GM_addStyle
// @grant GM_openInTab
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// @grant window.close
// @grant window.focus
// @grant unsafeWindow
// ==/UserScript==
(function() {
'use strict';
if (!window.location.href.match('/train-new/class-detail/|/study/subject/detail/|/study/course/detail/|/study/course/out-detail/')) return;
//非学习页面退出。
const ver = '1.5.0';
const superCss = '.study_box{position:fixed;top:2%;left:1%;z-index:99999}.view_box{display:none;color:red;}.progress_box{display:none;background-color:skyblue;width:400px}.progress_item{display:flex;justify-content:space-between}.progress_item_title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;width:280px}dialog{width:500px;padding:10px;border:solid 1px #000;border-radius:10px;background-color:#fff;box-shadow:0 0 9px #666777;}';
GM_addStyle(superCss);
const max = 1;
//当前只能为1
var lessons = [];
var lessonsinfo = {};
var sections = [];
var isTrain = false;
var taskID = 0;
var initID = '';
var hasExam = [];
var hasError = [];
var currentSection = '';
var studying = {};
var count = 0;
var listenerId = null
var urls = { 'normal':`${window.location.origin}/#/study/course/detail/10&$id$/6/1`,
'course':`${window.location.origin}/#/study/course/detail/11&$id$/5/1`}
var isCourse = window.location.href.match("/study/course/detail/|/study/course/out-detail/")
//是否学习页
function downPDF() {
let modleId = Object.keys(unsafeWindow.app._modules).find(value => value.startsWith('picker/pdf-filecloud--'));
if(modleId) {
const base64PDF = unsafeWindow.app._modules[modleId].store.models.downEncode.data;
if (base64PDF.length>0 || unsafeWindow.app._modules[modleId].store.models.downFile.data.type == 'pdf') {
const url = base64PDF.length>0? ('data:application/octe-stream;base64,' + base64PDF):unsafeWindow.app._modules[modleId].store.models.downEncode.options.url;
const title = $('.chapter-list-box.required[data-sectiontype=1]').length>1? $('.course-title-text').text()+'-':'';
let filename = base64PDF.length>0? $('.chapter-list-box.required.focus').find('.text-overflow').text().split(':')[1].trim():unsafeWindow.app._modules[modleId].store.models.downFile.data.filename;
filename = title + filename;
console.log(filename);
const a = document.createElement('a');
a.href = url;
a.download = filename.endsWith('.pdf')? filename:filename + '.pdf';
a.click();
return true;
}
}
return false;
}
function checkStatus(){
setTimeout(function () {
let r = GM_getValue("g.r:task" + initID, 0);
if ( r != taskID ) window.close();
//学习任务页与控制页失联,有新的页面打开,关闭本任务页。
if ($('.study-errors-page').length>0){
$("[id$='goOnStudy'").click();
checkStatus();
return;
}
//有其他学习页打开造成学习暂停,继续学习。
let requires = $(".item.pointer.item22[style$='margin-right:12px']");
if(requires.length==0){
$.post(`${window.location.origin}/api/v1/course-study/course-front/score`,`score=10&businessId=${initID}&businessType=1`);
GM_deleteValue("g.r:task" + initID);
if($('.chapter-list-box.required.focus[data-sectiontype=1]').length==1 && GM_getValue("g.r:downPDF", false)) {
if (downPDF()){
setTimeout(()=> {
GM_sendMessage('remain.true.to.our.original.aspiration', initID,"finished with exam is "+ ($('.chapter-list-box.required[data-sectiontype=9]').length>0));
window.close();
},3000);
return;
}
}
GM_sendMessage('remain.true.to.our.original.aspiration', initID,"finished with exam is "+ ($('.chapter-list-box.required[data-sectiontype=9]').length>0));
window.close();
}else{
if(!document.title.endsWith('🟩')) document.title += '🟩';
if($('.chapter-list-box.required.focus').find('span').length==1) {
if($('.chapter-list-box.required.focus[data-sectiontype=1]').length==1 && GM_getValue("g.r:downPDF", false)) {
if (downPDF()){
setTimeout(()=> {
$(".item.pointer.item22[style$='margin-right:12px']")[0].click();
},3000);
checkStatus();
return;
}
}
requires[0].click();
}
GM_sendMessage('remain.true.to.our.original.aspiration', initID,"studying|" + Date.now() + "|" + $('div[style$="margin-right:12px"]').find('span').text().match(/ \d+/g).reduce((accumulator, current)=>accumulator + parseInt(current),1));
autoPlay();
checkStatus()
}
}, 11000);
}
function autoPlay() {
if($('video').length>0) {
if ($('video')[0].onplay === null) {
$('video')[0].onplay = function() {
$('video')[0].muted = true;
};
};
if ($('video')[0].onpause === null) {
$('video')[0].onpause = function() {
$('video')[0].play();
};
};
$('video')[0].muted = true;
$('video')[0].playbackRate = GM_getValue("g.r:speedup", false)? 1.5:1
if( $('video')[0].paused) {
$('video')[0].play();
}
}
}
function clearLostTasks() {
const keys = GM_listValues();
let nt = Date.now();
for(let k in keys) {
if (keys[k].startsWith('g.r:task')) {
let id = GM_getValue(keys[k], nt);
if((nt - id) > 43200000) GM_deleteValue(keys[k]);
}
}
}
//去除章节顺序限制
function removeLearnSequence() {
let modleId = Object.keys(unsafeWindow.app._modules).find(value => value.startsWith('study/course/detail--'));
unsafeWindow.app._modules[modleId].store.models.course.data.courseChapters.forEach(function(Chapter){
if(Chapter.learnSequence) Chapter.learnSequence = null;
});
}
if( !isCourse ) {
let r = sessionStorage.getItem(window.location.href.slice(-36));
if (r) {
let rhtml = JSON.parse(r);
$(rhtml).appendTo("body");
sessionStorage.removeItem(window.location.href.slice(-36));
} else {
GM_deleteValue('remain.true.to.our.original.aspiration');
clearLostTasks();
let notice='';
if(ver>GM_getValue("g.r:version", '')) {
notice = `<a href="https://gf.qytechs.cn/zh-CN/scripts/472634-%E7%94%B5%E4%BF%A1%E7%BD%91%E4%B8%8A%E5%A4%A7%E5%AD%A6%E8%B6%85%E7%BA%A7%E5%AD%A6%E4%B9%A0" target="_blank"><span style="color:red;">⚠️超级学习 ${ver} 版有重要更新说明,去查看➡️</span></a>`;
GM_setValue("g.r:version", ver);
}
$(`<div id="autostudydiv" class="study_box">
<a id="autostudy" class="cd-btn cd-btn-primary" >自动学习</a><br/>
<input type="checkbox" id="downpdf" ${GM_getValue("g.r:downPDF", false)?"checked":""}/>自动下载PDF课件<br/>
<input type="checkbox" id="speedup" ${GM_getValue("g.r:speedup", false)?"checked":""}/>使用1.5倍速播放视频<br/>
<a id="wsView" class="view_box">查看详细情况⯆</a>${notice}<div id="progress" class="progress_box"></div>
</div>`).appendTo("body");
//课程列表页,增加学习按钮
}
} else {
$(`<div id="autostudydiv" class="study_box"><a id="coursePDF" class="cd-btn cd-btn-primary" >下载PDF课件</a>
<div id="progress" class="progress_box"><span class="progress_item_title" id="filename"></span>
<span id="per" style="float:right;"></span></div></div>`).appendTo("body");
$("#coursePDF").click(async function() {
if($(this).text() != "下载PDF课件") return;
$(this).text('下载PDF课件中......');
const configs = unsafeWindow.app.global.fileCloudConfig.configs;
const modleId = Object.keys(unsafeWindow.app._modules).find(value => value.startsWith('study/course/detail--'));
const data = unsafeWindow.app._modules[modleId].store.models.course.data;
const resources = data.courseChapters.reduce((accumulator, current)=>accumulator.concat(current.courseChapterSections.filter(item=>item.required&item.sectionType==1).map(item=> {return {id:item.resourceId,name:item.name}})),[]);
//console.log(resources);
if(resources.length==0) {
alert('当前课程没有PDF课件');
$("#autostudydiv").remove();
return;
}
$('#progress').show();
async function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function getBlob(url) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
}
};
xhr.send();
});
}
const a = document.createElement('a');
const title = resources.length>1? data.name+'-':'';
let filename = '';
for (let i=0; i< resources.length; i++) {
let resource = resources[i];
$('#per').text((i+1) + '/' + resources.length);
$.get(`${window.location.origin}/api/v1/tools-center-v2/file-cloud/preview?id=${resource.id}&_=${Date.now()}`)
.done((data)=> {
if (data.configId != 'default') {
getBlob(`${configs[data.configId].viewUrl}${data.url}`)
.then(blob=> {
let reader = new FileReader();
reader.onloadend = function () {
let base64PDF = reader.result; // 获得转换后的字符串结果
a.href = 'data:application/octe-stream;base64,' + base64PDF;
a.download = title + resource.name + '.pdf';
$('#filename').text(resource.name);
a.click();
};
reader.readAsText(blob);
})
} else {
filename = data.filename;
$('#filename').text(filename);
a.href = `${configs[data.configId].viewUrl}${data.url}`;
a.download = filename.endsWith('.pdf')? title + filename:title + filename + '.pdf';
a.click();
}
})
await wait(3000);
}
alert('下载完成');
$("#autostudydiv").remove();
})
initID = GM_getValue("g.r:current", 0);
if(initID) GM_deleteValue("g.r:current");
let r = GM_getValue("g.r:task" + initID, 0);
if (r) {
taskID = r;
} else {
console.log('非自动学习打开的页面不处理。');
return;
}
let waitApp = setInterval(function() {
if( window.location.href.match('error-page')) {
//资源不存在
GM_sendMessage('remain.true.to.our.original.aspiration', initID,"finished with error");
window.close();
};
try {
removeLearnSequence();
}
catch {
return;
}
clearInterval(waitApp);
let requires = $(".item.pointer.item22[style$='margin-right:12px']");
if(requires.length>0){
if($('.focus').find('div[style$="margin-right:12px"]').length == 0) {
requires[0].click();
}
}
document.title += '🟩';
checkStatus();
},1000);
let c = 0;
let waitPlay = setInterval(function() {
try {
//console.log('try mute')
if (!$('video')[0].muted) {
autoPlay();
clearInterval(waitPlay);
}
}
catch {
//console.log('no muted');
c++;
//console.log(c);
if(c>4) clearInterval(waitPlay);
return;
}
},1000);
}
$("#wsView").click(function() {
if($('#progress').is(':hidden')){
$("#progress").show();
$("#wsView").text('查看详细情况▲');
}else{
$("#progress").hide();
$("#wsView").text('查看详细情况▼');
}
});
$("#downpdf").click(function() {
GM_setValue("g.r:downPDF", $(this).prop('checked'));
});
$("#speedup").click(function() {
GM_setValue("g.r:speedup", $(this).prop('checked'));
});
function GM_onMessage(label, callback) {
listenerId = GM_addValueChangeListener(label, function() {
callback.apply(undefined, arguments[2]);
});
}
function GM_sendMessage(label) {
GM_setValue(label, Array.from(arguments).slice(1));
}
function studyFinised() {
if(lessonsinfo == null) return;
let resultStr = '所有课程学习完成!'
let url = '';
if(isTrain) {
url = urls['course'];
} else {
url = urls['normal'];
}
let ul = '';
if (hasError.length>0) {
resultStr += '部分课程资源不存在!';
hasError.forEach(function(id){
ul += `<li><a href="${url.replace('$id$',id)}" target="_blank">${lessonsinfo[id]}</a></li>`
})
}
if (hasExam.length>0) {
resultStr += '注意!部分课程包含考试!';
hasExam.forEach(function(id){
ul += `<li><a href="${url.replace('$id$',id)}" target="_blank"><span style="color:red;">${lessonsinfo[id]}</span></a></li>`
})
}
$("#autostudy").text(resultStr)
lessonsinfo = null;
if(listenerId != null) GM_removeValueChangeListener(listenerId);
GM_deleteValue('remain.true.to.our.original.aspiration');
if(resultStr == '所有课程学习完成!') {
$("#wsView").remove();
$('#progress').remove();
if(Math.random()>0.9) {
$("#autostudydiv").append('<a href="https://gf.qytechs.cn/zh-CN/scripts/472634-%E7%94%B5%E4%BF%A1%E7%BD%91%E4%B8%8A%E5%A4%A7%E5%AD%A6%E8%B6%85%E7%BA%A7%E5%AD%A6%E4%B9%A0" target="_blank"><span style="color:red;">的确好用,给作者打赏。</span></a>')
}
} else {
$('#progress').append(`<ul>${ul}</ul>`);
$("#wsView").show();
$('#progress').show();
}
sessionStorage.setItem(window.location.href.slice(-36),JSON.stringify($('#autostudydiv').prop("outerHTML")))
location.reload();
}
function return2Contral() {
window.focus();
if($('#progress').is(':hidden')){
$("#wsView").click();
}
}
function doStudy() {
if(lessons.length == 0) {
if(count == 0) studyFinised();
return2Contral()
return;
}
if(listenerId === null) {
GM_onMessage('remain.true.to.our.original.aspiration', function(src, message) {
//console.log((new Date()).toLocaleString() + ' ' + src + ' : ' + message);
if( !isCourse && (src in studying)){
if(message.startsWith('studying')) {
//let per = message.replace('studying ','');
let per = '还需学 ' + message.split("|")[2] + ' 分钟';
if(per) $('#p'+ src).text(per)
studying[src] = true;
return;
}
if( message.endsWith("true")) hasExam.push(src)
if( message.endsWith("error")) hasError.push(src)
count--;
$('#' + src).remove();
delete studying[src]
if(lessons.length == 0) {
if (count == 0) studyFinised();
} else {
doStudy();
$("#autostudy").text('自动学习中,学习中课程:' + count + ',待学习课程:' + lessons.length + '。')
}
}
})
}
$("#wsView").show();
if (count < max) {
let url = ''
if(isTrain) {
url = urls['course']
} else {
url = urls['normal']
}
let id = lessons.shift()
url = url.replace('$id$',id)
studying[id] = true;
$('#progress').append(`<div id="${id}" class="progress_item"><span class="progress_item_title">${lessonsinfo[id]}</span><span id="p${id}" style="float:right;">待上报</span></div>`)
count++;
$("#autostudy").text(`自动学习中,学习中课程:${count},待学习课程:${lessons.length}。`)
//console.log(id)
GM_setValue("g.r:current", id);
GM_setValue("g.r:task" + id, Date.now());
GM_openInTab(url,{ active: true, insert: true, setParent :true });
setTimeout(function(){
doStudy();
},9000);
} else {
return2Contral()
}
}
function getlessons(callback) {
if (sections.length==0)
{
doStudy();
return;
}
currentSection = sections.shift();
let tk = $('#' + currentSection);
console.log('当前学习板块: ' + currentSection)
if(tk.attr('title') != '收起') {
tk.click();
}
setTimeout(function(){
$('div.train-citem:contains(未完成)').each(function() {
$(this).find('.m-bottom.row-title-a.pointer').each(function() {
if(!$(this).attr('title').includes('考试')) {
let id = $(this).attr('id').slice(-36);
console.log('待学习课程id: ' + id);
lessons.push(id);
lessonsinfo[id] = $(this).attr("title");
}
})
})
getlessons(callback);
},2000);
}
function heartbeat() {
if(count == 0) return;
setTimeout(function(){
console.log('检查心跳')
let needRestart = false;
for(var key in studying) {
if(!studying[key]) {
taskID++;
console.log(key + ' 异常停止,重启')
needRestart = true;
lessons.unshift(key);
delete studying[key];
$('#' + key).remove();
count--;
}
studying[key] = false;
}
if(needRestart) doStudy()
if( taskID > max) {
$("#wsView").after('任务页失联次数过多!请参考<a href="https://gf.qytechs.cn/zh-CN/scripts/472634-%E7%94%B5%E4%BF%A1%E7%BD%91%E4%B8%8A%E5%A4%A7%E5%AD%A6%E8%B6%85%E7%BA%A7%E5%AD%A6%E4%B9%A0" target="_blank"><span style="color:red;">浏览器问题</span></a>检测是否关闭浏览器节能功能。');
} else {
heartbeat();
}
},59000)
}
$("#autostudy").click(function () {
if($(this).text() != "自动学习") return;
//if (('WebSocket' in unsafeWindow) && (max > 1)) {
// $(this).text('❌未检测到多开支持!');
// $("#autostudydiv").append('请先安装并启用 <a href="https://gf.qytechs.cn/zh-CN/scripts/472577-%E7%94%B5%E4%BF%A1%E7%BD%91%E4%B8%8A%E5%A4%A7%E5%AD%A6%E8%B6%85%E7%BA%A7%E5%AD%A6%E4%B9%A0%E5%A4%9A%E5%BC%80%E6%94%AF%E6%8C%81" target="_blank"><span style="color:red;">电信网上大学超级学习多开支持</span></a>');
// return;
//}
//$("#downpdf").attr("disabled", true);;
document.title += '🟥';
$(this).text('获取课程中');
console.log('开始');
if( lessons.length > 0) return;
$('.pointer.iconfont[id][title]').each(function() {
sections.push($(this).attr('id'))
})
if(sections.length == 0) {
console.log('普通课程');
$("div.item.current-hover").each(function(index,element){
//if(($("#downpdf").prop('checked') || $(this).find("i.icon-reload").length==0) && $(this)[0].firstElementChild.querySelector("span.section-type").innerText == "课程"){
if($(this).find("i.icon-reload").length==0 && $(this)[0].firstElementChild.querySelector("span.section-type").innerText == "课程"){
let id = $(this).attr("data-resource-id").slice(-36);
lessons.push(id);
lessonsinfo[id] = $(this).find('.inline-block.name-des.default-skin.text-overflow').attr("title");
//console.log(lessonsinfo);
}
})
doStudy();
} else {
console.log('培训班');
isTrain = true;
getlessons(doStudy);
}
heartbeat()
});
})();