HistogramHeatGraph_html5.user.js

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

2017-11-10 기준 버전입니다. 최신 버전을 확인하세요.

  1. // ==UserScript==
  2. // @name HistogramHeatGraph_html5.user.js
  3. // @namespace sotoba
  4. // @version 0.20171111
  5. // @description ニコニコ動画でコメントの盛り上がりをグラフで表示(html5版)
  6. // @match http://www.nicovideo.jp/watch/*
  7. // @include http://www.nicovideo.jp/watch/*
  8. // @require http://code.jquery.com/jquery-latest.min.js
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function () {
  13.  
  14. const MINIMUMBARNUM=50;
  15. const DEFAULTINTERBAL=10;
  16. const MAXCOMMENTNUM=30;
  17. const GRAPHHEIGHT = 30;
  18. const styleString = `
  19. #comment-graph {
  20. background: repeating-linear-gradient(to top, #000, #222 10px);
  21. border: 1px solid #000;
  22. border-top: 0;
  23. float: left;
  24. font-size: 0;
  25. white-space: nowrap;
  26. }
  27. #comment-list {
  28. background: #000;
  29. color: #fff;
  30. font-size: 12px;
  31. line-height: 1.25;
  32. padding: 4px 4px 0;
  33. pointer-events: none;
  34. position: absolute;
  35. z-index: 9999;
  36. }
  37. #comment-list:empty {
  38. display: none;
  39. }
  40. `;
  41. const style = document.createElement('style');
  42. style.appendChild(document.createTextNode(styleString));
  43. document.body.appendChild(style);
  44.  
  45. $('.PlayerContainer').eq(0).append('<div id=comment-graph></div>');
  46. $('.MainContainer').eq(0).append('<div id=comment-list></div>');
  47. const $commentgraph = $('#comment-graph');
  48. const $commentlist = $('#comment-list');
  49.  
  50. var ApiJsonData=JSON.parse(document.getElementById('js-initial-watch-data').getAttribute('data-api-data'));
  51.  
  52. var thread_id;
  53. var video_id;
  54. var user_id;
  55. if(ApiJsonData.video.dmcInfo !== null){
  56. thread_id=ApiJsonData.video.dmcInfo.thread.thread_id;
  57. video_id=ApiJsonData.video.dmcInfo.video.video_id;
  58. user_id=ApiJsonData.video.dmcInfo.user.user_id;
  59. }else{
  60. thread_id=ApiJsonData.thread.ids.default;
  61. video_id=ApiJsonData.video.id;
  62. user_id=ApiJsonData.viewer.id;
  63. }
  64.  
  65. if(video_id.startsWith('sm')||video_id.startsWith('nm')){
  66. $.ajax({
  67. url:'http://nmsg.nicovideo.jp/api/thread?thread='+thread_id+'&version=20061206&res_from=-1000&scores=1',
  68. type:'GET',
  69. dataType:'xml',
  70. timeout:3000,
  71. error:function() {
  72. console.log("Ajax:failed");
  73. },
  74. success:function(xml){
  75. drowgraph(xml,ApiJsonData);
  76. }
  77. });
  78. }else{
  79. $.ajax({
  80. url:'http://flapi.nicovideo.jp/api/getthreadkey?thread='+thread_id,
  81. type:'GET',
  82. timeout:3000,
  83. error:function() {
  84. console.log("Ajax:failed");
  85. },
  86. success:function(response){
  87. $.ajax({
  88. url:'http://nmsg.nicovideo.jp/api/thread?thread='+thread_id+'&version=20061206&res_from=-1000&scores=1&user='+user_id+'&'+response,
  89. type:'GET',
  90. dataType:'xml',
  91. timeout:3000,
  92. error:function() {
  93. console.log("Ajax:failed...");
  94. },
  95. success:function(xml){
  96. drowgraph(xml,ApiJsonData);
  97. }
  98. });
  99. }
  100. });
  101. }
  102.  
  103. function drowgraph(commentData,ApiJsonData){
  104. var $canvas=$("#CommentRenderer").children('canvas').eq(0);
  105. var playerWidth =parseFloat($canvas.css("width"));
  106. var videoTotalTime = ApiJsonData.video.dmcInfo !== null ? ApiJsonData.video.dmcInfo.video.length_seconds : ApiJsonData.video.duration;
  107. var barTimeInterval;
  108. var barIndexNum;
  109. if(videoTotalTime > MINIMUMBARNUM*DEFAULTINTERBAL){
  110. barTimeInterval=DEFAULTINTERBAL;
  111. barIndexNum=Math.ceil(videoTotalTime / barTimeInterval);
  112. }else if(videoTotalTime>MINIMUMBARNUM){
  113. barIndexNum=MINIMUMBARNUM;
  114. barTimeInterval=Math.round(videoTotalTime/MINIMUMBARNUM);
  115. }else{
  116. barIndexNum=Math.floor(videoTotalTime);
  117. barTimeInterval=1;
  118. }
  119. $('#comment-graph').width( playerWidth );
  120. const barColors = [
  121. '126da2', '1271a8', '1275ae', '1279b4', '137dba',
  122. '1381c0', '1385c6', '1489cc', '148dd2', '1491d8'
  123. ];
  124. var listCounts = (new Array(barIndexNum)).fill(0);
  125. var listMessages = (new Array(barIndexNum)).fill("");
  126. var listTimes = (new Array(barIndexNum)).fill("");
  127. var lastBarTimeIntervalGap = Math.floor(videoTotalTime- (barIndexNum * barTimeInterval));
  128. var barWidth = playerWidth / barIndexNum;
  129. var barTimePoint = 0;
  130.  
  131. $(commentData).find('chat').each(function(index){
  132. var vpos = $(this).attr('vpos')/100;
  133. var section=Math.floor(vpos/barTimeInterval);
  134. listCounts[section]++;
  135. if(listCounts[section]<=MAXCOMMENTNUM){
  136. var comment=$(this).text().replace(/"|<|&lt;/g, ' ').replace(/\n/g, '<br>');
  137. listMessages[section]+=comment+'<br>';
  138. }
  139. });
  140. var startMin=0;
  141. var startSec=0;
  142. var min=0;
  143. var sec=0;
  144. for (var i = 0; i < barIndexNum-1; i++) {
  145. startMin=min;
  146. startSec=sec;
  147. sec+=barTimeInterval;
  148. if(59 < sec){
  149. min+=1;
  150. sec-=60;
  151. }
  152. listTimes[i] += `${("0"+startMin).slice(-2)}:${("0"+startSec).slice(-2)}-${("0"+min).slice(-2)}:${("0"+sec).slice(-2)}`;
  153. }
  154. startMin=min;
  155. startSec=sec;
  156. sec+=(barTimeInterval+lastBarTimeIntervalGap);
  157. if(59 < sec){
  158. min+=1;
  159. sec-=60;
  160. }
  161. listTimes[barIndexNum-1] += `${("0"+startMin).slice(-2)}:${("0"+startSec).slice(-2)}-${("0"+min).slice(-2)}:${("0"+sec).slice(-2)}`;
  162.  
  163. // TODO なぜかbarIndexNum以上の配列ができる
  164. listCounts=listCounts.slice(0, barIndexNum);
  165. var listCountMax = Math.max.apply(null,listCounts);
  166. const barColorRatio = (barColors.length - 1) / listCountMax;
  167.  
  168.  
  169. $commentgraph.empty();
  170. $commentgraph.height(GRAPHHEIGHT);
  171. var barColor;
  172. var barBackground;
  173. for (i = 0; i <= barIndexNum; i++) {
  174. barColor = barColors[Math.floor(listCounts[i] * barColorRatio)];
  175. barBackground = `linear-gradient(to top, #${barColor}, #${barColor} ` +
  176. `${listCounts[i]}px, transparent ${listCounts[i]}px, transparent)`;
  177. var barText = listCounts[i] ?
  178. `${listMessages[i]}<br><br>${listTimes[i]} コメ ${listCounts[i]}` : '';
  179. $('<div>')
  180. .css('background-image', barBackground)
  181. .css('float','left')
  182. .data('text', barText)
  183. .height(GRAPHHEIGHT)
  184. .width(barWidth)
  185. .addClass("commentbar")
  186. .appendTo($commentgraph);
  187. }
  188. $commentgraph.children().on({
  189. 'mouseenter': function(val) {
  190. $commentlist.css({
  191. 'left': $(this).offset().left,
  192. 'top': $commentgraph.offset().top - $commentlist.height() - 10
  193. })
  194. .html($(this).data('text'));
  195. },
  196. 'mousemove': function(val) {
  197. $commentlist.offset({
  198. 'left': $(this).offset().left,
  199. 'top': $commentgraph.offset().top - $commentlist.height() - 10
  200. });
  201. },
  202. 'mouseleave': function() {
  203. $commentlist.empty();
  204. }
  205. });
  206. /* 1 Dom Style Watcher本体 監視する側*/
  207. var domStyleWatcher = {
  208. Start: function(tgt, styleobj){
  209. function eventHappen(data1, data2){
  210. var throwval = tgt.css(styleobj);
  211. tgt.trigger('domStyleChange', [throwval]);
  212. }
  213. var tge = tgt[0];
  214. var filter = ['style'];
  215. var options = {
  216. attributes: true,
  217. attributeFilter: filter
  218. };
  219. var mutOb = new MutationObserver(eventHappen);
  220. mutOb.observe(tge, options);
  221. return mutOb;
  222. },
  223. Stop: function(mo){
  224. mo.disconnect();
  225. }
  226. };
  227. function catchEvent(event, value){
  228. playerWidth=parseFloat(value);
  229. $('#comment-graph').width(playerWidth );
  230. $('.commentbar').width(playerWidth / barIndexNum);
  231. console.log(event);
  232. console.log(value);
  233. }
  234. var target = $canvas;
  235. var styleobj = 'width';
  236. target.on('domStyleChange', catchEvent);//イベントを登録
  237. var dsw = domStyleWatcher.Start(target, styleobj);//監視開始
  238. //domStyleWatcher.Stop(dsw);//監視終了
  239. }
  240. })();