慕课网笔记导出

导出慕课网的笔记,分为点赞和采集两种(本人编写的也会被导出)。导出的格式为json。导出过程可以暂停,下次在进行导出。

// ==UserScript==
// @name         慕课网笔记导出
// @namespace    https://github.com/yeomanye
// @version      0.2
// @description  导出慕课网的笔记,分为点赞和采集两种(本人编写的也会被导出)。导出的格式为json。导出过程可以暂停,下次在进行导出。
// @author       Ming Ye
// @match        http://www.imooc.com/*/*
// @include      https://www.imooc.com/*/*
// @require      https://greasyfork.org/scripts/34143-debug/code/debug.js?version=232648
// @require      http://cdn.bootcss.com/jquery/1.8.3/jquery.min.js
//  @require https://greasyfork.org/scripts/27104-filesaver/code/FileSaver.js?version=173518
// @require      https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var log = window.myDebugger.consoleFactory('慕课网导出笔记脚本:', 'color:brown;font-size:13px'),
        debugTrue = window.myDebugger.debugTrue(true);
    var $btnContainer = $('#main .course-info-main .content-wrap .mod-tab-menu .course-menu'),
        $collectBtn = $('<a id="exportNoteBtn" style="margin:10px;padding:10px;border-radius:5px;background-color:orange;color:white;cursor:pointer;">导出笔记</a>'),//采集按钮
        $exportColCheck = $('<div style="display:inline-block;margin-right:10px"><input type="checkbox" id="exportColCheck"/><span style="color:white;">采集</span>'),
        $exportPraCheck = $('<input type="checkbox" id="exportPraCheck"/><span style="color:white;">点赞</span>'),
        $curPageLabel = $('<p id="curPage" style="margin-top:10px;margin-left:30px;color:white;"><span>当前采集页数:</span><span id="curPageNum">0</span></p>');
    
    $collectBtn.click(btnClickHandler).appendTo($('#main .course-infos .hd'));
    $exportColCheck.appendTo($('#main .course-infos .hd'));
    $exportPraCheck.appendTo($('#main .course-infos .hd'));
    $curPageLabel.appendTo($('#main .course-infos .hd'));
    
    var isPause = true,
        courseId = window.location.pathname.split("/")[2], //课程ID
        baseUrl = window.location.protocol + '//' + window.location.hostname + '/note/' + courseId + '?sort=hot&page=',
        pageNum = localStorage.getItem('pageNum-'+courseId), //笔记页码
        noteTimeout = null,
        userObj = fetchUser().data, //用户对象,保存用户相关信息
        notes = JSON.parse(localStorage.getItem('notes-'+courseId)), //
        isCol = false,//是否收集采集的笔记
        isPra = false;//是否收集点赞的笔记
    log('课程ID', courseId);
    log('基准url', baseUrl);
    pageNum = pageNum ? pageNum : 1;

    /**
     * 导出笔记按钮点击事件
     */
    function btnClickHandler(){
        isPause = !isPause;
        if(isPause){
            $("#exportNoteBtn").text('导出笔记');
            clearTimeout(noteTimeout)
        }else{
            isCol = $('#exportColCheck')[0].checked;
            isPra = $('#exportPraCheck')[0].checked;
            $("#exportNoteBtn").text('暂停导出');
            fetchNote();
        }
    }
    /**
     * 采集笔记
     */
    function fetchNote(){
        var htmlText = $.ajax({
            url: baseUrl + pageNum,
            async: false
        }).responseText;
        //最后导出笔记
        if(htmlText.indexOf('此课程暂无同学记录过笔记')>=0){
            var blob = new Blob([JSON.stringify(notes)], {type: "text/plain;charset=utf-8"});
            saveAs(blob, "notes.json");
            return;
        }

        log('pageNum',pageNum);
        $("#curPageNum").html(pageNum);
        ++pageNum;
        var obj = getNoteElem(htmlText);
        var noteStatus = getUserNoteStatus(obj.noteIds);
        var colObj = getColObj(noteStatus);
        var colIdArr = []; //需要采集的笔记的id数组
        if(isCol){
            colIdArr = colObj.col;
        }else if(isPra){
            for(var i=0,len=colObj.pra.length;i<len;i++){
                colIdArr.push(colObj.pra[i]);
            }
        }
        var dataArr = fetchColContent(colIdArr,obj.noteStrArr);
        
        notes = notes || [];
        for (var i = 0, len = dataArr.length; i < len; i++) {
            notes.push(dataArr[i]);
        }
        log('pageNum',pageNum);
        // debugTrue();
        localStorage.setItem('notes-'+courseId, JSON.stringify(notes));
        localStorage.setItem('pageNum-'+courseId,pageNum);
        //当查询不到笔记容器id表示已经获取完所有的笔记了;
        noteTimeout = setTimeout(fetchNote,100);
        return ;
    }
    
    /**
     * 获取采集对象
     * @param  {array} colArr 采集参照的id数组
     * @return {object}        采集对象
     */
    function fetchColContent(colArr,liStrArr) {
        var retDatas = [];
        for(var i=0,len=colArr.length;i<len;i++){
            var id = colArr[i];
            for(var j=0,len2=liStrArr.length;j<len2;j++){
                var liStr = liStrArr[j];
                if(liStr.indexOf('id="'+id+'"')>=0 || liStr.indexOf('authorid="'+userObj.uid+'"')>=0){
                    var patternNote = /<div class="js-notelist-content notelist-content mynote">[\s\S]*?<pre class="autowrap">([\s\S]*?)<\/pre>/g,
                        patternUser = /<div class="tit">[\s\S]*?<a [\s\S]*? target="_blank">([\s\S]*?)<\/a>/g,
                        patternTime = /<div class="footer clearfix">[\s\S]*?<span [\s\S]*?>时间:([\s\S]*?)<\/span>/g,
                        patternChapter = /<div class="footer clearfix">[\s\S]*?<a [\s\S]*?>源自:([\s\S]*?)<\/a>/g,
                        patternAuthLink = /<div class="media">[\s\S]*?<a href="([\s\S]*?)">/,
                        patternScreenshot = /<div class="js-toggle-notelist answerImg">[\s\S]*?<img [\s\S]*? data-src="([\s\S]*?)"/,
                        patternCourseLink = /<div class="footer clearfix">[\s\S]*?<a href="[\s\S]*?"/g,
                        patternFileId = /<div class="disscus-code-icon-wrap">[\s\S]*?<i class="disscus-code-icon js-show-node-code" data-id="([\s\S]*?)"/g;
                    var noteStr = patternNote.exec(liStr)[1],
                        user = patternUser.exec(liStr)[1],
                        time = patternTime.exec(liStr)[1],
                        chapter = patternChapter.exec(liStr)[1],
                        authLink = window.location.origin + patternAuthLink.exec(liStr)[1],
                        courseLink = window.location.origin + patternCourseLink.exec(liStr)[1],
                        matchScreenshotLink = patternScreenshot.exec(liStr),
                        matchFileId = patternFileId.exec(liStr);
                    var data = {
                        note:noteStr,
                        time:time,
                        user:user,
                        chapter:chapter,
                        authLink:authLink,
                        courseLink:courseLink
                    };
                    //如果存在截图,则获取截图url
                    if(matchScreenshotLink && matchScreenshotLink[1]){
                        data.screenshotLink = window.location.protocol+matchScreenshotLink[1]
                    }
                    //如果存在代码快照,则获取代码快照
                    if(matchFileId && matchFileId[1]){
                        var retStr = $.ajax({
                            url: 'https://www.imooc.com/course/viewnotecode?id='+matchFileId[1],
                            async: false,
                        }).responseText;
                        data.codeFiles = JSON.parse(retStr).files;
                    }
                    log.logObj('data',data);
                    retDatas.push(data);
                }
            }
        }

        log.logArr('fetchColContent', retDatas);
        return retDatas;
    }
    /**
     * 使用jsonp方式获取用户对象,获得对象后保存在window.usr中
     */
    function fetchUser() {
        var retStr = $.ajax({
            url: window.location.protocol+'//www.imooc.com/u/card%20?jsonpcallback=getUser&_=' + new Date().getTime(),
            async: false,
        }).responseText;
        log('fetchUser', retStr);
        retStr = retStr.replace('getUser(', '');
        retStr = retStr.substr(0, retStr.length - 1);
        var usr = JSON.parse(retStr);
        log.logObj('fetchUser', usr);
        return usr;
    }
    /**
     * 获取采集的id数组
     * @param  {object} noteStatus 笔记状态对象
     * @return {object}            返回采集和点赞的数组{col:[],pra:[]}
     */
    function getColObj(noteStatus) {
        var tmp = noteStatus.data.collections,
            retObj = {
                col: [],
                pra: []
            };
        for (var i = 0, len = tmp.length; i < len; i++) {
            retObj.col.push(tmp[i].src_note_id);
        }
        tmp = noteStatus.data.praises;
        for (var i = 0, len = tmp.length; i < len; i++) {
            retObj.pra.push(tmp[i].note_id);
        }
        log.logObj('getColIds', retObj);
        return retObj;
    }
    /**
     * 获取笔记元素的id和字符串数组
     * @return {object} 笔记id和笔记元素字符串数组
     */
    function getNoteElem(htmlText){
        var pattern = /<ul id="js-note-container" class="mod-post">([\s\S]*)?<\/ul>/g;
        var pattern1 = /<li id="([\d]+)" class="post-row js-find-txt" courseid="[\d]+" noteId="[\d]+" authorid="[\d]+">[\s\S]*?<\/li>/g;
        var matchArr = pattern.exec(htmlText),
            liIds = [],
            liStrArr = [],
            regInput = matchArr[0];
        while(matchArr = pattern1.exec(regInput)){
            log.logObj('matchArr',matchArr);
            liIds.push(matchArr[1]);
            liStrArr.push(matchArr[0]);
        }
        log.logObj('liStrArr',liStrArr);
        log.logArr('liIds',liIds);
        return {noteIds:liIds,noteStrArr:liStrArr};
    }
    /**
     * 获取用户笔记状态
     * @param  {array} ids 用户笔记id数组
     * @return {object} 用户笔记操作状态对象
     */
    function getUserNoteStatus(ids) {
        var retStr = $.ajax({
                url: window.location.protocol+'//www.imooc.com/course/AjaxUserNotesStatus?ids=' + ids.join('%2C'),
                async: false
            }).responseText,
            retObj = JSON.parse(retStr);
        log('url',window.location.protocol+'//www.imooc.com/course/AjaxUserNotesStatus?ids=' + encodeURI(ids.join('%2C')));
        log.logObj('getUserNoteStatus', retObj);
        return retObj;
    }
    
})();