Greasy Fork

Youtube直播在线人数记录

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

// ==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;
    }
})();