Add Spotify to YouTube

Adds a link to Spotify on music videos on YouTube

当前为 2016-01-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Add Spotify to YouTube
  3. // @description Adds a link to Spotify on music videos on YouTube
  4. // @include *://youtube.*/*
  5. // @include *://*.youtube.*/*
  6. // @version 0.0.1.20160128202230
  7. // @namespace https://greasyfork.org/users/3216
  8. // ==/UserScript==
  9.  
  10. var userscript = function()
  11. {
  12.  
  13. // http://stackoverflow.com/questions/2246901/how-can-i-use-jquery-in-greasemonkey-scripts-in-google-chrome
  14. function addJQuery(callback) {
  15. var script = document.createElement("script");
  16. script.setAttribute("src", "//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js");
  17. script.addEventListener('load', function() {
  18. var script = document.createElement("script");
  19. script.textContent = "window.jQ=jQuery.noConflict(true);(" + callback.toString() + ")();";
  20. document.body.appendChild(script);
  21. }, false);
  22. document.body.appendChild(script);
  23. }
  24.  
  25. function main()
  26. {
  27.  
  28. var videoinfo = {url: top.location.href, complete: 0, text: "", timer: null, running: 0}
  29. var DEBUG = 0;
  30.  
  31. if(DEBUG) var DEBUGLOG = console;
  32. else { var TDEBUGLOG = function() { return {log: function() {}}; }; var DEBUGLOG = TDEBUGLOG(); }
  33.  
  34. function IsMusic()
  35. {
  36. if(jQ('#eow-title #watch-headline-show-title').length != 0) return 1;
  37. //if(jQ('#eow-category').text().trim() == "Music") return 1;
  38. var extrasismusic = false;
  39. jQ('.watch-extras-section .watch-meta-item').each(function(index)
  40. {
  41. var item = jQ(this);
  42. var title = item.find('.title').text().trim();
  43. var content = item.find('.content').text().trim();
  44. if(title == "Category" && content == "Music") extrasismusic = true;
  45. if(title == "Music") extrasismusic = true;
  46. });
  47. if(extrasismusic == true) return 1;
  48. var metadata = jQ('.metadata-info-title');
  49. for(var i = 0; i < metadata.length; i++)
  50. {
  51. if(metadata[i].innerText.match(/Buy ".*?" on|Artist/) != null) return 1;
  52. }
  53.  
  54. if(jQ('#eow-title').text().split('-', 2).length == 2) return 1; // Might be dangerous!
  55. return 0;
  56. }
  57.  
  58. function GetVideoTitle()
  59. {
  60. var artistname;
  61. var trackname;
  62. var fullname;
  63. var artist = jQ('#eow-title #watch-headline-show-title');
  64. if(artist.length != 0 && !artistname)
  65. {
  66. artistname = artist.text();
  67. }
  68. var artist = jQ('.metadata-info');
  69. if(artist.length != 0 && !artistname)
  70. {
  71. for(var i = 0; i < artist.length; i++)
  72. {
  73. var tname = artist[i].innerText.match(/(Artist)[ \n\r]*(.*)/);
  74. if(tname)
  75. {
  76. if(tname[2].trim() != "Various Artists")
  77. {
  78. artistname = tname[2];
  79. break;
  80. }
  81. }
  82. }
  83. }
  84. if(!artistname || !trackname)
  85. {
  86. jQ('.watch-extras-section .watch-meta-item').each(function(index)
  87. {
  88. var item = jQ(this);
  89. var title = item.find('.title').text().trim();
  90. var content = item.find('.content').text().trim();
  91. if(title == "Music")
  92. {
  93. var musicsplit = content.split('by');
  94. var tname = musicsplit[0].match(/"(.*)"/)[1];
  95. var tartist = musicsplit[1].replace(/\([a-z• ]*\)$/i, '').trim()
  96. if(tartist != "") artistname = tartist;
  97. if(tname != "") trackname = tname;
  98. }
  99. });
  100. }
  101. if(!artistname)
  102. {
  103. var tname = jQ('#eow-title').text().split('-', 2);
  104. if(tname.length == 2)
  105. {
  106. artistname = tname[0].trim();
  107. }
  108. }
  109. var track = jQ('.metadata-info-title').text().match(/Buy "(.*?)" on/);
  110. if(track != null && !trackname)
  111. {
  112. trackname = track[1];
  113. }
  114. if(!trackname)
  115. {
  116. //trackname = jQ('#eow-title').text().replace(artistname, "").replace(/-/, '').trim();
  117. var tname = jQ('#eow-title').text().split(/[-]+/, 2);
  118. if(tname.length == 2)
  119. {
  120. trackname = tname[1].trim();
  121. }
  122. else
  123. {
  124. trackname = trackname = jQ('#eow-title').text().replace(/-/, '').trim();
  125. if(artistname) trackname = trackname.replace(artistname, "");
  126. }
  127. }
  128. if(!artistname && trackname)
  129. {
  130. artistname = " ";
  131. }
  132. if(artistname && trackname)
  133. {
  134. trackname = trackname.replace(/\((.*?)\)|\[(.*?)\]| (Re)?mastered| f(ea)?t(uring)?(.)? .*|\"|\'/gi, "").trim();
  135. artistname = artistname.replace(/f(ea)?t(uring)?(.)? .*/gi, "").trim(); // & .*
  136. return {artist: artistname, track: trackname};
  137. }
  138. else
  139. {
  140. if(!artistname) DEBUGLOG.log("failed to get artistname");
  141. if(!trackname) DEBUGLOG.log("failed to get trackname");
  142. }
  143. return 0;
  144. }
  145.  
  146. // http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
  147. function escapeRegExp(str) {
  148. return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  149. }
  150.  
  151. // http://stackoverflow.com/a/21081760
  152. function wordInString(s, word){
  153. return new RegExp( '\\b' + word + '\\b', 'i').test(s);
  154. }
  155.  
  156. function FindOriginal(title, data)
  157. {
  158. var scores = []
  159. var highest = 0;
  160. var title_artist = title.artist.replace(/[^a-zA-Z ]/g, "").split(' ').filter(Boolean);
  161. var extra = [];
  162. if(!/remix/i.test(title.track))
  163. {
  164. extra.push("remix");
  165. }
  166.  
  167. for(var i = 0; i < Math.min(data.tracks.total, 10); i++)
  168. {
  169. var points = 0;
  170. var track = data.tracks.items[i];
  171. if(!new RegExp(escapeRegExp(title.track) + ' .*(radio|clean|live|remaster(ed)?|edit|karaoke|' + extra.join('|') + ')', 'gi').test(track.name))
  172. {
  173. points += 1;
  174. }
  175. var artists = track.artists;
  176. for(var x = 0; x < artists.length; x++)
  177. {
  178. if(new RegExp('(karaoke)', 'gi').test(track.name)) points -= 2;
  179. if(title_artist.some(function(str) { return wordInString(this, str); }, artists[x].name)) points += 1;
  180. if(title.artist == artists[x].name) points += 2; // Favour exact matches
  181. }
  182. scores[i] = points;
  183. }
  184. for(var i = 0; i < scores.length; i++) if(scores[i] > scores[highest]) highest = i;
  185. return highest;
  186. }
  187.  
  188. function CheckAddress()
  189. {
  190. if(window.location.href.match(/^(https?\:\/\/(www\.)?youtube.com\/watch)/))
  191. {
  192. return 1;
  193. }
  194. return 0;
  195. }
  196.  
  197. function AddSpotify()
  198. {
  199. if(!CheckAddress()) return 1;
  200.  
  201. videoinfo.complete = 0;
  202. if(IsMusic())
  203. {
  204. var title = GetVideoTitle();
  205. DEBUGLOG.log("title", title);
  206. if(title != 0)
  207. {
  208. jQ.getJSON('//api.spotify.com/v1/search?q=' + encodeURIComponent(title.artist + " " + title.track) + "&type=track&limit=10", function(data)
  209. {
  210. DEBUGLOG.log("data", data);
  211. DEBUGLOG.log("fullname:", title.artist, title.track);
  212. if(data.tracks.total > 0)
  213. {
  214. var artists = [];
  215. var index = FindOriginal(title, data);
  216. DEBUGLOG.log("Index", index, "selected");
  217. var track = data.tracks.items[index];
  218. jQ.each(track.artists, function(key, val) { artists.push('<a href="' + val.uri + '">' + val.name + '</a>'); });
  219. var artistsname = artists.join(", ");
  220. var trackname = track.name;
  221. var htmloutput = '<div class="youtubespotify" style="font-size: 70%"><a href="' + track.uri + '">';
  222. 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">';
  223. htmloutput += ' ' + trackname + '</a> - ' + artistsname + '</div>';
  224. jQ('.youtubespotify').remove();
  225. jQ('#watch-headline-title').append(htmloutput);
  226. }
  227. else
  228. {
  229. jQ('.youtubespotify').remove();
  230. DEBUGLOG.log("No song found");
  231. }
  232. });
  233. } else
  234. {
  235. jQ('.youtubespotify').remove();
  236. DEBUGLOG.log("failed to get title");
  237. }
  238. }
  239. else
  240. {
  241. DEBUGLOG.log("not music!!");
  242. }
  243. videoinfo.complete = 1;
  244. videoinfo.url = top.location.href;
  245. videoinfo.text = jQ('#eow-title').text().trim();
  246. }
  247.  
  248. AddSpotify();
  249.  
  250. function timerfunction()
  251. {
  252. videoinfo.running = 1;
  253. if(videoinfo.complete && videoinfo.url != top.location.href)
  254. {
  255. if(videoinfo.text != jQ('#eow-title').text().trim())
  256. {
  257. AddSpotify();
  258. }
  259. }
  260. }
  261.  
  262. window.addEventListener('focus', function()
  263. {
  264. clearInterval(videoinfo.timer);
  265. if(!videoinfo.running) videoinfo.timer = setInterval(timerfunction, CheckAddress() ? 1000 : 2000);
  266. });
  267.  
  268. window.addEventListener('blur', function()
  269. {
  270. clearInterval(videoinfo.timer);
  271. videoinfo.running = 0;
  272. });
  273.  
  274. }
  275.  
  276. addJQuery(main);
  277.  
  278. };
  279.  
  280. // http://stackoverflow.com/questions/7971930/how-to-call-youtube-flash-api-of-existing-videos-using-greasemonkey
  281. function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
  282. var D = document;
  283. var scriptNode = D.createElement ('script');
  284. if (runOnLoad) {
  285. scriptNode.addEventListener ("load", runOnLoad, false);
  286. }
  287. scriptNode.type = "text/javascript";
  288. if (text) scriptNode.textContent = text;
  289. if (s_URL) scriptNode.src = s_URL;
  290. if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
  291.  
  292. var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
  293. targ.appendChild (scriptNode);
  294. }
  295.  
  296. addJS_Node(null, null, userscript);