// ==UserScript==
// @name chinahrt全自动刷课
// @namespace https://github.com/jsgang/chinahrt
// @version 1.5
// @description chinahrt刷课,规则更改了,只能耗时间,可以把视频加入列表放置让自动切换。。
// @author jsgang yikuaibaiban https://www.jsgang.top
// @icon https://www.google.com/s2/favicons?sz=64&domain=jsgang.top
// @match http://*.chinahrt.com/*
// @match https://*.chinahrt.com/*
// @match http://videoadmin.chinahrt.com.cn/videoPlay/play*
// @match http://videoadmin.chinahrt.com/videoPlay/play*
// @match https://videoadmin.chinahrt.com.cn/videoPlay/play*
// @match https://videoadmin.chinahrt.com/videoPlay/play*
//
// @grant unsafeWindow
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addValueChangeListener
// @grant GM_notification
//
// @license GPL
// ==/UserScript==
/**
* 课程预览页面
* @type {number}
*/
const COURSE_PREVIEW = 0;
/**
* 课程播放页面
* @type {number}
*/
const COURSE_PALY = 1;
/**
* 视频播放页面
* @type {number}
*/
const VIDEO_PALY = 2;
/**
* VUE课程预览页面
* @type {number}
*/
const VUE_COURSE_PREVIEW = 3;
/**
* 未知页面
* @type {number}
*/
const UNKOWN_PAGE = 9999;
/**
* 课程存储关键字
* @type {string}
*/
const COURSES = "courses";
/**
* 自动播放
* @type {string}
*/
const AUTOPLAY = "autoPlay";
/**
* 静音
* @type {string}
*/
const MUTE = "mute";
/**
* 拖动
* @type {string}
*/
const DRAG = "drag";
/**
* 播放速度
* @type {string}
*/
const SPEED = "speed";
/**
* 播放模式
* @type {string}
*/
const PLAY_MODE = "play_mode";
/**
* 重复次数
* @type {string}
*/
const REAPT_NUM = "reapt_num";
/**
* 自刷切换
* @type {string}
*/
const REAPT_MODE = "reapt_mode";
/**
* 获取自动播放
* @returns {*}
*/
function getAutoPlay() {
return GM_getValue(AUTOPLAY, true);
}
/**
* 获取静音
* @returns {*}
*/
function getMute() {
return GM_getValue(MUTE, true);
}
/**
* 获取拖动
* @returns {*}
*/
function getDrag() {
return GM_getValue(DRAG, 5);
}
/**
* 获取播放速度
* @returns {*}
*/
function getSpeed() {
return GM_getValue(SPEED, 1);
}
/**
* 获取播放列表
* @returns {*|*[]}
*/
function getCourses() {
var value = GM_getValue(COURSES, []);
if (Array.isArray(value)) {
return value;
}
return [];
}
/**
* 添加到播放列表
* @param element
* @returns {boolean}
*/
function addCourse(element) {
if (!element.title || !element.url) {
console.error(element);
alert("添加失败,缺少必要参数");
return false;
}
var oldValue = getCourses();
if (oldValue.findIndex(value => value.url == element.url) > -1) {
alert("已经存在播放列表中");
return false;
}
oldValue.push({title: element.title, url: element.url});
GM_setValue(COURSES, oldValue);
return true;
}
/**
* 从播放列表移除
* @param index
*/
function removeCourse(index) {
var courses = getCourses();
if (Number.isNaN(index)) {
for (let i = courses.length; i >= 0; i--) {
const element = courses[i];
// 正则提取 href 中 sectionId courseId trainplanId
var jsonHref = element.url;
var jsonSectionId = jsonHref.match(/sectionId=([^&]*)/)[1];
var jsonCourseId = jsonHref.match(/courseId=([^&]*)/)[1];
var jsonTrainplanId = jsonHref.match(/trainplanId=([^&]*)/)[1];
// 正则提取 window.location.href 中 sectionId courseId trainplanId
var href = window.location.href;
var sectionId = href.match(/sectionId=([^&]*)/)[1];
var courseId = href.match(/courseId=([^&]*)/)[1];
var trainplanId = href.match(/trainplanId=([^&]*)/)[1];
if (jsonCourseId == courseId && jsonSectionId == sectionId && jsonTrainplanId == trainplanId) {
courses.splice(i, 1);
}
}
} else {
courses.splice(index, 1);
}
GM_setValue(COURSES, courses);
}
/**
* 生成可以添加到播放列表的容器
* @returns {*|jQuery|HTMLElement}
*/
function createCanPlayList() {
// 生成容器
var playListBox = $("<div>", {
id: "canPlayBox",
css: {
width: "300px",
height: "500px",
position: "fixed",
top: "100px",
background: "rgba(255,255,255,1)",
right: "20px",
border: "1px solid #c1c1c1"
}
});
var status = $("<div></div>", {
text: "获取中...",
css: {
height: "30px",
"border-bottom": "1px solid",
"text-align": "center",
"line-height": "30px",
"color": "#4bccf2",
"font-weight": "bold"
},
click: function () {
playListBox.trigger("clear");
if (getPageNumber() == VUE_COURSE_PREVIEW) {
vue_findCourses(playListBox);
} else {
findCourses(playListBox);
}
}
});
status.appendTo(playListBox);
var listBox = $("<div></div>", {
css: {
height: "470px",
"overflow-y": "auto"
}
}).appendTo(playListBox);
playListBox.on("clear", function () {
status.text("获取中...");
listBox.empty();
});
// 添加绑定事件
playListBox.on("bind", function (e, data) {
if (status.text() == "获取中...") {
status.text("点击刷新");
}
var box = $("<div>", {
css: {
"border-bottom": "1px solid #c1c1c1",
"padding": "8px",
"line-height": "150%",
"border-bottom": "1px solid #c1c1c1",
"margin-bottom": "3px"
}
});
var ptitle = $("<p>", {
text: data.title,
title: data.title,
css: {
"font-size": "13px",
"white-space": "nowrap",
"overflow": "hidden",
"text-overflow": "ellipsis",
}
});
ptitle.appendTo(box);
var pstatus = $("<p>", {
text: "学习状态: " + data.status,
title: "学习状态: " + data.status,
css: {
"font-size": "12px",
"white-space": "nowrap",
"overflow": "hidden",
"text-overflow": "ellipsis",
"color": "#c1c1c1"
}
});
pstatus.appendTo(box);
var disabled = getCourses().findIndex(value => value.url == data.url) > -1;
var button = $("<button>", {
text: disabled ? "已在列表中" : "添加到播放列表",
type: "button",
disabled: disabled,
css: {
color: disabled ? "#000" : "#FFF",
backgroundColor: disabled ? "#c3c3c3" : "#4bccf2",
border: "none",
padding: "5px 10px",
"margin-top": "4px"
},
click: function () {
if (addCourse({title: data.title, url: data.url})) {
$(this).attr("disabled", true);
$(this).css({
color: "#000",
backgroundColor: "#c3c3c3",
}).text("已在列表中");
}
}
});
button.appendTo(box);
box.appendTo(listBox);
});
playListBox.appendTo('body');
return playListBox;
}
/**
* 显示通知
* @param content
*/
function showNotification(content) {
GM_notification({
text: content,
title: "Chinahrt自动刷课",
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAArFJREFUWEftlttPE0EUxr+9ddtdYOXSSmmlFFoasSFemmg0qYkIMfGF/9J/wTeN0cSYGBHEBBIoLSIU0VrKpe1eagaySWVmutu+GJPO4+5cfvOdc745wkGl3sI/HEIf4L9ToHrqoHLi4LTRgmm1IAhAMCDgmi4ibEgQhe4SyncOFA9tFMoWaucO9wRJFDAZlpCeUKAq/kA8AY7PHKwWmqic+i8WRRaQTciIj8qeFB0Bjo4dvN9ooOX/7L8OnLshYybaWQouALn5m/XeD3dJ5qcCSEQkrhJcgLfrdabshV8mvtdsPEoEPeV1JzzOqhjSROZ8JsBO2cJa0WQueLFSQ6lqYWpYQT4ZQnSQfzt3g+iwhFw64B/g9VqDm+0uANlNkYB8UkMupnqqkc+qMBgqUAqQOiex5412AHdOJhzAUlqDpvBNIBOXMTtBJyQF0El+ciALgHwfUEUspjTMjrGzPmKIuJ+hlaIA1ksmtg+srhRon7wwE0IuTieoHhTwZJ7+TgGsbDexe2T3DJAdV/E8o1HriTMu3QlR3ymAz4UmSj96A1AkActzOqZH6DD4BtjYM7G5130IYkMynmV0jHHqfXRQxMObPnKg/NvGh81mVyG4HQ1gcVYH22out5oel3Fr0kcVOC3g5cdz2JxH72oVPE1ruDfh7QMPMirCBo3IdMIvRfPi6WUNF2BMl0Aynjii1yAGRIyINZgADRN4tVa/aDiujnfFOg5PbCykNAyp/roPYsPEjn0DkInfflr4tMV+D7xu3P4/eV1GNsFXqWM/sLVv4usuvyK8QGIjEu6m2I+Qu9azIyKt2OoOvyp4EF439w1AJpLmhHjDfoVvUO6GJOHSMZkb86vAngq0L6ieOSA+UalddsWWfZmkA0ERhi4iYkjMUusUqq4AvGLey/8+QF+BP0npcPDdfTv7AAAAAElFTkSuQmCC",
});
}
/**
* 创建配置窗口
*/
function createConfigBox() {
var box = $("<div>", {
css: {
position: "fixed",
right: 0,
top: 0,
width: "250px",
height: "240px",
"background-color": "#FFF",
"z-index": 9999,
border: "1px solid #ccc"
}
});
$("<div>", {
text: "视频控制配置",
css: {
"border-bottom": "1px solid #ccc",
padding: "5px",
"font-weight": "bold"
}
}).appendTo(box);
var configBox = $("<div>", {
css: {
padding: "5px",
"padding-bottom": "5px",
"font-size": "12px",
"line-height": "150%"
}
});
// 自动播放
var autoPlayBox = $("<div>", {css: {"border-bottom": "1px dotted #ccc", "padding-bottom": "5px"}});
$("<p>", {text: "是否自动播放:"}).appendTo(autoPlayBox);
$("<input>", {
type: "radio", name: "autoPlay", value: true, checked: getAutoPlay(), click: function () {
GM_setValue(AUTOPLAY, true);
}
}).appendTo(autoPlayBox);
$("<label>", {text: "是"}).appendTo(autoPlayBox);
$("<input>", {
type: "radio", name: "autoPlay", value: false, checked: !getAutoPlay(), click: function () {
GM_setValue(AUTOPLAY, false);
}
}).appendTo(autoPlayBox);
$("<label>", {text: "否"}).appendTo(autoPlayBox);
autoPlayBox.appendTo(configBox);
// 是否静音
var mutePlayBox = $("<div>", {css: {"border-bottom": "1px dotted #ccc", "padding-bottom": "5px"}});
$("<p>", {text: "是否静音:"}).appendTo(mutePlayBox);
$("<input>", {
type: "radio", name: "mute", value: true, checked: getMute(), click: function () {
GM_setValue(MUTE, true);
}
}).appendTo(mutePlayBox);
$("<label>", {text: "是"}).appendTo(mutePlayBox);
$("<input>", {
type: "radio", name: "mute", value: false, checked: !getMute(), click: function () {
GM_setValue(MUTE, false);
}
}).appendTo(mutePlayBox);
$("<label>", {text: "否"}).appendTo(mutePlayBox);
$("<p>", {
text: "注意:不静音,视频可能会出现不会自动播放",
css: {"font-size": "13px", "font-weight": "bold"}
}).appendTo(mutePlayBox);
mutePlayBox.appendTo(configBox);
// 启用拖放
var dragPlayBox = $("<div>", {css: {"border-bottom": "1px dotted #ccc", "padding-bottom": "5px"}});
$("<p>", {text: "启用拖放(慎用):"}).appendTo(dragPlayBox);
$("<input>", {
type: "radio", name: "drag", value: 5, checked: getDrag() == 5, click: function () {
GM_setValue(DRAG, 5);
}
}).appendTo(dragPlayBox);
$("<label>", {text: "还原"}).appendTo(dragPlayBox);
$("<input>", {
type: "radio", name: "drag", value: 1, checked: getDrag() == 1, click: function () {
GM_setValue(DRAG, 1);
}
}).appendTo(dragPlayBox);
$("<label>", {text: "启用"}).appendTo(dragPlayBox);
dragPlayBox.appendTo(configBox);
var playModeBox = $("<div>", {css: {"border-bottom": "1px dotted #ccc", "padding-bottom": "5px"}});
playModeBox.appendTo(configBox);
configBox.appendTo(box);
box.appendTo("body");
}
/**
* 创建播放列表窗口
*/
function createPlayListBox() {
var box = $("<div>", {
id: "playListBox",
css: {
position: "fixed",
right: 0,
top: 250,
width: "250px",
height: "450px",
"background-color": "#FFF",
"z-index": 9999,
border: "1px solid #ccc",
"overflow-y": "auto"
}
});
$("<div>", {
text: "视频列表",
css: {
"border-bottom": "1px solid #ccc",
padding: "5px",
"font-weight": "bold"
}
}).appendTo(box);
// 渲染课程列表
var courses = getCourses();
for (let index = 0; index < courses.length; index++) {
const element = courses[index];
var ptitle = $("<p>", {
text: element.title,
title: element.title,
css: {
"font-size": "13px",
"white-space": "nowrap",
"overflow": "hidden",
"text-overflow": "ellipsis",
}
});
ptitle.appendTo(box);
var button = $("<button>", {
text: "移除",
type: "button",
data: {index: index},
css: {
color: "#FFF",
backgroundColor: "#fd1952",
border: "none",
padding: "5px 10px",
"margin": "4px 0 10px 0",
},
click: function () {
if (confirm("确定删除这个视频么?")) {
removeCourse($(this).data("index"));
}
}
});
button.appendTo(box);
}
box.appendTo("body");
}
/**
* 获取当前页面编号
* @returns {number}
*/
function getPageNumber() {
var href = window.location.href;
// 以下是Vue版的请求地址
if (href.indexOf("/index.html#/v_courseDetails") > -1) {
return VUE_COURSE_PREVIEW;
}
// 默认课程详情地址
if (href.indexOf("/course/preview") > -1) {
return COURSE_PREVIEW;
}
if (href.indexOf("/course/play_video") > -1) {
return COURSE_PALY;
}
if (href.indexOf("/videoPlay/play") > -1) {
return VIDEO_PALY;
}
return UNKOWN_PAGE;
}
/**
* 获取课程信息
* @param playListBox
*/
function findCourses(playListBox) {
// 提取所有链接
var allLinks = document.querySelectorAll("a");
// 提取所有可以播放的数据
for (let i = 0; i < allLinks.length; i++) {
const element = allLinks[i];
if (element.href.indexOf("/course/play_video") > -1) {
playListBox.trigger("bind", {
title: element.innerText,
url: element.href,
status: $(element).prev().text()
});
}
}
}
/**
* VUE版本获取课程信息
* @param playListBox
*/
function vue_findCourses(playListBox) {
// 获取data
var data = document.querySelector("article")?.__vue__?._data;
if (!data) {
return;
}
// 获取页面信息
var pageData = data?.pageData;
if (!pageData) {
return;
}
// 获取所有章节信息
var chapters = pageData?.course?.chapter_list;
// 循环获取章节信息
if (chapters && chapters.length > 0) {
for (let i = 0; i < chapters.length; i++) {
const chapter = chapters[i];
// 循环分段(课时)
var sections = chapter?.section_list;
if (sections && sections.length > 0) {
for (let j = 0; j < sections.length; j++) {
const section = sections[j];
// 拼接课时网址
var url = window.location.protocol + "//" + window.location.host + window.location.pathname + "#/v_video?platformId=" + data.platformId + "&trainplanId=" + data.trainplanId + "&courseId=" + data.courseId + "§ionId=" + section.id;
playListBox.trigger("bind", {
title: section.name,
url: url,
status: section.study_status + "( " + section.studyTimeHHmmss + " )"
});
}
}
}
}
}
window.onload = function () {
try {
// 检测到是Vue
if (Vue) {
console.log("当前是Vue模式");
// 创建播放列表
var playListBox;
// 循环检测
setInterval(() => {
if (getPageNumber() == VUE_COURSE_PREVIEW) {
if (!playListBox) {
playListBox = createCanPlayList();
setTimeout(function () {
vue_findCourses(playListBox);
}, 1000);
}
} else {
if (playListBox) {
playListBox.remove();
playListBox = undefined;
}
}
}, 1000);
}
} catch (error) {
console.log("当前不是Vue模式");
}
if (getPageNumber() == COURSE_PREVIEW) {
$(document).ready(function () {
// 创建播放列表
var playListBox = createCanPlayList();
findCourses(playListBox);
});
}
if (getPageNumber() == VIDEO_PALY) {
$(document).ready(function () {
// 增加提示信息
$('<div></div>', {
html: "点课程详情页中的【添加到播放列表】按钮添加需要学习的课程。受到浏览器策略影响第一次可能无法自动播放,请手动点击播放。视频播放标签放置最前面",
css: {
"font-size": "14px",
"font-weight": "bold",
color: "red",
background: "#FFF",
position: "absolute",
"line-height": "30px",
"z-index": "99999",
"left": "30px",
bottom: "10px"
}
}).prependTo("#body");
$("video").prop("muted", "muted");
// 移除讨厌的事件
removePauseBlur();
// 视频播放初始化
function run() {
// 总是显示播放进度
player.changeControlBarShow(true);
// 拖动开关
player.changeConfig('config', 'timeScheduleAdjust', getDrag());
// 静音
if (getMute()) {
player.videoMute();
} else {
+
player.videoEscMute();
}
// 播放速度
player.changePlaybackRate(getSpeed());
// 自动播放
if (getAutoPlay()) {
player.videoPlay();
}
}
// 视频总长度
var videoDuration = 0;
var videocurrentTime=0;
var startstaus=0;
var actisok=0;
var ctimes=0;
var rtimes=0;
var rtimess=0;
var jumpas=0;
var nonum=0;
var nplay=0;
var tmp = setInterval(function () {
if (player != undefined) {
player.addListener('loadedmetadata', run);
run();
clearInterval(tmp);
// 移除本课程学习完毕
attrset.proxyUrl = "";
// 播放结束
player.addListener('ended', function () {
removeCourse(window.location.href);
var courses = getCourses();
if (courses.length == 0) {
GM_setValue(REAPT_MODE, 0);
GM_setValue(REAPT_NUM, 0);
showNotification("所有视频已经播放完毕");
} else {
GM_setValue(REAPT_MODE, 0);
GM_setValue(REAPT_NUM, 0);
showNotification("即将播放下一个视频:" + courses[0].title);
window.top.location.href = courses[0].url;
}
});
player.addListener('time', function (t) {
videoDuration = parseInt(player.getMetaDate().duration);
videocurrentTime = parseInt(attrset.currentTime);
// console.log(attrset.currentTime+"/"+videoDuration);
});
}
}, 1000);
// 创建配置窗口
createConfigBox();
// 创建播放列表窗口
createPlayListBox();
// 检测播放列表
GM_addValueChangeListener(COURSES, function (name, oldValue, newValue, remote) {
console.log("检测播放列表变动");
$("#playListBox").remove();
createPlayListBox();
});
// 监测自动播放
GM_addValueChangeListener(AUTOPLAY, function (name, oldValue, newValue, remote) {
console.log("监测自动播放变动");
if (newValue) {
player.videoPlay();
}
});
// 检测静音
GM_addValueChangeListener(MUTE, function (name, oldValue, newValue, remote) {
console.log("检测静音变动");
if (newValue) {
player.videoMute();
} else {
player.videoEscMute();
}
});
// 检测拖动
GM_addValueChangeListener(DRAG, function (name, oldValue, newValue, remote) {
console.log("检测拖动变动");
player.changeConfig('config', 'timeScheduleAdjust', newValue);
});
// 检测速度
GM_addValueChangeListener(SPEED, function (name, oldValue, newValue, remote) {
console.log("检测速度变动");
player.changePlaybackRate(newValue);
});
});
}
}