Youtube直播在线人数记录

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

  1. // ==UserScript==
  2. // @name Youtube直播在线人数记录
  3. // @namespace http://tampermonkey.net/
  4. // @namespace https://github.com/artyyin/YoutubeLiveOnline
  5. // @version 0.41
  6. // @description 绘制直播过程中的在线人数变化曲线,支持下载数据或图片
  7. // @author yinbaoyong@gmail.com
  8. // @match https://www.youtube.com/watch?v=*
  9.  
  10. // @require https://code.jquery.com/jquery-1.11.3.min.js
  11. // @require https://code.highcharts.com/highcharts.js
  12. // @require https://code.highcharts.com/modules/series-label.js
  13. // @require https://code.highcharts.com/modules/exporting.js
  14. // @require https://code.highcharts.com/modules/offline-exporting.js
  15. // @require https://code.highcharts.com/modules/export-data.js
  16.  
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // @grant GM_deleteValue
  20. // @grant GM_listValues
  21. // ==/UserScript==
  22.  
  23.  
  24.  
  25. (function() {
  26. //'use strict';
  27. var mainkey ="monkey-youtube-number-online-123456789"
  28. var prefixofdatakey='artyyby-';
  29. var debug_switch = false;
  30. var onlinedata =new Array()
  31. var hisdata;
  32. var HistroyVisible=true;
  33. var chart;
  34. var aq_interval=5*1000;
  35. var n=0;
  36. var datakey ;
  37. var testdata = [
  38. [new Date(2018,7,16,23,49,4).getTime(),7296]
  39. ];
  40. var timer_2;
  41.  
  42. //deleteAllKeys();
  43. //listAllKeys();
  44.  
  45. reset()
  46. setTimeout(delay_init, 5*1000);
  47.  
  48. function reset()
  49. {
  50. if(timer_2!=undefined)
  51. {
  52. clearTimeout(timer_2)
  53. timer_2=undefined
  54. }
  55. if(chart!=undefined)
  56. {
  57. $("#online_chartwin").remove()
  58. chart=undefined
  59. }
  60. onlinedata =new Array()
  61. hisdata=undefined
  62. HistroyVisible=true
  63. n=0;
  64. datakey=undefined
  65. testdata = [
  66. [new Date(2018,7,16,23,49,4).getTime(),7296]
  67. ];
  68. }
  69. function delay_init()
  70. {
  71. if($('button.ytp-live-badge.ytp-button').length==0)
  72. {
  73. setTimeout(delay_init, 5*1000);
  74. console.log('wait 5s , retry to find button');
  75. return;
  76. }
  77. if($('button.ytp-live-badge.ytp-button').is(":visible")==false)
  78. {
  79. //alert("no live")
  80. n+=1;
  81. if(n<=4)
  82. {
  83. setTimeout(delay_init, 5*1000);
  84. console.log('wait 5s , retry to live state');
  85. }
  86. else
  87. {
  88. console.log('not live stream, exit');
  89. }
  90. return
  91. }
  92. datakey = ceateAndAddKey();
  93. add_chart_winnode()
  94. showchartwin()
  95. timer_2 = setTimeout(onTimer_2, aq_interval);
  96. }
  97.  
  98.  
  99. function add_chart_winnode()
  100. {
  101. var windivnode =$('<div id="online_chartwin"></div>')
  102. windivnode.css({
  103. /*希望窗口有边框*/
  104. 'border': '1px #0769ad solid',
  105. /*希望窗口宽度和高度固定,不要太大*/
  106. //'width': '500px',
  107. //'height': '300px',
  108. /*希望控制窗口的位置*/
  109. //'position': 'absolute',
  110. //'top': '100px',
  111. //'left': '350px',
  112. /*希望窗口开始时不可见*/
  113. 'display': 'none'
  114. });
  115. var titledivnode = $('<div id="online_charttitle">在线人数记录</div>')
  116. titledivnode.css({
  117. /*控制标题栏的背景色*/
  118. 'background-color': '#7cB5EC',
  119. /*控制标题栏中文字的颜色*/
  120. 'color': 'white',
  121. /*控制标题栏的左内边距*/
  122. 'padding-left':'3px'
  123. })
  124. var closebuttonnode = $('<span id="online_chartclose">[X]</span>')
  125. closebuttonnode.css({
  126. /*使关闭按钮向右侧移动*/
  127. //'position':'relative',
  128. 'margin-right': '5px',
  129. //'right':'-10px',
  130. 'float':'right',
  131. /*让鼠标进入时可以显示小手,告知用户可以点击操作*/
  132. 'cursor': 'pointer'
  133. })
  134. closebuttonnode.click(onbuttonclick)
  135. var contentdivnode = $('<div id="online_chartcontent">窗口</div>')
  136. contentdivnode.css({
  137. 'padding-left': '3px',
  138. 'padding-top': '5px'
  139. })
  140. windivnode.append(titledivnode)
  141. windivnode.append(contentdivnode)
  142. titledivnode.append(closebuttonnode)
  143. $("#meta-contents").after(windivnode)
  144. addChart()
  145. }
  146.  
  147.  
  148. function addChart()
  149. {
  150. var dafaultMenuItem = Highcharts.getOptions().exporting.buttons.contextButton.menuItems;
  151. var myMenuItem=[
  152. {
  153. separator: true
  154. },
  155. {
  156. text:'隐藏历史数据',
  157. onclick:hideHistoryData
  158. },
  159. {
  160. text:'显示历史数据',
  161. onclick:showHistoryData
  162. },
  163. {
  164. text: '清除历史数据',
  165. onclick:clearCurrentPageKey
  166. },
  167. {
  168. text: '清除所有历史数据',
  169. onclick:clearAppKeys//deleteAllKeys
  170. }
  171. ]
  172. dafaultMenuItem.pop();
  173. dafaultMenuItem.pop();
  174. myMenuItem = dafaultMenuItem.concat(myMenuItem);
  175. //console.log(myMenuItem);
  176.  
  177. Highcharts.setOptions({
  178. global: {
  179. useUTC: false
  180. }
  181. });
  182. //console.log('addChart')
  183. chart = Highcharts.chart('online_chartcontent', {
  184. chart: {
  185. //type: 'scatter',
  186. plotBorderWidth: 1,
  187. zoomType: 'x'
  188. },
  189. title: {
  190. text: 'youtube直播在线人数--'+$('#text > a').text()//document.title.substring(0,50)//'在线人数--youtube直播'
  191. },
  192. subtitle: {
  193. useHTML:true,
  194. text:getSubTitle()
  195. },
  196. xAxis: {
  197. dateTimeLabelFormats: {
  198. millisecond: '%H:%M:%S.%L',
  199. second: '%H:%M:%S',
  200. minute: '%H:%M',
  201. hour: '%H:%M',
  202. day: '%Y-%m-%d',
  203. week: '%m-%d',
  204. month: '%Y-%m',
  205. year: '%Y'
  206. },
  207. title: {
  208. text: '时间'
  209. },
  210. type: 'datetime'
  211. },
  212. yAxis: {
  213. //min:0,
  214. title: {
  215. text: '人数'
  216. }
  217. },
  218. legend:{enabled:false},
  219. tooltip: {
  220. headerFormat: '<b>时间:{point.x:%Y-%m-%d %H:%M:%S}</b><br>',
  221. pointFormat : '<b>人数: {point.y:.0f} 人</b>'
  222. },
  223. plotOptions: {
  224. line:{
  225. lineWidth : 2,
  226. marker: {radius:3}
  227. }
  228. },
  229. exporting: {
  230. buttons: {
  231. contextButton: {
  232. menuItems:myMenuItem
  233. }
  234. }
  235. },
  236.  
  237. series: [{
  238. name: '在线人数',
  239. type: 'line',
  240. data: testdata
  241. }]
  242. });
  243. //console.log('addCHart() end')
  244. hisdata = loadHistoryData();
  245. if(hisdata.length>0)
  246. {
  247. var tmp=[]
  248. tmp = tmp.concat(hisdata)
  249. chart.series[0].setData(tmp)
  250. }
  251. else
  252. {
  253. chart.series[0].removePoint(0);
  254. }
  255. //console.log(chart.options.exporting.buttons.contextButton.menuItems)
  256. }
  257. function getSubTitle()
  258. {
  259. var s = $('#container > h1 > yt-formatted-string').text();
  260. if(s.length>50)
  261. {
  262. s = s.substring(0,30)+' ... '+s.substring(s.length-15);
  263. }
  264. return '<p align="center">'+s+'</p>'
  265. }
  266. function onbuttonclick()
  267. {
  268. var winNode = $("#online_chartcontent");
  269. if(winNode.is(":hidden"))
  270. {
  271. //winNode.show();
  272. winNode.fadeIn("slow");
  273. }
  274. else
  275. {
  276. //winNode.hide();
  277. winNode.fadeOut("slow");
  278. }
  279. }
  280. //显示曲线窗口
  281. function showchartwin() {
  282. //lert("准备显示弹出窗口啦!!!");
  283. //1.找到窗口对应的div节点
  284. var winNode = $("#online_chartwin");
  285. //2.让div对应的窗口显示出来
  286. //方法1,修改节点的css值,让窗口显示出来
  287. //winNode.css("display","block");
  288. //方法2,利用Jqeury的show方法
  289. //winNode.show("slow");
  290. //方法3,利用JQuery的fadeIn方法
  291. winNode.fadeIn("slow");
  292. }
  293.  
  294. //定时采集数据
  295. function onTimer_2()
  296. {
  297. var point = getNumberOfOnline()
  298. var idx = onlinedata.length;
  299. chart.series[0].addPoint(point, true, false);
  300. //activeLastPointToolip(chart);
  301. onlinedata[idx] = point
  302. GM_setValue(datakey,onlinedata);
  303. timer_2 = setTimeout(onTimer_2, aq_interval);
  304. }
  305. function activeLastPointToolip(chart) {
  306. var points = chart.series[0].points;
  307. chart.tooltip.refresh(points[points.length -1]);
  308. }
  309. function getTestData()
  310. {
  311. var currentdate = new Date().getTime();
  312. var numofonline =5000+700*Math.sin(onlinedata.length/3.14) + Math.floor(Math.random()*700+1)
  313. var result =new Array()
  314. result[0]=currentdate
  315. result[1]=numofonline
  316. if(debug_switch)
  317. {
  318. var datestr = result[0]
  319. var logmsg = datestr+" | "+result[1]
  320. console.log(logmsg);
  321. }
  322. return result
  323. }
  324.  
  325. //采集在线人数
  326. function getNumberOfOnline()
  327. {
  328. let currentdate = (new Date()).getTime();
  329. //var elm = $("div[id=info-text]>div[id=count]>ytd-video-view-count-renderer>span:first-child")
  330. let elm = $("div[id=info-container]>yt-formatted-string[id=info]>span:first-child")
  331. let numtext = elm.text()
  332. let numstr=/[0-9,]+/.exec(numtext).toString();
  333. let numofonline =parseInt(numstr.replace(',',''));
  334. if(debug_switch)
  335. {
  336. let logmsg = currentdate+" | "+numofonline+" | "+ numtext
  337. console.log(logmsg);
  338. }
  339. let result =new Array()
  340. result[0]=currentdate
  341. result[1]=numofonline
  342. return result
  343. }
  344. function listAllKeys()
  345. {
  346. var allkey = GM_listValues();
  347. for (var idx in allkey)
  348. {
  349. var data = GM_getValue(allkey[idx]);
  350. console.log(idx,allkey[idx]);
  351. console.log(data);
  352. }
  353. }
  354. function deleteAllKeys()
  355. {
  356. var allkey = GM_listValues();
  357. for (var idx in allkey)
  358. {
  359. GM_deleteValue(allkey[idx]);
  360. }
  361. }
  362. function clearAppKeys()
  363. {
  364. var allurl = GM_getValue(mainkey,[]);
  365. for (var u in allurl)
  366. {
  367. var url = allurl[u];
  368. var alldatakey = GM_getValue(url,[])
  369. for(var d in alldatakey)
  370. {
  371. GM_deleteValue(alldatakey[d])
  372. }
  373. GM_deleteValue(url)
  374. }
  375. GM_getValue(mainkey)
  376. var allkey = GM_listValues();
  377. for (var idx in allkey)
  378. {
  379. if(allkey[idx].indexOf(prefixofdatakey)==0)
  380. {
  381. GM_deleteValue(allkey[idx]);
  382. }
  383. }
  384. }
  385. function clearCurrentPageKey()
  386. {
  387. var alldatakey = GM_getValue(document.URL,[]);
  388. for(var d in alldatakey)
  389. {
  390. GM_deleteValue(alldatakey[d])
  391. }
  392. }
  393. function ceateAndAddKey()
  394. {
  395. var allurl = GM_getValue(mainkey,[])
  396. var url = document.URL;
  397. if(!allurl.includes(url))
  398. {
  399. allurl[allurl.length] = url
  400. GM_setValue(mainkey,allurl)
  401. }
  402. var idx = url.indexOf('?v=')
  403.  
  404. var timestr = new Date().toLocaleString();
  405. var dkey = prefixofdatakey+url.substring(idx+3)+timestr
  406. var keyarray = GM_getValue(url,[])
  407. keyarray[keyarray.length]=dkey
  408. GM_setValue(url,keyarray)
  409. return dkey
  410. }
  411. function loadHistoryData()
  412. {
  413. var url = document.URL;
  414. var alldatakey = GM_getValue(url)
  415. var result=[];
  416. for(var i in alldatakey)
  417. {
  418. var cur_data = GM_getValue(alldatakey[i]);
  419. if(cur_data!=undefined)
  420. {
  421. result = result.concat(cur_data);
  422. }
  423. }
  424. return result;
  425. }
  426. function hideHistoryData()
  427. {
  428. if(HistroyVisible)
  429. {
  430. //alert(HistroyVisible)
  431. var data=[];
  432. console.log('hideHistoryData 0:', data.length, hisdata.length, onlinedata.length)
  433. data = data.concat(onlinedata)
  434. console.log('hideHistoryData 1:', data.length, hisdata.length, onlinedata.length)
  435. chart.series [0].setData(data)//,true,new Highcharts.AnimationOptions({duration:1000}));
  436. console.log('hideHistoryData 2:', data.length, hisdata.length, onlinedata.length)
  437. //console.log(data)
  438. HistroyVisible = false;
  439. }
  440. }
  441. function showHistoryData()
  442. {
  443. if(!HistroyVisible)
  444. {
  445. clearTimeout(timer_2)
  446. var tmp=[];
  447. //console.log('showHistoryData 0:',tmp.length, hisdata.length, onlinedata.length)
  448. tmp = tmp.concat(hisdata)
  449. tmp = tmp.concat(onlinedata)
  450. //console.log('showHistoryData 1:',tmp.length, hisdata.length, onlinedata.length)
  451. //chart.series[0].setData([ [new Date(1970,1,1).getTime(),7296] ],false)
  452. chart.series[0].setData(tmp)
  453. timer_2 = setTimeout(onTimer_2, aq_interval);
  454. HistroyVisible = true;
  455. }
  456. }
  457. function getAbsoluteUrl(url){
  458. var a = document.createElement('A');
  459. a.href = url; // 设置相对路径给Image, 此时会发送出请求
  460. url = a.href; // 此时相对路径已经变成绝对路径
  461. return url;
  462. }
  463. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址