HistogramHeatGraph_html5.user.js

ニコニコ動画でコメントの盛り上がりをグラフで表示(html5版)

Version vom 10.11.2017. Aktuellste Version

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

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

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

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

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

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name           HistogramHeatGraph_html5.user.js
// @namespace      sotoba
// @version        0.20171111
// @description    ニコニコ動画でコメントの盛り上がりをグラフで表示(html5版)
// @match          http://www.nicovideo.jp/watch/*
// @include        http://www.nicovideo.jp/watch/*
// @require        http://code.jquery.com/jquery-latest.min.js
// @grant          none
// ==/UserScript==

(function () {

    const MINIMUMBARNUM=50;
    const DEFAULTINTERBAL=10;
    const MAXCOMMENTNUM=30;
    const  GRAPHHEIGHT = 30;
    const styleString = `
#comment-graph {
background: repeating-linear-gradient(to top, #000, #222 10px);
border: 1px solid #000;
border-top: 0;
float: left;
font-size: 0;
white-space: nowrap;
}
#comment-list {
background: #000;
color: #fff;
font-size: 12px;
line-height: 1.25;
padding: 4px 4px 0;
pointer-events: none;
position: absolute;
z-index: 9999;
}
#comment-list:empty {
display: none;
}
`;
    const style = document.createElement('style');
    style.appendChild(document.createTextNode(styleString));
    document.body.appendChild(style);

    $('.PlayerContainer').eq(0).append('<div id=comment-graph></div>');
    $('.MainContainer').eq(0).append('<div id=comment-list></div>');
    const $commentgraph = $('#comment-graph');
    const $commentlist = $('#comment-list');

    var ApiJsonData=JSON.parse(document.getElementById('js-initial-watch-data').getAttribute('data-api-data'));

    var thread_id;
    var video_id;
    var user_id;
    if(ApiJsonData.video.dmcInfo !== null){
        thread_id=ApiJsonData.video.dmcInfo.thread.thread_id;
        video_id=ApiJsonData.video.dmcInfo.video.video_id;
        user_id=ApiJsonData.video.dmcInfo.user.user_id;
    }else{
        thread_id=ApiJsonData.thread.ids.default;
        video_id=ApiJsonData.video.id;
        user_id=ApiJsonData.viewer.id;
    }

    if(video_id.startsWith('sm')||video_id.startsWith('nm')){
        $.ajax({
            url:'http://nmsg.nicovideo.jp/api/thread?thread='+thread_id+'&version=20061206&res_from=-1000&scores=1',
            type:'GET',
            dataType:'xml',
            timeout:3000,
            error:function() {
                console.log("Ajax:failed");
            },
            success:function(xml){
                drowgraph(xml,ApiJsonData);
            }
        });
    }else{
        $.ajax({
            url:'http://flapi.nicovideo.jp/api/getthreadkey?thread='+thread_id,
            type:'GET',
            timeout:3000,
            error:function() {
                console.log("Ajax:failed");
            },
            success:function(response){
                $.ajax({
                    url:'http://nmsg.nicovideo.jp/api/thread?thread='+thread_id+'&version=20061206&res_from=-1000&scores=1&user='+user_id+'&'+response,
                    type:'GET',
                    dataType:'xml',
                    timeout:3000,
                    error:function() {
                        console.log("Ajax:failed...");
                    },
                    success:function(xml){
                        drowgraph(xml,ApiJsonData);
                    }
                });
            }
        });
    }

    function  drowgraph(commentData,ApiJsonData){
        var $canvas=$("#CommentRenderer").children('canvas').eq(0);
        var playerWidth =parseFloat($canvas.css("width"));
        var videoTotalTime = ApiJsonData.video.dmcInfo !== null ?  ApiJsonData.video.dmcInfo.video.length_seconds : ApiJsonData.video.duration;
        var barTimeInterval;
        var barIndexNum;
        if(videoTotalTime > MINIMUMBARNUM*DEFAULTINTERBAL){
            barTimeInterval=DEFAULTINTERBAL;
            barIndexNum=Math.ceil(videoTotalTime / barTimeInterval);
        }else if(videoTotalTime>MINIMUMBARNUM){
            barIndexNum=MINIMUMBARNUM;
            barTimeInterval=Math.round(videoTotalTime/MINIMUMBARNUM);
        }else{
            barIndexNum=Math.floor(videoTotalTime);
            barTimeInterval=1;
        }
        $('#comment-graph').width( playerWidth );
        const barColors = [
            '126da2', '1271a8', '1275ae', '1279b4', '137dba',
            '1381c0', '1385c6', '1489cc', '148dd2', '1491d8'
        ];
        var listCounts = (new Array(barIndexNum)).fill(0);
        var listMessages = (new Array(barIndexNum)).fill("");
        var listTimes = (new Array(barIndexNum)).fill("");
        var lastBarTimeIntervalGap = Math.floor(videoTotalTime- (barIndexNum * barTimeInterval));
        var barWidth = playerWidth / barIndexNum;
        var barTimePoint = 0;

        $(commentData).find('chat').each(function(index){
            var vpos = $(this).attr('vpos')/100;
            var section=Math.floor(vpos/barTimeInterval);
            listCounts[section]++;
            if(listCounts[section]<=MAXCOMMENTNUM){
                var comment=$(this).text().replace(/"|<|&lt;/g, ' ').replace(/\n/g, '<br>');
                listMessages[section]+=comment+'<br>';
            }
        });
        var startMin=0;
        var startSec=0;
        var min=0;
        var sec=0;
        for (var i = 0; i < barIndexNum-1; i++) {
            startMin=min;
            startSec=sec;
            sec+=barTimeInterval;
            if(59 < sec){
                min+=1;
                sec-=60;
            }
            listTimes[i] += `${("0"+startMin).slice(-2)}:${("0"+startSec).slice(-2)}-${("0"+min).slice(-2)}:${("0"+sec).slice(-2)}`;
        }
        startMin=min;
        startSec=sec;
        sec+=(barTimeInterval+lastBarTimeIntervalGap);
        if(59 < sec){
            min+=1;
            sec-=60;
        }
        listTimes[barIndexNum-1] += `${("0"+startMin).slice(-2)}:${("0"+startSec).slice(-2)}-${("0"+min).slice(-2)}:${("0"+sec).slice(-2)}`;

        // TODO なぜかbarIndexNum以上の配列ができる
        listCounts=listCounts.slice(0, barIndexNum);
        var listCountMax = Math.max.apply(null,listCounts);
        const barColorRatio = (barColors.length - 1) / listCountMax;


        $commentgraph.empty();
        $commentgraph.height(GRAPHHEIGHT);
        var barColor;
        var barBackground;
        for (i = 0; i <= barIndexNum; i++) {
            barColor = barColors[Math.floor(listCounts[i] * barColorRatio)];
            barBackground = `linear-gradient(to top, #${barColor}, #${barColor} ` +
                `${listCounts[i]}px, transparent ${listCounts[i]}px, transparent)`;
            var barText = listCounts[i] ?
                `${listMessages[i]}<br><br>${listTimes[i]} コメ ${listCounts[i]}` : '';
            $('<div>')
                .css('background-image', barBackground)
                .css('float','left')
                .data('text', barText)
                .height(GRAPHHEIGHT)
                .width(barWidth)
                .addClass("commentbar")
                .appendTo($commentgraph);
        }
        $commentgraph.children().on({
            'mouseenter': function(val) {
                $commentlist.css({
                    'left': $(this).offset().left,
                    'top': $commentgraph.offset().top - $commentlist.height() - 10
                })
                    .html($(this).data('text'));
            },
            'mousemove': function(val) {
                $commentlist.offset({
                    'left': $(this).offset().left,
                    'top': $commentgraph.offset().top - $commentlist.height() - 10
                });
            },
            'mouseleave': function() {
                $commentlist.empty();
            }
        });
        /* 1 Dom Style Watcher本体 監視する側*/
        var domStyleWatcher = {
            Start: function(tgt, styleobj){
                function eventHappen(data1, data2){
                    var throwval = tgt.css(styleobj);
                    tgt.trigger('domStyleChange', [throwval]);
                }
                var tge = tgt[0];
                var filter = ['style'];
                var options = {
                    attributes: true,
                    attributeFilter: filter
                };
                var mutOb = new MutationObserver(eventHappen);
                mutOb.observe(tge, options);
                return mutOb;
            },
            Stop: function(mo){
                mo.disconnect();
            }
        };
        function catchEvent(event, value){
            playerWidth=parseFloat(value);
            $('#comment-graph').width(playerWidth );
            $('.commentbar').width(playerWidth / barIndexNum);
            console.log(event);
            console.log(value);
        }
        var target = $canvas;
        var styleobj = 'width';
        target.on('domStyleChange', catchEvent);//イベントを登録
        var dsw = domStyleWatcher.Start(target, styleobj);//監視開始
        //domStyleWatcher.Stop(dsw);//監視終了
    }
})();