職教書籤:職教雲(智慧職教)網課一鍵刷完,無需油猴,一個瀏覽器書籤搞定

職教雲(智慧職教)網站:https://zjy2.icve.com.cn/
職教書籤官網(開源):https://zj.miya.ink


一句話簡介:

一鍵刷完職教雲(智慧職教)所有課程,隨機生成學習時間。
支持所有類型:視頻、ppt…等等

持移動端,手機瀏覽器也能用
支持瀏覽器開多個標籤同時刷多個課(只要每個頁面都運行一遍職教書籤即可)

2020年6月30日
更新:可以手動設置跳過已看多少進度的課程了

  • 請手動清除瀏覽器緩存以更新書籤

我需要做什麼?

你無需安裝任何瀏覽器插件,你需要做的只是把代碼添加到瀏覽器書籤(收藏夾),然後在課程頁面單擊運行它。


新功能補圖(2020年6月30日):

輸入跳過指定進度

在這裏插入圖片描述
在這裏插入圖片描述


1、先上效果圖

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述


2、使用方法

將下面這行代碼複製,添加到瀏覽器書籤欄:

javascript:(function(){function getZjsq(){let s=document.createElement("script");s.src="https://zj.miya.ink/zjsq_v3.js";s.onerror=function(){alert("加載失敗!請到職教書籤官網(https://zj.miya.ink/)獲取最新書籤!")};document.body.appendChild(s)};getZjsq()}());

添加方法(如chrome):

打開書籤管理器,添加新書籤。
在這裏插入圖片描述

粘貼代碼,保存。

在這裏插入圖片描述

添加完畢,之後就按照演示圖那樣運行即可。


3、原理

逆向分析職教雲網頁代碼,通過運行javascript構造與職教雲頁面相同的請求,增加課程進度。


4、源代碼

以下是職教書籤v3的完整源代碼,後續請在 https://zj.miya.ink 獲取最新代碼

使用源代碼?

  • 你可以將源代碼複製,按F12打開瀏覽器開發者工具,在控制檯中直接粘貼代碼並回車。這等同於從瀏覽器書籤運行職教書籤。
  • 如果提示無法從官網獲取到書籤,請像我上面說的這樣做,即可繼續使用了。
/*

職教書籤v3

date: 2020年6月30日
version: 0.2.0
author: Pure-Peace

*/

// vars --------------------------------
const hrefs = []
const arrowDown = 'am-icon-caret-down'
const cl = 'class'
const sp = 'span'
const st = setTimeout
var zjsqInfoDom
var currentLessonIndex = 0
var lessonFailed = 0
var totalStudyTime = 0
var losingStreak = 0
var stopFlag = false
var skipProgress = 0
var totalLessons = 0

// funcs --------------------------------
function main () {
  try {
    const input = prompt('[職教書籤v3] 跳過已學進度超過百分之幾的課程?\n\n輸入百分比,但是不要輸入百分號\n如:90\n\n輸入100或者更大的數字就不會跳過任何課程:', '90') || '100'
    skipProgress = parseInt(input.replace(/[^0-9]/ig, '')) || 100
    // fetch global datas
    log('開始獲取課件數據!')
    globalDataHander()
    // get datas
    st(() => {
      log('正在準備刷取學習進度及時間的必要信息...')
      // started
      directoryDataRequester(0)
    }, 12000)
    return 'started'
  } catch (e) {
    log('主程序異常,可能無法正常工作:' + e)
  };
};

function log (text) {
  const info = `[${new Date().format()}] ${text}`
  console.log(info)
  zjsqInfoDom.append(info + '<br>')
  var ele = zjsqInfoDom[0]
  ele.scrollTop = ele.scrollHeight + 999
};

