-
Notifications
You must be signed in to change notification settings - Fork 0
/
youtube-live-translation-collector.js
161 lines (131 loc) · 7.48 KB
/
youtube-live-translation-collector.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// ==UserScript==
// @name YouTube Live Translation Collector
// @namespace https://github.com/Gestalte/youtube-live-translation-collector
// @version 1.3.1
// @description Creates a window in YouTube's live chat window, that shows live translations and spanner comments.
// @author https://github.com/Gestalte
// @match https://www.youtube.com/live_chat*
// @grant none
// @license WTFPL
// ==/UserScript==
var specialCommentCollector = (function() {
// Final Regex: /^(\/|\u200b)?((英訳|英訳\/en|en|tr|translation|(en))\s?(:|-|\})|\[(英訳|英訳\/en|en|tr|translation|(en))\]|\((英訳|英訳\/en|en|tr|translation|(en))\)|(英訳|英訳\/en|en|tr|translation|(en))\s)/i
var basePattern = "^(\\/|\\u200b)?((§)\\s?(:|-|\\})|\\[(§)\\]|\\((§)\\)|(§)\\s)";
var thingsToMatch = ["英訳", "英訳\\/en", "en", "tr", "translation", "\(en\)"];
var pattern = basePattern.replace(/§/g, thingsToMatch.join('|'));
var chat = document.querySelector('#chat');
var itemScroller = chat.querySelector('#item-scroller');
var chatItems = document.querySelector('#items.style-scope.yt-live-chat-item-list-renderer');
return {
setupWindow: function() {
// This creates a window at the top of the live chat window
// where translations and spanner comments will appear.
let sticky = document.createElement('div');
let ticker = document.getElementById('ticker');
sticky.id = 'sticky';
sticky.style.maxHeight = '30%';
sticky.style.overflow = 'auto';
ticker.parentNode.insertBefore(sticky, ticker);
},
init: function() {
// stick items that exist on load
chatItems.querySelectorAll('yt-live-chat-text-message-renderer').forEach(specialCommentCollector.identify);
// stick mutations
let observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach(specialCommentCollector.identify);
});
});
// start the observer
observer.observe(chatItems, { childList: true });
},
getSticky: function() {
return document.getElementById('sticky');
},
isTranslation: function(txt) {
return RegExp(pattern, 'i').test(txt)
},
isSpecial: function(author) {
if (author === null) {
return false;
}
return author.classList
? (author.classList.contains('moderator') || author.classList.contains('owner'))
: author.content.author.classes.indexOf('moderator') > -1;
},
identify: function(node) {
let authorElement = node.querySelector('#author-name');
let messageElement = node.querySelector('#message');
if (messageElement != null && authorElement != null) {
let message = messageElement.innerText.trim();
if (specialCommentCollector.isSpecial(authorElement) || specialCommentCollector.isTranslation(message)) {
specialCommentCollector.stickComment(node);
}
}
},
stickComment: function(node) {
/*
NOTE: The ideal code would look like this:
var clone = node.cloneNode(true);
sticky.appendChild(clone); // This is where the screw-up happens, the structure gets added, but not the content.
itemScroller.scrollTop = itemScroller.scrollHeight;
sticky.scrollTop = sticky.scrollHeight;
*/
var containerDiv = document.createElement("div");
containerDiv.setAttribute("style", "padding: 4px 24px; display: flex;");
var newAuthorImage = document.createElement("img");
newAuthorImage.src = node.querySelector("#img").src
newAuthorImage.setAttribute("width", "24");
newAuthorImage.setAttribute("height", "24");
newAuthorImage.setAttribute("style", "border-radius: 50%; margin-right: var(--yt-live-chat-author-photo-margin-right,16px);");
containerDiv.appendChild(newAuthorImage);
var contentDiv = document.createElement("div");
var newTimestamp = document.createElement("span");
var timeStamp = node.querySelector("#timestamp");
newTimestamp.innerHTML = timeStamp.innerHTML;
newTimestamp.classList = timeStamp.classList;
newTimestamp.id = "timestamp";
contentDiv.appendChild(newTimestamp);
var authorDiv = document.createElement("div");
authorDiv.setAttribute("style", "display: inline-flex; margin-right: var(--yt-live-chat-author-chip-margin-right,8px);");
var newAuthorName = document.createElement("span");
var authorName = node.querySelector("#author-name");
newAuthorName.innerHTML = authorName.innerHTML;
newAuthorName.classList = authorName.classList;
newAuthorName.id = "author-name";
authorDiv.appendChild(newAuthorName);
var chatBadges = node.querySelector("#chat-badges");
var badgeImages = chatBadges.getElementsByTagName("img");
if (chatBadges.firstChild != null && chatBadges.firstChild.type === "moderator"){
var spannerDiv = document.createElement("div");
spannerDiv.setAttribute("style", "width: 16px; height: 16px; display: block; fill: currentcolor; color: var(--yt-live-chat-moderator-color,#5e84f1);");
spannerDiv.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" focusable="false" style="pointer-events: none; display: inherit; width: 100%; height: 100%;"><path d="M9.64589146,7.05569719 C9.83346524,6.562372 9.93617022,6.02722257 9.93617022,5.46808511 C9.93617022,3.00042984 7.93574038,1 5.46808511,1 C4.90894765,1 4.37379823,1.10270499 3.88047304,1.29027875 L6.95744681,4.36725249 L4.36725255,6.95744681 L1.29027875,3.88047305 C1.10270498,4.37379824 1,4.90894766 1,5.46808511 C1,7.93574038 3.00042984,9.93617022 5.46808511,9.93617022 C6.02722256,9.93617022 6.56237198,9.83346524 7.05569716,9.64589147 L12.4098057,15 L15,12.4098057 L9.64589146,7.05569719 Z"></path></svg>';
authorDiv.appendChild(spannerDiv);
}
for (let i = 0; i < badgeImages.length; i++) {
var src = badgeImages[i].src;
var badgeImg = document.createElement("img");
badgeImg.src = src;
badgeImg.setAttribute("style", "width: 16px; height: 16px; margin: 0 0 0 2px;");
authorDiv.appendChild(badgeImg);
}
contentDiv.appendChild(authorDiv);
var newMessage = document.createElement("span");
var message = node.querySelector("#message");
newMessage.innerHTML = message.innerHTML;
newMessage.classList = message.classList;
newMessage.id = "message";
contentDiv.appendChild(newMessage);
containerDiv.appendChild(contentDiv);
sticky.appendChild(containerDiv);
itemScroller.scrollTop = itemScroller.scrollHeight;
sticky.scrollTop = sticky.scrollHeight;
}
}
}());
var sticky = '';
(function() {
specialCommentCollector.setupWindow();
sticky = specialCommentCollector.getSticky();
window.top.addEventListener('load', specialCommentCollector.init());
})();