Add Spotify to YouTube

Adds a link to Spotify on music videos on YouTube

当前为 2016-04-08 提交的版本,查看 最新版本

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