Skip to content

Instantly share code, notes, and snippets.

@crapier
Last active October 17, 2015 19:50
Show Gist options
  • Save crapier/3349a3f70fbcf67aeeb4 to your computer and use it in GitHub Desktop.
Save crapier/3349a3f70fbcf67aeeb4 to your computer and use it in GitHub Desktop.
This userscript for YouTube adds the ability to count votes (or anything in the livestream chat) on livestreams.

YouTube Livestream Vote Counter

This userscript for YouTube adds the ability to count votes (or anything in the livestream chat) on livestreams.

Usage

  • Refresh page on livestream video
    • A menu with a text field and button should appear below the comments
  • Wait for the comment section to finish loading (if it hasn't started showing comments yet)
  • Enter options in menu text field as a comma seperated list
    • Each option can be in one of two forms
      • A list of strings to check for seperated by a + sign (ex. hi, or hi+hello)
      • A regex expression inside of // (ex. /regex/)
    • All comparrisons are case insensitive
  • When you press enter or hit the update button a table will appear below the menu and will count any comment that includes the options
    • You may change the options and update the list at any time
    • When you update, old option counts will be maintained (order doesn't matter)

Installation

Only tested in FireFox with Greasemonkey (should work with Tapermonkey and Chrome).

  • Get Greasemonkey for FireFox if you don't already have it. link
  • Click on the Raw link for YouTube Livestream Vote Counter.user.js below, wait 5 seconds, and click install to add the script.

Feel free to borrow, modify, and/or steal any of the script for your own needs. No credit is required.

// ==UserScript==
// @name YouTube Livestream Vote Counter
// @namespace 9001
// @description Counts Votes
// @include https://www.youtube.com/*
// @version 1.3.3
// @downloadURL https://gist.github.com/crapier/3349a3f70fbcf67aeeb4/raw/YouTube%20Livestream%20Vote%20Counter.user.js
// @grant unsafeWindow
// ==/UserScript==
// Prevent script from running in frames and iframes
if (window.top != window.self) {
console.log("YLVC: Not running on an iframe");
return;
}
var injection = function() {
// prevent script from loading on non livestream pages
console.log("YLVC: Checking Page");
if (document.getElementsByClassName("concurrent-viewers watch-view-count").length > 0) {
console.log("YLVC: Loading Functions");
// current options and counts
var vote_options = [];
var vote_counts = [];
// menu to input options
var menu = document.createElement("div");
menu.innerHTML = "<b>YouTube Livestream Vote Counter</b><br><input type='text' name='options' id='vote_options'><input type='submit' value='Update' id='update_button'><br>";
menu.style.cssText = "background-color:white;padding:10px";
// table that displays counts
var counter_div = document.createElement("div");
counter_div.innerHTML = "<table id='counter_table' style='width:100%;border:1px solid gray;'></table>";
counter_div.style.cssText = "background-color:white;padding:10px;margin-bottom:10px";
// mutation observer to check new comments
var observer;
var observing = false;
// config for mutation observer
var config = { attributes: true, childList: true, characterData: true };
var vote_count = 0;
var comment_count = 0;
// update the table on the page
var print_counters = function() {
var table = document.getElementById("counter_table");
// clear table
table.innerHTML = "";
// add column headers
var row_head = table.insertRow(0);
var td1_head = row_head.insertCell(0);
td1_head.innerHTML = "<b>Option</b>";
td1_head.style.cssText = "padding:5px;border:1px solid gray"
var td2_head = row_head.insertCell(1);
td2_head.innerHTML = "<b>Votes</b>";
td2_head.style.cssText = "padding:5px;border:1px solid gray"
// Add each row
for (var i = 0; i < vote_options.length; i++) {
var row = table.insertRow(i+1);
var td1 = row.insertCell(0);
td1.innerHTML = vote_options[i];
td1.style.cssText = "padding:5px;border:1px solid gray"
var td2 = row.insertCell(1);
td2.innerHTML = "" + vote_counts[i];
td2.style.cssText = "padding:5px;border:1px solid gray"
}
i++;
// add extra total information
var row_votes = table.insertRow(i++);
var td1_votes = row_votes.insertCell(0);
td1_votes.innerHTML = "<b>Total Votes</b>";
td1_votes.style.cssText = "padding:5px;border:1px solid gray"
var td2_votes = row_votes.insertCell(1);
td2_votes.innerHTML = "" + vote_count;
td2_votes.style.cssText = "padding:5px;border:1px solid gray"
var row_comments = table.insertRow(i++);
var td1_comments = row_comments.insertCell(0);
td1_comments.innerHTML = "<b>Total Comments</b>";
td1_comments.style.cssText = "padding:5px;border:1px solid gray"
var td2_comments = row_comments.insertCell(1);
td2_comments.innerHTML = "" + comment_count;
td2_comments.style.cssText = "padding:5px;border:1px solid gray"
};
// update options to count (keeps old counts for any option that is the same)
var set_option = function(options) {
var vote_options_old = vote_options;
var vote_couts_old = vote_counts;
vote_options = options.replace(/,\s+/g, ",").split(",");
// remove empty strings
while (vote_options.indexOf("") >= 0) {
vote_options.splice(vote_options.indexOf(""), 1);
}
var only_new = true;
// clear counts
vote_counts = [];
// initialize counts to 0
for (var i = 0; i < vote_options.length; i++) {
vote_counts[i] = 0;
}
// copy old values for options that remain the same
for (i = 0; i < vote_options.length; i++) {
var new_option_split = [];
// interpret as regular expression, don't split
if (vote_options[i].length > 3 && vote_options[i][0] == "/" && vote_options[i][vote_options[i].length-1] == "/") {
new_option_split[0] = vote_options[i];
}
else {
new_option_split = vote_options[i].split("+");
}
for (var j = 0; j < new_option_split.length; j++) {
for (var k = 0; k < vote_options_old.length; k++) {
var old_option_split = [];
// interpret as regular expression, don't split
if (vote_options_old[k].length > 3 && vote_options_old[k][0] == "/" && vote_options_old[k][vote_options_old[k].length-1] == "/") {
old_option_split[0] = vote_options_old[k];
}
else {
old_option_split = vote_options_old[k].split("+");
}
for (var l = 0; l < old_option_split.length; l++) {
if (old_option_split[l].toLowerCase() == new_option_split[j].toLowerCase()) {
// options are the same so update counted value
vote_counts[i] += vote_couts_old[k];
// find all options from this pair that are the same
var trim_old_option_split = [];
for (var m = 0; m < old_option_split.length; m++) {
var found = false;
for (var n = 0; n < new_option_split.length; n++) {
if (old_option_split[m].toLowerCase() == new_option_split[n].toLowerCase()) {
found = true;
break;
}
}
if (!found) {
trim_old_option_split[trim_old_option_split.length] = old_option_split[m];
}
}
// create new trimmed option
var trim_option = "";
for (m = 0; m < trim_old_option_split.length; m++) {
trim_option += trim_old_option_split[m] + "+";
}
// remove extra last "+"
if (trim_option.length > 0) {
trim_option = trim_option.substring(0, trim_option.length - 1);
}
// modify old options to prevent double counting
vote_options_old[k] = trim_option;
// don't compare the rest of the old split
break;
}
}
}
}
}
//update counted votes
vote_count = 0;
for (i = 0; i < vote_counts.length; i++) {
vote_count += vote_counts[i];
}
// start observer if its not already started
if (!observing) {
observing = true;
start_mutation_observer();
}
// update table
print_counters();
};
// check a message against all options
var check_text = function(text) {
for (var i = 0; i < vote_options.length; i++) {
// interpret as regular expression if surrounded by /string/
if (vote_options[i].length > 3 && vote_options[i][0] == "/" && vote_options[i][vote_options[i].length-1] == "/") {
var reg_exp = new RegExp(vote_options[i].substring(1, vote_options[i].length-2), "i");
if (reg_exp.test(text)) {
vote_counts[i]++;
vote_count++;
}
}
// else just interpret as list of things to check
else {
var options_split = vote_options[i].split("+");
for (var j = 0; j < options_split.length; j++) {
if (text.toLowerCase().indexOf(options_split[j].toLowerCase()) >= 0) {
vote_counts[i]++;
vote_count++;
break;
}
}
}
}
};
// check new comments from muation events
var check_new_comments = function(mutations) {
for (var i = 0; i < mutations.length; i++) {
for (var j = 0; j < mutations[i].addedNodes.length; j++) {
check_text(mutations[i].addedNodes[j].childNodes[5].childNodes[5].textContent);
comment_count++;
}
}
print_counters();
}
// use this function for the observer callback
observer = new MutationObserver(check_new_comments);
var start_mutation_observer = function() {
// the comment div for the mutation observer
var comments = document.getElementById("all-comments");
// start the observer
console.log("YLVC: Starting Mutation Observer")
observer.observe(comments, config);
}
console.log("YLVC: Loading Menu");
// add elements to the page, inbetween comments and suggested videos
document.getElementById("watch7-sidebar").insertBefore(menu, document.getElementById("watch7-sidebar-contents"));
document.getElementById("watch7-sidebar").insertBefore(counter_div, document.getElementById("watch7-sidebar-contents"));
// set update button function
document.getElementById("update_button").onclick = function() {
set_option(document.getElementById("vote_options").value);
};
// pressing enter also clicks the update button
document.getElementById("vote_options").onkeyup = function(key) {
if (key.keyCode == 13) {
document.getElementById("update_button").click();
}
}
}
}
// create script to inject onto page
var script = document.createElement("script");
screen.type = "text/javascript";
script.innerHTML = "var injection = " + injection.toString();
// append created script
document.getElementsByTagName("head")[0].appendChild(script);
// call injected script
unsafeWindow.injection();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment