// ==UserScript==
// @name Add Spotify to YouTube
// @description Adds a link to Spotify on music videos on YouTube
// @include *://youtube.*/*
// @include *://*.youtube.*/*
// @version 0.0.1.20160521101909
// @namespace https://greatest.deepsurf.us/users/3216
// ==/UserScript==
var userscript = function()
{
// http://stackoverflow.com/questions/2246901/how-can-i-use-jquery-in-greasemonkey-scripts-in-google-chrome
function addJQuery(callback) {
var script = document.createElement("script");
script.setAttribute("src", "//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js");
script.addEventListener('load', function() {
var script = document.createElement("script");
script.textContent = "window.jQ=jQuery.noConflict(true);(" + callback.toString() + ")();";
document.body.appendChild(script);
}, false);
document.body.appendChild(script);
}
function main()
{
var videoinfo = {url: top.location.href, complete: 0, text: "", timer: null, running: 0};
var DEBUG = 0;
var PLAYVERSION = 0;
var DEBUGLOG;
if(DEBUG) DEBUGLOG = console;
else { var TDEBUGLOG = function() { return {log: function() {}}; }; DEBUGLOG = TDEBUGLOG(); }
function IsMusic()
{
if(jQ('#eow-title #watch-headline-show-title').length !== 0) return 1;
//if(jQ('#eow-category').text().trim() == "Music") return 1;
var extrasismusic = false;
jQ('.watch-extras-section .watch-meta-item').each(function(index)
{
var item = jQ(this);
var title = item.find('.title').text().trim();
var content = item.find('.content').text().trim();
if(title == "Category" && content == "Music") extrasismusic = true;
if(title == "Music") extrasismusic = true;
});
if(extrasismusic === true) return 1;
var metadata = jQ('.metadata-info-title');
for(var i = 0; i < metadata.length; i++)
{
if(metadata[i].innerText.match(/Buy ".*?" on|Artist/) !== null) return 1;
}
if(jQ('#eow-title').text().split('-', 2).length == 2) return 1; // Might be dangerous!
return 0;
}
function GetVideoTitle()
{
var artistname;
var trackname;
var fullname;
var artist;
var artist = jQ('#eow-title #watch-headline-show-title');
if(artist.length !== 0 && !artistname)
{
artistname = artist.text();
}
artist = jQ('.metadata-info');
if(artist.length !== 0 && !artistname)
{
for(var i = 0; i < artist.length; i++)
{
var tname = artist[i].innerText.match(/(Artist)[ \n\r]*(.*)/);
if(tname)
{
if(tname[2].trim() != "Various Artists")
{
artistname = tname[2];
break;
}
}
}
}
if(!artistname || !trackname)
{
jQ('.watch-extras-section .watch-meta-item').each(function(index)
{
var item = jQ(this);
var title = item.find('.title').text().trim();
var content = item.find('.content').text().trim();
if(title == "Music")
{
var musicsplit = content.split('by');
var tname = musicsplit[0].match(/"(.*)"/)[1];
var tartist = musicsplit[1].replace(/\([a-z• ]*\)$/i, '').trim();
if(tartist !== "") artistname = tartist;
if(tname !== "") trackname = tname;
}
});
}
if(!artistname)
{
var tname = jQ('#eow-title').text().split('-', 2);
if(tname.length == 2)
{
artistname = tname[0].trim();
}
}
var track = jQ('.metadata-info-title').text().match(/Buy "(.*?)" on/);
if(track !== null && !trackname)
{
trackname = track[1];
}
if(!trackname)
{
//trackname = jQ('#eow-title').text().replace(artistname, "").replace(/-/, '').trim();
var tname = jQ('#eow-title').text().split(/[-]+/, 2);
if(tname.length == 2)
{
trackname = tname[1].trim();
}
else
{
trackname = trackname = jQ('#eow-title').text().replace(/-/, '').trim();
if(artistname) trackname = trackname.replace(artistname, "");
}
}
if(!artistname && trackname)
{
artistname = " ";
}
if(artistname && trackname)
{
trackname = trackname.replace(/\((.*?)\)|\[(.*?)\]| (Re)?mastered| f(ea)?t(uring)?(.)? .*|\"|\'/gi, "").trim();
artistname = artistname.replace(/f(ea)?t(uring)?(.)? .*/gi, "").trim(); // & .*
return {artist: artistname, track: trackname};
}
else
{
if(!artistname) DEBUGLOG.log("failed to get artistname");
if(!trackname) DEBUGLOG.log("failed to get trackname");
}
return 0;
}
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
// http://stackoverflow.com/a/21081760
function wordInString(s, word){
return new RegExp( '\\b' + word + '\\b', 'i').test(s);
}
function FindOriginal(title, data)
{
var scores = [];
var highest = 0;
var title_artist = title.artist.replace(/[^a-zA-Z ]/g, "").split(' ').filter(Boolean);
var extra = [];
if(!/remix/i.test(title.track))
{
extra.push("remix");
}
for(var i = 0; i < Math.min(data.tracks.total, 10); i++)
{
var points = 0;
var track = data.tracks.items[i];
if(new RegExp('^(' + escapeRegExp(title.track) + ')', 'i').test(track.name))
{
points += 1;
}
if(!new RegExp('.*(radio|clean|live|remaster(ed)?|edit|karaoke|' + extra.join('|') + ')', 'gi').test(track.name))
//if(!new RegExp(escapeRegExp(title.track) + ' .*(radio|clean|live|remaster(ed)?|edit|karaoke|' + extra.join('|') + ')', 'gi').test(track.name))
{
points += 1;
}
var artists = track.artists;
for(var x = 0; x < artists.length; x++)
{
if(new RegExp('(karaoke)', 'gi').test(track.name)) points -= 2;
if(title_artist.some(function(str) { return wordInString(this, str); }, artists[x].name)) points += 1;
if(title.artist == artists[x].name) points += 2; // Favour exact matches
}
scores[i] = points;
}
for(var i = 0; i < scores.length; i++) if(scores[i] > scores[highest]) highest = i;
DEBUGLOG.log("scores", scores);
return highest;
}
function CheckAddress()
{
if(window.location.href.match(/^(https?\:\/\/(www\.)?youtube.com\/watch)/))
{
return 1;
}
return 0;
}
function AddSpotify()
{
if(!CheckAddress()) return 1;
videoinfo.complete = 0;
if(IsMusic())
{
var title = GetVideoTitle();
DEBUGLOG.log("title", title);
if(title !== 0)
{
jQ.getJSON('//api.spotify.com/v1/search?q=' + encodeURIComponent(title.artist + " " + title.track) + "&type=track&limit=10", function(data)
{
DEBUGLOG.log("data", data);
DEBUGLOG.log("fullname:", title.artist, title.track);
if(data.tracks.total > 0)
{
var artists = [];
var index = FindOriginal(title, data);
DEBUGLOG.log("Index", index, "selected");
var track = data.tracks.items[index];
jQ.each(track.artists, function(key, val)
{
var artisturl = "";
if(PLAYVERSION === 0) artisturl = val.uri;
else artisturl = val.external_urls.spotify;
artists.push('<a href="' + artisturl + '" ' + (PLAYVERSION ? 'target="_blank"' : '') + '>' + val.name + '</a>');
});
var artistsname = artists.join(", ");
var trackname = track.name;
var urltrack = "";
if(PLAYVERSION === 0) urltrack = track.uri;
else urltrack = track.external_urls.spotify;
var htmloutput = '<div class="youtubespotify yt-uix-menu" style="font-size: 70%"><a href="' + urltrack + '" ' + (PLAYVERSION ? 'target="_blank"' : '') + '>';
htmloutput += '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAQhSURBVDhPhZVtSJ11GMZ/zznHoz56XHbsbFmtpgj5khuZWCO3hDnG+tLqy+hDRRBsxPoSFX4IIigZsQjWgogFoygI5iAsSpdrMqHhXEKzCUujhWe+TI969OiZL6frfp4jKS12w9/n5fz/131d133fj05GwW1iLn2D38eOE5/tYCZ9lZXVFMGAy6a8SkqL9lIVO0Jh7r3Z3RvjP6BLKwucvlJPfGaASC7khiAnAAEHVrVzeRXSy5C8hcCrebaml5xgfva0HxtAryfO8nV/s9hAfo7Agj5gSNdgFnRJoMsrlhwWBD6zCAe3d7K1eE8WRQSyV/5KdPJFXzPFAswTu5BAbBlwWCuknXYNWxItS5anZ9v/5eVm7/xaeExN8tEul6irQ2KYFxZTk677sMnX4VUxzJh0yV5Mw60sW1t2P5mCN5tSnhUe6KneGqZSA0TvgoICGP0TRq7B9LgAtNmkF0SgZAtsrZCX5ahw8nVOBZ0XqGxIKdndbjUv1F/BSS7GM8fOlXJ/DL7/HNpOZDXcIcoqoek52HNQKvQ8nYAJJXm9KU5w30vhd0aTF4iIyTcfQkLs8mTDw49C7ePwSIPud8ADYle4CeaTYib5iZvwazec/kRqxHbHLv99KBDGOXWxLjM+1+cVJ1KkrEsC2OZLtoIEtRzdo8pbn1gHTAmwux3OnIThAY8477WpDlGI5tfhHPvJ1TYzWC9VyckR6NGBod/k7XV5NiNwFS66GR6Un1WPwZNPw5aH/Nbq74HBfmh8xveYjIvT+iMZrx+1jOnLdX5mC0csTfKyirAgiesjdh+82AIN++TnlGzR7zYYVjTng05XqlKeVFeDMdirKqpotU8okSyxSTL5ASWdHIW+8/DzGbh0zgff3givfQSz0/5gOIjpyZ66zFiyj1wdMv+K7/F9HZb88az8gL1XovIa2FblK5iehI/f0rtaST8gA9V6xnJzkTztGmzJdP/RiivfCtUBX7XChW99Fv8XO/fD828okXw2hgsCtL61Xm2saMGZXYhn3v2ulJJCydUEJSRx4m+oVEGMdUjvzAIbgpFh+OUHaFfVLQ4dVeFkU1rzb6CT8vXt/XF/oj7trmF0dsBrKwO2tarKWhvZMk9tBe13G195365BqdkpdcX+qKZlWUxfrUO7NFEGarPf0uZSrBG1/jRmawXyelRhF8uxFiGBW7NbWxnLaSl5/4A/+7Jc2XVz+KkOJjQta5usPby17tm76tmYpSR1Sezs+abOHd7d4eFYeEy9O8XQxFlOdDUT0RDY18ljqvdrbC1st+eK/li15+Tnq02dlMX+/Z5uALUwKz47X8+1sQEKJNH7QEuPJ187V7SM3bwqXRGr5pXdd/jyr4/k4g0uDh9naLxD8q6SXkmpl11KIpWUx/bSUHZEim73Pwr+AWfotNJ4G681AAAAAElFTkSuQmCC">';
htmloutput += ' ' + trackname + '</a> - ' + artistsname + '</div>';
jQ('.youtubespotify').remove();
jQ('#watch8-secondary-actions').append(htmloutput);
}
else
{
jQ('.youtubespotify').remove();
DEBUGLOG.log("No song found");
}
});
} else
{
jQ('.youtubespotify').remove();
DEBUGLOG.log("failed to get title");
}
}
else
{
DEBUGLOG.log("not music!!");
}
videoinfo.complete = 1;
videoinfo.url = top.location.href;
videoinfo.text = jQ('#eow-title').text().trim();
}
AddSpotify();
function timerfunction()
{
videoinfo.running = 1;
if(videoinfo.complete && videoinfo.url != top.location.href)
{
if(videoinfo.text != jQ('#eow-title').text().trim())
{
AddSpotify();
}
}
}
window.addEventListener('focus', function()
{
clearInterval(videoinfo.timer);
if(!videoinfo.running) videoinfo.timer = setInterval(timerfunction, CheckAddress() ? 1000 : 2000);
});
window.addEventListener('blur', function()
{
clearInterval(videoinfo.timer);
videoinfo.running = 0;
});
}
addJQuery(main);
};
// http://stackoverflow.com/questions/7971930/how-to-call-youtube-flash-api-of-existing-videos-using-greasemonkey
function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
var D = document;
var scriptNode = D.createElement ('script');
if (runOnLoad) {
scriptNode.addEventListener ("load", runOnLoad, false);
}
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
targ.appendChild (scriptNode);
}
addJS_Node(null, null, userscript);