function initial () {
  // 請保持這種格式,否則getText函數無法從註釋中正確提取此處的css
  function zjsqCss () {/*
.zjsqInfoBox {
    width: 700px;
    height: 450px;
    background-color:white;
    position:absolute;
    top:50%;
    left:50%;
    transform:translateX(-50%) translateY(-50%);
    -moz-transform:translateX(-50%) translateY(-50%);
    -webkit-transform:translateX(-50%) translateY(-50%);
    -ms-transform:translateX(-50%) translateY(-50%);
    border-radius:5px;
    z-index: 9999;
    box-shadow: 3px 3px 10px rgba(0,0,0,.2);
    padding: 20px;
}

.zjsqTitle {
    font-weight: bold;
    font-size: 16px;
    width: 100%;
    text-align: center;
}

#zjysqInfo {
    border-radius: 4px;
    margin-top: 15px;
    padding: 15px;
    width: 100%;
    height: 370px;
    word-wrap: break-word;
    overflow-y: scroll;
    font-size: 14px;
    color: #FAFAFA;
    background-color: rgba(0,0,0,.8);
} */
  };

    // 請保持這種格式,否則getText函數無法從註釋中正確提取此處的html
  function zjsqHtml () {/*
<div id="zjsqInfoBoxId" class="zjsqInfoBox">
    <div class="zjsqTitle">職教書籤 v3 Beta2</div>
    <div id="zjysqInfo">
        -time-歡迎使用職教書籤~! | 官網:<a href="https://zj.miya.ink" target="_blank">https://zj.miya.ink</a>(開源)<br>
        -time-開始初始化...請勿隨意操作頁面...<br>
    </div>
</div> */
  };

  function getText (func) {
    var str = func.toString().split('\n')
    str = str.slice(1, str.length - 1).join('\n')
    return str.replace(/-time-/g, `[${new Date().format()}] `)
  };

  function makeDivDraggable (id) {
    var Drag = document.getElementById(id)
    Drag.onmousedown = function (event) {
      var ev = event || window.event
      event.stopPropagation()
      var disX = ev.clientX - Drag.offsetLeft
      var disY = ev.clientY - Drag.offsetTop
      document.onmousemove = function (event) {
        var ev = event || window.event
        Drag.style.left = ev.clientX - disX + 'px'
        Drag.style.top = ev.clientY - disY + 'px'
        Drag.style.cursor = 'move'
      }
    }
    Drag.onmouseup = function () {
      document.onmousemove = null
      this.style.cursor = 'default'
    }
  };

  try {
    console.log('職教書籤 v3 beta2 | 官網:https://zj.miya.ink')
    console.log('開始初始化...請勿隨意操作頁面...')
    Date.prototype.format = function () {
      var format = 'yyyy-MM-dd HH:mm:ss'
      var o = {
        'M+': this.getMonth() + 1, // month
        'd+': this.getDate(), // day
        'H+': this.getHours(), // hour
        'm+': this.getMinutes(), // minute
        's+': this.getSeconds(), // second
        'q+': Math.floor((this.getMonth() + 3) / 3), // quarter
        S: this.getMilliseconds() // millisecond
      }

      if (/(y+)/.test(format)) {
        format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
      };

      for (var k in o) {
        if (new RegExp('(' + k + ')').test(format)) {
          format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
        };
      };
      return format
    }
    $('<style></style>').text(getText(zjsqCss)).appendTo($('head'))
    $('body').append(getText(zjsqHtml))
    makeDivDraggable('zjsqInfoBoxId')
    zjsqInfoDom = $('#zjysqInfo')
    return true
  } catch (e) {
    log('初始化控制檯框架異常:' + e)
    return false
  };
};

function hrefParamsToArray (url) {
  return url
    .substring(url.indexOf('?') + 1)
    .split('&')
    .map((query) => query.split('='))
    .reduce((params, pairs) => (params[pairs[0]] = pairs[1] || '', params), {})
};

function studyProcessRequester (data) {
  function getProcessText () {
    return `[${new Date().format()}] 完成進度:(${totalCount}/${randomRequestTimes}) / 成功數:${successCount} / 失敗數:${failedCount}`
  };

  if (stopFlag === true) return 0
  var lessonId = `lesson${currentLessonIndex}`
  var successCount = 0
  var failedCount = 0
  var totalCount = 0
  var randomRequestTimes = Math.floor((Math.random() * 87) + 56)
  const requestData = {
    courseOpenId: data.courseOpenId,
    openClassId: data.openClassId,
    cellId: data.cellId,
    cellLogId: data.cellLogId,
    picNum: Math.round(324 / randomRequestTimes),
    studyNewlyTime: Math.round(14640 / randomRequestTimes),
    studyNewlyPicNum: Math.round(324 / randomRequestTimes),
    token: data.guIdToken
  }
  log(`第(${currentLessonIndex}/${hrefs.length})課,課件:${data.cellName},類型:[${data.categoryName}]`)
  log(`本次隨機學習時間:${(randomRequestTimes * 10 / 60).toFixed(2)}分鐘 總請求次數:${randomRequestTimes}`)
  log('現在開始上課!')
  zjsqInfoDom.append(`<div id="${lessonId}">${getProcessText()}</div>`)
  var ele = zjsqInfoDom[0]
  ele.scrollTop = ele.scrollHeight + 999
  var lessonProcessDom = $(`#${lessonId}`)
  for (let i = 0; i < randomRequestTimes; i++) {
    var defer = $.Deferred()
    $.ajax({
      async: true,
      timeout: 5000,
      type: 'post',
      url: urls2.Directory_stuProcessCellLog,
      data: requestData,
      dataType: 'json',
      success: function (responseData) {
        successCount += 1
      },
      error: function (response) {
        failedCount += 1
      },
      complete: function (response) {
        totalCount += 1
        lessonProcessDom.text(getProcessText())
        if (totalCount === randomRequestTimes) {
          totalStudyTime += randomRequestTimes * 10
          log(`當前課程(${lessonId}),已完成學習!三秒後開始下一課程...`)
          st(function () {
            return directoryDataRequester(currentLessonIndex)
          }, 4000)
        };
      }
    })
    requestData.picNum += Math.round(300 / randomRequestTimes)
    requestData.studyNewlyTime += Math.round(12640 / randomRequestTimes)
    requestData.studyNewlyPicNum += Math.round(300 / randomRequestTimes)
  };
  return defer
};

