@xpadev-net/niconicomments 0.2.6 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +4 -5
  2. package/dist/bundle.js +192 -67
  3. package/package.json +9 -6
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # [niconicomments](https://xpadev.net/niconicomments/)
2
2
  [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/xpadev-net/niconicomments/blob/master/LICENSE)
3
- ニコニコ動画の公式プレイヤーに多少の互換性を持つコメント描画ライブラリです
4
- This is a comment drawing library that is somewhat compatible with the official Nico Nico Douga player.
3
+ ニコニコ動画の公式プレイヤー互換の高パフォーマンスなコメント描画ライブラリ
4
+ High peformance High compatibility comment drawing library
5
5
  Reference: https://xpadev-net.github.io/niconicomments/
6
6
  Github: https://github.com/xpadev-net/niconicomments
7
7
  npm: https://www.npmjs.com/package/@xpadev-net/niconicomments
@@ -27,6 +27,5 @@ setInterval(() => niconiComments.drawCanvas(Math.floor(video.currentTime * 100))
27
27
  ```
28
28
 
29
29
  ## Sample
30
- [レッツゴー!陰陽師](https://xpadev.net/niconicomments/sample.html)
31
- [レッツゴー!陰陽師(CodePen)](https://codepen.io/xpadev-net/pen/mdBdQmX)
32
- [ニコニコ動画流星群](https://xpadev.net/niconicomments/ryuuseigun.html)
30
+ [サンプル](https://xpadev.net/niconicomments/sample.html)
31
+ [CodePen](https://codepen.io/xpadev-net/pen/mdBdQmX)
package/dist/bundle.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- niconicomments.js v0.2.6
2
+ niconicomments.js v0.2.9
3
3
  (c) 2021 xpadev-net https://xpadev.net
4
4
  Released under the MIT License.
5
5
  */
@@ -9,6 +9,22 @@
9
9
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.NiconiComments = factory());
10
10
  })(this, (function () { 'use strict';
11
11
 
12
+ var _assign = function __assign() {
13
+ _assign = Object.assign || function __assign(t) {
14
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
15
+ s = arguments[i];
16
+
17
+ for (var p in s) {
18
+ if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
19
+ }
20
+ }
21
+
22
+ return t;
23
+ };
24
+
25
+ return _assign.apply(this, arguments);
26
+ };
27
+
12
28
  var NiconiComments = (function () {
13
29
  function NiconiComments(canvas, data, options) {
14
30
  if (options === void 0) { options = {
@@ -22,7 +38,10 @@
22
38
  }; }
23
39
  var _this = this;
24
40
  this.canvas = canvas;
25
- this.context = canvas.getContext("2d");
41
+ var context = canvas.getContext("2d");
42
+ if (!context)
43
+ throw new Error("Fail to get CanvasRenderingContext2D");
44
+ this.context = context;
26
45
  this.context.strokeStyle = "rgba(0,0,0,0.7)";
27
46
  this.context.textAlign = "start";
28
47
  this.context.textBaseline = "alphabetic";
@@ -46,11 +65,11 @@
46
65
  this.doubleResizeMaxWidth = {
47
66
  full: {
48
67
  legacy: 3020,
49
- "default": 3220
68
+ default: 3220
50
69
  },
51
70
  normal: {
52
71
  legacy: 2540,
53
- "default": 2740
72
+ default: 2740
54
73
  }
55
74
  };
56
75
  var parsedData = options.formatted ? data : this.parseData(data);
@@ -59,17 +78,18 @@
59
78
  this.showFPS = options.showFPS;
60
79
  this.showCommentCount = options.showCommentCount;
61
80
  this.timeline = {};
62
- this.nicoScripts = { "reverse": [], "default": [] };
81
+ this.nicoScripts = { reverse: [], default: [], replace: [], ban: [] };
63
82
  this.collision_right = {};
64
83
  this.collision_left = {};
65
84
  this.collision_ue = {};
66
85
  this.collision_shita = {};
86
+ this.data = [];
67
87
  this.lastVpos = -1;
68
88
  this.useLegacy = options.useLegacy;
69
89
  this.preRendering(parsedData, options.drawAllImageOnLoad);
70
90
  this.fpsCount = 0;
71
91
  this.fps = 0;
72
- this.fpsClock = window.setInterval(function () {
92
+ window.setInterval(function () {
73
93
  _this.fps = _this.fpsCount * 2;
74
94
  _this.fpsCount = 0;
75
95
  }, 500);
@@ -78,8 +98,11 @@
78
98
  var data_ = [];
79
99
  for (var i = 0; i < data.length; i++) {
80
100
  for (var key in data[i]) {
81
- var value = data[i][key];
82
- if (key === "chat" && value["deleted"] !== 1 && !value["content"].startsWith("/")) {
101
+ var val = data[i];
102
+ if (!val)
103
+ continue;
104
+ var value = val[key];
105
+ if (isApiChat(value) && value["deleted"] !== 1) {
83
106
  var tmpParam = {
84
107
  "id": value["no"],
85
108
  "vpos": value["vpos"],
@@ -93,6 +116,9 @@
93
116
  if (value["mail"]) {
94
117
  tmpParam["mail"] = value["mail"].split(/[\s ]/g);
95
118
  }
119
+ if (value["content"].startsWith("/") && !value["user_id"]) {
120
+ tmpParam["mail"].push("invisible");
121
+ }
96
122
  data_.push(tmpParam);
97
123
  }
98
124
  }
@@ -115,9 +141,7 @@
115
141
  return data_;
116
142
  };
117
143
  NiconiComments.prototype.preRendering = function (rawData, drawAll) {
118
- rawData = this.getFont(rawData);
119
- rawData = this.getCommentSize(rawData);
120
- var parsedData = this.getCommentPos(rawData);
144
+ var parsedData = this.getCommentPos(this.getCommentSize(this.getFont(rawData)));
121
145
  this.data = this.sortComment(parsedData);
122
146
  if (drawAll) {
123
147
  for (var i in parsedData) {
@@ -126,27 +150,19 @@
126
150
  }
127
151
  };
128
152
  NiconiComments.prototype.getFont = function (parsedData) {
153
+ var result = [];
129
154
  for (var i in parsedData) {
130
- var comment = parsedData[i];
131
- var command = this.parseCommandAndNicoscript(comment);
132
- parsedData[i].loc = command.loc;
133
- parsedData[i].size = command.size;
134
- parsedData[i].fontSize = command.fontSize;
135
- parsedData[i].font = command.font;
136
- parsedData[i].color = command.color;
137
- parsedData[i].full = command.full;
138
- parsedData[i].ender = command.ender;
139
- parsedData[i]._live = command._live;
140
- parsedData[i].long = command.long;
141
- parsedData[i].invisible = command.invisible;
142
- parsedData[i].content = parsedData[i].content.replaceAll("\t", "\u2003\u2003");
143
- if (parsedData[i].content.match(/\s{4,}/))
144
- console.log(parsedData[i], parsedData[i].content.replace(/\t/g, "\u2003\u2003"));
155
+ var value = parsedData[i];
156
+ if (!value)
157
+ continue;
158
+ value.content = value.content.replace(/\t/g, "\u2003\u2003");
159
+ result[i] = this.parseCommandAndNicoscript(value);
145
160
  }
146
- return parsedData;
161
+ return result;
147
162
  };
148
163
  NiconiComments.prototype.getCommentSize = function (parsedData) {
149
164
  var tmpData = groupBy(parsedData, "font", "fontSize");
165
+ var result = [];
150
166
  for (var i in tmpData) {
151
167
  for (var j in tmpData[i]) {
152
168
  this.context.font = parseFont(i, j, this.useLegacy);
@@ -156,24 +172,26 @@
156
172
  continue;
157
173
  }
158
174
  var measure = this.measureText(comment);
159
- parsedData[comment.index].height = measure.height;
160
- parsedData[comment.index].width = measure.width;
161
- parsedData[comment.index].width_max = measure.width_max;
162
- parsedData[comment.index].width_min = measure.width_min;
175
+ var size = parsedData[comment.index];
176
+ size.height = measure.height;
177
+ size.width = measure.width;
178
+ size.width_max = measure.width_max;
179
+ size.width_min = measure.width_min;
163
180
  if (measure.resized) {
164
- parsedData[comment.index].fontSize = measure.fontSize;
181
+ size.fontSize = measure.fontSize;
165
182
  this.context.font = parseFont(i, j, this.useLegacy);
166
183
  }
184
+ result[comment.index] = size;
167
185
  }
168
186
  }
169
187
  }
170
- return parsedData;
188
+ return result;
171
189
  };
172
190
  NiconiComments.prototype.getCommentPos = function (parsedData) {
173
191
  var data = parsedData;
174
192
  for (var i in data) {
175
193
  var comment = data[i];
176
- if (comment.invisible) {
194
+ if (!comment || comment.invisible) {
177
195
  continue;
178
196
  }
179
197
  for (var j = 0; j < 500; j++) {
@@ -231,7 +249,7 @@
231
249
  break;
232
250
  }
233
251
  }
234
- if (left_pos <= 40 && is_break === false) {
252
+ if (left_pos <= 40 && !is_break) {
235
253
  for (var k in this.collision_left[vpos]) {
236
254
  var l = this.collision_left[vpos][k];
237
255
  if ((posY < data[l].posY + data[l].height && posY + comment.height > data[l].posY) && data[l].owner === comment.owner) {
@@ -328,6 +346,8 @@
328
346
  };
329
347
  NiconiComments.prototype.sortComment = function (parsedData) {
330
348
  for (var vpos in this.timeline) {
349
+ if (!this.timeline[vpos])
350
+ continue;
331
351
  this.timeline[vpos].sort(function (a, b) {
332
352
  var A = parsedData[a];
333
353
  var B = parsedData[b];
@@ -350,19 +370,19 @@
350
370
  if (comment.size === "big" && lines.length > 2) {
351
371
  comment.fontSize = this.fontSize.big.resized;
352
372
  comment.resized = true;
353
- comment.tateRisized = true;
373
+ comment.tateresized = true;
354
374
  this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
355
375
  }
356
376
  else if (comment.size === "medium" && lines.length > 4) {
357
377
  comment.fontSize = this.fontSize.medium.resized;
358
378
  comment.resized = true;
359
- comment.tateRisized = true;
379
+ comment.tateresized = true;
360
380
  this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
361
381
  }
362
382
  else if (comment.size === "small" && lines.length > 6) {
363
383
  comment.fontSize = this.fontSize.small.resized;
364
384
  comment.resized = true;
365
- comment.tateRisized = true;
385
+ comment.tateresized = true;
366
386
  this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
367
387
  }
368
388
  }
@@ -374,7 +394,7 @@
374
394
  width_max = Math.max.apply(Math, width_arr);
375
395
  width_min = Math.min.apply(Math, width_arr);
376
396
  height = (comment.fontSize * (1 + this.commentYPaddingTop) * lines.length) + (this.commentYMarginBottom * comment.fontSize);
377
- if (comment.loc !== "naka" && !comment.tateRisized) {
397
+ if (comment.loc !== "naka" && !comment.tateresized) {
378
398
  if (comment.full && width_max > 1920) {
379
399
  comment.fontSize -= 2;
380
400
  comment.resized = true;
@@ -390,14 +410,15 @@
390
410
  return this.measureText(comment);
391
411
  }
392
412
  }
393
- else if (comment.loc !== "naka" && comment.tateRisized && (comment.full && width_max > 1920 || !comment.full && width_max > 1440) && !comment.yokoResized) {
394
- comment.fontSize = this.fontSize[comment.size]["default"];
413
+ else if (comment.loc !== "naka" && comment.tateresized && (comment.full && width_max > 2120 || !comment.full && width_max > 1440) && !comment.yokoResized) {
414
+ comment.fontSize = this.fontSize[comment.size].default;
395
415
  comment.resized = true;
396
416
  comment.yokoResized = true;
397
417
  this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
418
+ console.log(comment);
398
419
  return this.measureText(comment);
399
420
  }
400
- else if (comment.loc !== "naka" && comment.tateRisized && comment.yokoResized) {
421
+ else if (comment.loc !== "naka" && comment.tateresized && comment.yokoResized) {
401
422
  if (comment.full && width_max > this.doubleResizeMaxWidth.full[this.useLegacy ? "legacy" : "default"]) {
402
423
  comment.fontSize -= 1;
403
424
  this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
@@ -429,6 +450,12 @@
429
450
  reverse = true;
430
451
  }
431
452
  }
453
+ for (var i in this.nicoScripts.ban) {
454
+ var range = this.nicoScripts.ban[i];
455
+ if (range.start < vpos && vpos < range.end) {
456
+ return;
457
+ }
458
+ }
432
459
  var posX = (1920 - comment.width_max) / 2, posY = comment.posY;
433
460
  if (comment.loc === "naka") {
434
461
  if (reverse) {
@@ -441,17 +468,20 @@
441
468
  else if (comment.loc === "shita") {
442
469
  posY = 1080 - comment.posY - comment.height;
443
470
  }
444
- this.context.drawImage(comment.image, posX, posY);
471
+ if (comment.image)
472
+ this.context.drawImage(comment.image, posX, posY);
445
473
  };
446
474
  NiconiComments.prototype.getTextImage = function (i) {
447
475
  var value = this.data[i];
448
- if (value.invisible) {
476
+ if (!value || value.invisible) {
449
477
  return;
450
478
  }
451
479
  var image = document.createElement("canvas");
452
480
  image.width = value.width_max;
453
481
  image.height = value.height;
454
482
  var context = image.getContext("2d");
483
+ if (!context)
484
+ throw new Error("Fail to get CanvasRenderingContext2D");
455
485
  context.strokeStyle = "rgba(0,0,0,0.7)";
456
486
  context.textAlign = "start";
457
487
  context.textBaseline = "alphabetic";
@@ -518,11 +548,11 @@
518
548
  switch (command) {
519
549
  case "big":
520
550
  size = "big";
521
- fontSize = this.fontSize.big["default"];
551
+ fontSize = this.fontSize.big.default;
522
552
  break;
523
553
  case "small":
524
554
  size = "small";
525
- fontSize = this.fontSize.small["default"];
555
+ fontSize = this.fontSize.small.default;
526
556
  break;
527
557
  }
528
558
  }
@@ -635,7 +665,7 @@
635
665
  if (nicoscript) {
636
666
  switch (nicoscript[1]) {
637
667
  case "デフォルト":
638
- this.nicoScripts["default"].push({
668
+ this.nicoScripts.default.push({
639
669
  start: comment.vpos,
640
670
  long: data.long === null ? null : Math.floor(data.long * 100),
641
671
  color: data.color,
@@ -646,6 +676,8 @@
646
676
  break;
647
677
  case "逆":
648
678
  var reverse = comment.content.match(/^@逆 ?(全|コメ|投コメ)?/);
679
+ if (!reverse)
680
+ reverse = [];
649
681
  if (!reverse[1]) {
650
682
  reverse[1] = "全";
651
683
  }
@@ -653,31 +685,112 @@
653
685
  data.long = 30;
654
686
  }
655
687
  this.nicoScripts.reverse.push({
656
- "start": comment.vpos,
657
- "end": comment.vpos + (data.long * 100),
658
- "target": reverse[1]
688
+ start: comment.vpos,
689
+ end: comment.vpos + (data.long * 100),
690
+ target: reverse[1]
691
+ });
692
+ break;
693
+ case "コメント禁止":
694
+ if (data.long === null) {
695
+ data.long = 30;
696
+ }
697
+ this.nicoScripts.reverse.push({
698
+ start: comment.vpos,
699
+ end: comment.vpos + (data.long * 100),
700
+ });
701
+ break;
702
+ case "置換":
703
+ var content = comment.content.split(""), quote = "", last_i = "", string = "", result = [];
704
+ for (var _i = 0, _a = content.slice(4); _i < _a.length; _i++) {
705
+ var i = _a[_i];
706
+ if (i.match(/["'「]/) && quote === "") {
707
+ quote = i;
708
+ }
709
+ else if (i.match(/["']/) && quote === i && last_i !== "\\") {
710
+ result.push(replaceAll(string, "\\n", "\n"));
711
+ quote = "";
712
+ string = "";
713
+ }
714
+ else if (i.match(/」/) && quote === "「") {
715
+ result.push(string);
716
+ quote = "";
717
+ string = "";
718
+ }
719
+ else if (quote === "" && i.match(/[\s ]/)) {
720
+ if (string) {
721
+ result.push(string);
722
+ string = "";
723
+ }
724
+ }
725
+ else {
726
+ string += i;
727
+ }
728
+ last_i = i;
729
+ }
730
+ result.push(string);
731
+ this.nicoScripts.replace.push({
732
+ start: comment.vpos,
733
+ long: data.long === null ? null : Math.floor(data.long * 100),
734
+ keyword: result[0],
735
+ replace: result[1] || "",
736
+ range: result[2] || "単",
737
+ target: result[3] || "コメ",
738
+ condition: result[4] || "部分一致",
739
+ color: data.color,
740
+ size: data.size,
741
+ font: data.font,
742
+ loc: data.loc
659
743
  });
660
744
  break;
661
745
  }
662
746
  data.invisible = true;
663
747
  }
664
748
  var color = "#FFFFFF", size = "medium", font = "defont", loc = "naka";
665
- for (var i in this.nicoScripts["default"]) {
666
- if (this.nicoScripts["default"][i].long !== null && this.nicoScripts["default"][i].start + this.nicoScripts["default"][i].long < comment.vpos) {
667
- this.nicoScripts["default"] = this.nicoScripts["default"].splice(Number(i), 1);
749
+ for (var i in this.nicoScripts.default) {
750
+ if (this.nicoScripts.default[i].long !== null && this.nicoScripts.default[i].start + this.nicoScripts.default[i].long < comment.vpos) {
751
+ this.nicoScripts.default = this.nicoScripts.default.splice(Number(i), 1);
668
752
  continue;
669
753
  }
670
- if (this.nicoScripts["default"][i].loc) {
671
- loc = this.nicoScripts["default"][i].loc;
754
+ if (this.nicoScripts.default[i].loc) {
755
+ loc = this.nicoScripts.default[i].loc;
756
+ }
757
+ if (this.nicoScripts.default[i].color) {
758
+ color = this.nicoScripts.default[i].color;
759
+ }
760
+ if (this.nicoScripts.default[i].size) {
761
+ size = this.nicoScripts.default[i].size;
672
762
  }
673
- if (this.nicoScripts["default"][i].color) {
674
- color = this.nicoScripts["default"][i].color;
763
+ if (this.nicoScripts.default[i].font) {
764
+ font = this.nicoScripts.default[i].font;
675
765
  }
676
- if (this.nicoScripts["default"][i].size) {
677
- size = this.nicoScripts["default"][i].size;
766
+ }
767
+ for (var i in this.nicoScripts.replace) {
768
+ if (this.nicoScripts.replace[i].long !== null && this.nicoScripts.replace[i].start + this.nicoScripts.replace[i].long < comment.vpos) {
769
+ this.nicoScripts.default = this.nicoScripts.default.splice(Number(i), 1);
770
+ continue;
678
771
  }
679
- if (this.nicoScripts["default"][i].font) {
680
- font = this.nicoScripts["default"][i].font;
772
+ var item = this.nicoScripts.replace[i];
773
+ if ((item.target === "コメ" && comment.owner) || (item.target === "投コメ" && !comment.owner) || (item.target === "含まない" && comment.owner))
774
+ continue;
775
+ if ((item.condition === "完全一致" && comment.content === item.keyword) || (item.condition === "部分一致" && comment.content.indexOf(item.keyword) !== -1)) {
776
+ if (item.range === "単") {
777
+ comment.content = replaceAll(comment.content, item.keyword, item.replace);
778
+ }
779
+ else {
780
+ comment.content = item.replace;
781
+ }
782
+ if (item.loc) {
783
+ loc = item.loc;
784
+ }
785
+ if (item.color) {
786
+ color = item.color;
787
+ }
788
+ if (item.size) {
789
+ size = item.size;
790
+ }
791
+ if (item.font) {
792
+ font = item.font;
793
+ }
681
794
  }
682
795
  }
683
796
  if (!data.loc) {
@@ -688,7 +801,7 @@
688
801
  }
689
802
  if (!data.size) {
690
803
  data.size = size;
691
- data.fontSize = this.fontSize[data.size]["default"];
804
+ data.fontSize = this.fontSize[data.size].default;
692
805
  }
693
806
  if (!data.font) {
694
807
  data.font = font;
@@ -701,7 +814,7 @@
701
814
  data.long = Math.floor(data.long * 100);
702
815
  }
703
816
  }
704
- return data;
817
+ return _assign(_assign({}, comment), data);
705
818
  };
706
819
  NiconiComments.prototype.drawCanvas = function (vpos) {
707
820
  if (this.lastVpos === vpos)
@@ -722,13 +835,14 @@
722
835
  this.context.drawImage(this.video, offsetX, offsetY, this.video.videoWidth * scale, this.video.videoHeight * scale);
723
836
  }
724
837
  if (this.timeline[vpos]) {
725
- for (var index in this.timeline[vpos]) {
726
- var comment = this.data[this.timeline[vpos][index]];
727
- if (comment.invisible) {
838
+ for (var i in this.timeline[vpos]) {
839
+ var index = this.timeline[vpos][Number(i)];
840
+ var comment = this.data[index];
841
+ if (!comment || comment.invisible) {
728
842
  continue;
729
843
  }
730
844
  if (!comment.image) {
731
- this.getTextImage(this.timeline[vpos][index]);
845
+ this.getTextImage(index);
732
846
  }
733
847
  this.drawText(comment, vpos);
734
848
  }
@@ -804,6 +918,17 @@
804
918
  return parseInt(str, 16);
805
919
  });
806
920
  };
921
+ var replaceAll = function (string, target, replace) {
922
+ var count = 0;
923
+ while (string.indexOf(target) !== -1 && count < 100) {
924
+ string = string.replace(target, replace);
925
+ count++;
926
+ }
927
+ return string;
928
+ };
929
+ var isApiChat = function (item) {
930
+ return !!item.chat;
931
+ };
807
932
 
808
933
  return NiconiComments;
809
934
 
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "@xpadev-net/niconicomments",
3
- "version": "0.2.6",
3
+ "version": "0.2.9",
4
4
  "description": "NiconiComments is a comment drawing library that is somewhat compatible with the official Nico Nico Douga player.",
5
5
  "main": "dist/bundle.js",
6
+ "types": "dist/dts/main.d.ts",
6
7
  "scripts": {
7
8
  "test": "echo \"Error: no test specified\" && exit 1",
8
9
  "build": "rollup -c rollup.config.js",
9
10
  "watch": "rollup -c rollup.config.js -w",
10
11
  "typedoc": "typedoc --options tsdoc.json --out ./docs/type/ ./src/main.ts",
11
- "prepublishOnly": "npm run build"
12
+ "tsc": "tsc --emitDeclarationOnly",
13
+ "prepublishOnly": "npm run tsc&&npm run build&&npm run typedoc"
12
14
  },
13
15
  "repository": {
14
16
  "type": "git",
@@ -28,10 +30,11 @@
28
30
  "license": "MIT",
29
31
  "devDependencies": {
30
32
  "@rollup/plugin-babel": "^5.3.0",
31
- "@rollup/plugin-typescript": "^8.3.1",
32
- "rollup": "~2.66",
33
- "typedoc": "^0.22.13",
33
+ "@rollup/plugin-typescript": "^8.3.2",
34
+ "rollup": "^2.75.6",
35
+ "typedoc": "^0.22.17",
34
36
  "typedoc-plugin-missing-exports": "^0.22.6",
35
- "typescript": "^4.6.3"
37
+ "typescript": "^4.7.3",
38
+ "@babel/preset-env": "^7.18.2"
36
39
  }
37
40
  }