Youtube直播在线人数记录

绘制直播过程中的在线人数变化曲线,支持下载数据或图片

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Youtube直播在线人数记录
// @namespace    http://tampermonkey.net/
// @namespace    https://github.com/artyyin/YoutubeLiveOnline
// @version      0.41
// @description  绘制直播过程中的在线人数变化曲线,支持下载数据或图片
// @author       [email protected]
// @match        https://www.youtube.com/watch?v=*

// @require      https://code.jquery.com/jquery-1.11.3.min.js
// @require      https://code.highcharts.com/highcharts.js
// @require      https://code.highcharts.com/modules/series-label.js
// @require      https://code.highcharts.com/modules/exporting.js
// @require      https://code.highcharts.com/modules/offline-exporting.js
// @require      https://code.highcharts.com/modules/export-data.js

// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// ==/UserScript==



(function() {
    //'use strict';
    var mainkey ="monkey-youtube-number-online-123456789"
    var prefixofdatakey='artyyby-';
    var debug_switch = false;
    var onlinedata =new Array()
    var hisdata;
    var HistroyVisible=true;
    var chart;
    var aq_interval=5*1000;
    var n=0;
    var datakey ;
    var testdata = [
		[new Date(2018,7,16,23,49,4).getTime(),7296]
    ];
    var timer_2;

    //deleteAllKeys();
    //listAllKeys();

    reset()
    setTimeout(delay_init, 5*1000);

    function reset()
    {
        if(timer_2!=undefined)
        {
            clearTimeout(timer_2)
            timer_2=undefined
        }
        if(chart!=undefined)
        {
            $("#online_chartwin").remove()
            chart=undefined
        }
        onlinedata =new Array()
        hisdata=undefined
        HistroyVisible=true
        n=0;
        datakey=undefined
        testdata = [
            [new Date(2018,7,16,23,49,4).getTime(),7296]
        ];
    }
    function delay_init()
    {
        if($('button.ytp-live-badge.ytp-button').length==0)
        {
            setTimeout(delay_init, 5*1000);
            console.log('wait 5s , retry to find button');
            return;
        }
        if($('button.ytp-live-badge.ytp-button').is(":visible")==false)
        {
            //alert("no live")
            n+=1;
            if(n<=4)
            {
                 setTimeout(delay_init, 5*1000);
                 console.log('wait 5s , retry to live state');
            }
            else
            {
                console.log('not live stream, exit');
            }
            return
        }
        datakey = ceateAndAddKey();
        add_chart_winnode()
        showchartwin()
        timer_2 = setTimeout(onTimer_2, aq_interval);
    }


    function add_chart_winnode()
    {
        var windivnode =$('<div id="online_chartwin"></div>')
        windivnode.css({
            /*希望窗口有边框*/
            'border': '1px #0769ad solid',
            /*希望窗口宽度和高度固定,不要太大*/
            //'width': '500px',
            //'height': '300px',
            /*希望控制窗口的位置*/
            //'position': 'absolute',
            //'top': '100px',
            //'left': '350px',
            /*希望窗口开始时不可见*/
            'display': 'none'
        });
        var titledivnode = $('<div id="online_charttitle">在线人数记录</div>')
        titledivnode.css({
            /*控制标题栏的背景色*/
            'background-color': '#7cB5EC',
            /*控制标题栏中文字的颜色*/
            'color': 'white',
            /*控制标题栏的左内边距*/
            'padding-left':'3px'
        })
        var closebuttonnode = $('<span id="online_chartclose">[X]</span>')
        closebuttonnode.css({
            /*使关闭按钮向右侧移动*/
            //'position':'relative',
            'margin-right': '5px',
            //'right':'-10px',
            'float':'right',
            /*让鼠标进入时可以显示小手,告知用户可以点击操作*/
            'cursor': 'pointer'
        })
        closebuttonnode.click(onbuttonclick)
        var contentdivnode = $('<div id="online_chartcontent">窗口</div>')
        contentdivnode.css({
                'padding-left': '3px',
                'padding-top': '5px'
            })
        windivnode.append(titledivnode)
        windivnode.append(contentdivnode)
        titledivnode.append(closebuttonnode)
        $("#meta-contents").after(windivnode)
        addChart()
    }


    function addChart()
    {
        var dafaultMenuItem = Highcharts.getOptions().exporting.buttons.contextButton.menuItems;
        var myMenuItem=[
            {
                separator: true
            },
            {
                text:'隐藏历史数据',
                onclick:hideHistoryData
            },
            {
                text:'显示历史数据',
                onclick:showHistoryData
            },
            {
                text: '清除历史数据',
                onclick:clearCurrentPageKey
            },
            {
                text: '清除所有历史数据',
                onclick:clearAppKeys//deleteAllKeys
            }
        ]
        dafaultMenuItem.pop();
        dafaultMenuItem.pop();
        myMenuItem = dafaultMenuItem.concat(myMenuItem);
        //console.log(myMenuItem);

        Highcharts.setOptions({
            global: {
                useUTC: false
            }
        });
        //console.log('addChart')
        chart = Highcharts.chart('online_chartcontent', {
            chart: {
                //type: 'scatter',
                plotBorderWidth: 1,
                zoomType: 'x'
                   },
            title: {
                text: 'youtube直播在线人数--'+$('#text > a').text()//document.title.substring(0,50)//'在线人数--youtube直播'
            },
            subtitle: {
                useHTML:true,
                text:getSubTitle()
            },
            xAxis: {
                dateTimeLabelFormats: {
                    millisecond: '%H:%M:%S.%L',
                    second: '%H:%M:%S',
                    minute: '%H:%M',
                    hour: '%H:%M',
                    day: '%Y-%m-%d',
                    week: '%m-%d',
                    month: '%Y-%m',
                    year: '%Y'
                },
                title: {
                    text: '时间'
                },
                type: 'datetime'
            },
            yAxis: {
                //min:0,
                title: {
                    text: '人数'
                }
            },
            legend:{enabled:false},
            tooltip: {
                headerFormat: '<b>时间:{point.x:%Y-%m-%d %H:%M:%S}</b><br>',
                pointFormat : '<b>人数: {point.y:.0f} 人</b>'
            },
            plotOptions: {
                line:{
                    lineWidth : 2,
                    marker: {radius:3}
                }
            },
            exporting: {
                buttons: {
                    contextButton: {
                        menuItems:myMenuItem
                    }
                }
            },

            series: [{
                name: '在线人数',
                type: 'line',
                data: testdata
            }]
        });
        //console.log('addCHart() end')
        hisdata = loadHistoryData();
        if(hisdata.length>0)
        {
            var tmp=[]
            tmp = tmp.concat(hisdata)
            chart.series[0].setData(tmp)
        }
        else
        {
            chart.series[0].removePoint(0);
        }
        //console.log(chart.options.exporting.buttons.contextButton.menuItems)
    }
    function getSubTitle()
    {
        var s = $('#container > h1 > yt-formatted-string').text();
        if(s.length>50)
        {
            s = s.substring(0,30)+' ... '+s.substring(s.length-15);
        }
        return '<p align="center">'+s+'</p>'
    }
    function onbuttonclick()
    {
        var winNode = $("#online_chartcontent");
        if(winNode.is(":hidden"))
        {
            //winNode.show();
            winNode.fadeIn("slow");
        }
        else
        {
            //winNode.hide();
            winNode.fadeOut("slow");
        }
    }
    //显示曲线窗口
    function showchartwin() {
        //lert("准备显示弹出窗口啦!!!");
        //1.找到窗口对应的div节点
        var winNode = $("#online_chartwin");
        //2.让div对应的窗口显示出来
        //方法1,修改节点的css值,让窗口显示出来
        //winNode.css("display","block");
        //方法2,利用Jqeury的show方法
        //winNode.show("slow");
        //方法3,利用JQuery的fadeIn方法
        winNode.fadeIn("slow");
    }

    //定时采集数据
    function onTimer_2()
    {
        var point = getNumberOfOnline()
        var idx = onlinedata.length;
        chart.series[0].addPoint(point, true, false);
        //activeLastPointToolip(chart);
        onlinedata[idx] = point
        GM_setValue(datakey,onlinedata);
        timer_2 = setTimeout(onTimer_2, aq_interval);
    }
    function activeLastPointToolip(chart) {
        var points = chart.series[0].points;
        chart.tooltip.refresh(points[points.length -1]);
    }
    function getTestData()
    {
        var currentdate = new Date().getTime();
        var numofonline =5000+700*Math.sin(onlinedata.length/3.14) + Math.floor(Math.random()*700+1)
        var result =new Array()
        result[0]=currentdate
        result[1]=numofonline
        if(debug_switch)
        {
            var datestr = result[0]
            var logmsg = datestr+" | "+result[1]
            console.log(logmsg);
        }
        return result
    }

    //采集在线人数
    function getNumberOfOnline()
    {
        let currentdate = (new Date()).getTime();
        //var elm = $("div[id=info-text]>div[id=count]>ytd-video-view-count-renderer>span:first-child")
        let elm = $("div[id=info-container]>yt-formatted-string[id=info]>span:first-child")
        let numtext = elm.text()
        let numstr=/[0-9,]+/.exec(numtext).toString();
        let numofonline =parseInt(numstr.replace(',',''));
        if(debug_switch)
        {
            let logmsg = currentdate+" | "+numofonline+" | "+ numtext
            console.log(logmsg);
        }
        let result =new Array()
        result[0]=currentdate
        result[1]=numofonline
        return result
    }
    function listAllKeys()
    {
        var allkey = GM_listValues();
        for (var idx in allkey)
        {
            var data = GM_getValue(allkey[idx]);
            console.log(idx,allkey[idx]);
            console.log(data);
        }
    }
    function deleteAllKeys()
    {
        var allkey = GM_listValues();
        for (var idx in allkey)
        {
            GM_deleteValue(allkey[idx]);
        }
    }
    
    function clearAppKeys()
    {
        var allurl = GM_getValue(mainkey,[]);
        for (var u in allurl)
        {
            var url = allurl[u];
            var alldatakey = GM_getValue(url,[])
            for(var d in alldatakey)
            {
               GM_deleteValue(alldatakey[d])
            }
            GM_deleteValue(url)
        }
        GM_getValue(mainkey)
        var allkey = GM_listValues();
        for (var idx in allkey)
        {
            if(allkey[idx].indexOf(prefixofdatakey)==0)
            {
                GM_deleteValue(allkey[idx]);
            }
        }
    }
    function clearCurrentPageKey()
    {
        var alldatakey = GM_getValue(document.URL,[]);
        for(var d in alldatakey)
        {
            GM_deleteValue(alldatakey[d])
        }
    }
    function ceateAndAddKey()
    {
        var allurl = GM_getValue(mainkey,[])
        var url = document.URL;
        if(!allurl.includes(url))
        {
            allurl[allurl.length] = url
            GM_setValue(mainkey,allurl)
        }
        var idx = url.indexOf('?v=')

        var timestr = new Date().toLocaleString();
        var dkey = prefixofdatakey+url.substring(idx+3)+timestr
        var keyarray = GM_getValue(url,[])
        keyarray[keyarray.length]=dkey
        GM_setValue(url,keyarray)
        return dkey
    }
    function loadHistoryData()
    {
        var url = document.URL;
        var alldatakey = GM_getValue(url)
        var result=[];
        for(var i in alldatakey)
        {
            var cur_data = GM_getValue(alldatakey[i]);
            if(cur_data!=undefined)
            {
                result = result.concat(cur_data);
            }
        }
        return result;
    }
    function hideHistoryData()
    {
        if(HistroyVisible)
        {
            //alert(HistroyVisible)
            var data=[];
            console.log('hideHistoryData 0:', data.length, hisdata.length, onlinedata.length)
            data = data.concat(onlinedata)
            console.log('hideHistoryData 1:', data.length, hisdata.length, onlinedata.length)
            chart.series [0].setData(data)//,true,new Highcharts.AnimationOptions({duration:1000}));
            console.log('hideHistoryData 2:', data.length, hisdata.length, onlinedata.length)
            //console.log(data)
            HistroyVisible = false;
        }
    }
    function showHistoryData()
    {
        if(!HistroyVisible)
        {
            clearTimeout(timer_2)
            var tmp=[];
            //console.log('showHistoryData 0:',tmp.length, hisdata.length, onlinedata.length)
            tmp = tmp.concat(hisdata)
            tmp = tmp.concat(onlinedata)
            //console.log('showHistoryData 1:',tmp.length, hisdata.length, onlinedata.length)
            //chart.series[0].setData([ [new Date(1970,1,1).getTime(),7296] ],false)
            chart.series[0].setData(tmp)
            timer_2 = setTimeout(onTimer_2, aq_interval);
            HistroyVisible = true;
        }
    }
    function getAbsoluteUrl(url){
        var a = document.createElement('A');
        a.href = url; // 设置相对路径给Image, 此时会发送出请求
        url = a.href; // 此时相对路径已经变成绝对路径
        return url;
    }
})();