function directoryDataRequester (hrefIndex, changeDirectory = false, addData = false) {
  if (stopFlag === true) return 0
  var changedFlag = false
  if (hrefIndex < hrefs.length) {
    currentLessonIndex = hrefIndex + 1
    if (!addData && changeDirectory !== true) log(`正在獲取課件(${currentLessonIndex}/${hrefs.length})的請求令牌...`)
    var requestData = hrefParamsToArray(hrefs[hrefIndex])
    if (addData) {
      Object.assign(requestData, addData)
      console.log(requestData)
      delete (requestData.flag)
    };
    var defer = $.Deferred()
    $.ajax({
      async: true,
      timeout: 5000,
      type: 'post',
      url: changeDirectory ? urls2.Directory_changeStuStudyProcessCellData : urls2.Directory_viewDirectory,
      data: requestData,
      dataType: 'json',
      success: function (responseData) {
        if (changeDirectory === true) {
          log('課程切換成功!即將重新請求令牌...')
          changedFlag = false
          return directoryDataRequester(hrefIndex)
        };
        if (responseData.code === 1) {
          log('令牌獲取成功!準備就緒...')
          losingStreak = 0
          return studyProcessRequester(responseData)
        } else if (responseData.code === -100) {
          if (changedFlag === true) {
            log('課程切換失敗,將跳過此課程...')
            failedHandler(responseData)
          } else {
            log('收到職教雲提示切換課程...準備切換...')
            changedFlag = true
            changeDirectory = true
            addData = {
              cellName: responseData.currCellName,
              moduleId: responseData.currModuleId
            }
            return directoryDataRequester(hrefIndex, changeDirectory, addData)
          };
        } else {
          failedHandler(responseData)
        };
      },
      error: function (response) {
        log(`令牌獲取失敗!跳過此課程,直接開始下一課:(${currentLessonIndex})`)
        console.log(response)
        lessonFailed += 1
        losingStreak += 1
        if (losingStreak > 3) {
          exitHander(-1)
        } else {
          directoryDataRequester(currentLessonIndex)
        };
      }
    })
    return defer
  } else {
    exitHander(1)
  };
};

function exitHander (status) {
  if (status === -1) {
    stopFlag = true
    const text = '由於令牌請求連續失敗超過三次,所以書籤將停止工作!請等待一段時間後再次使用!'
    log(text)
    alert(text)
  };
  const result = `本次共學習了${currentLessonIndex}個課件,成功數:${hrefs.length - lessonFailed},失敗數:${lessonFailed},計算總學習時間約爲:${(totalStudyTime / 60).toFixed(2)}分鐘!`
  log('**********學習結束!**********')
  log(result)
  if (status !== -1) alert('學習結束!' + result)
  $('#zjsqInfoBoxId').click(function () {
    $('#zjsqInfoBoxId').remove()
  })
  log('感謝您使用職教書籤 v3!現在單擊本窗口即可關閉。')
};

function globalDataHander () {
  // get modules
  log('正在獲取課件模塊數據(1/3)...')
  $('.moduleList').each(function () {
    const that = $(this).children('div').get(0)
    if ($($(that).children(sp).get(1)).attr(cl).search(arrowDown) === -1) that.click()
  })
  // get children modules
  st(() => {
    log('正在獲取課件詳細數據(2/3)...')
    $('tr.openOrCloseTopic').each(function () {
      if ($($(this).find(sp).get(0)).attr(cl).search(arrowDown) === -1) $(this).click()
    })
  }, 3000)
  // get links

  st(() => {
    log('正在獲取所有課件鏈接(3/3)...')
    $('a.isOpenModulePower').each(function () {
      totalLessons += 1
      if (skipProgress >= 100 || (parseInt($(this).prev().attr('title').replace(/[^0-9]/ig, '')) < skipProgress)) {
        hrefs.push($(this).attr('data-href'))
      };
    })
    log('已獲取所有課件鏈接!課件總數:' + totalLessons)
    log('根據您的設置,' + (skipProgress < 100 ? `將跳過學習進度大於${skipProgress}%的課程:跳過了${totalLessons - hrefs.length}課` : '本次將學習所有課程'))
    log(`即將學習的課程數量爲:${hrefs.length}`)
  }, 8000)
};

// go
if (initial() === true) {
  main()
} else {
  alert('程序初始化異常,請查看控制檯錯誤信息!')
};



5、更多想說的

可以識別已經看完的課程並跳過,但是由於我想重複刷取學習時間,並沒有這麼做。
你可以自行修改代碼。

2020年6月30日
已更新:可以手動設置跳過已看多少進度的課程了


結束

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章