BestSubscriber

Organiza tus suscripciones por juego y da like automáticamente a los vídeos que veas

当前为 2015-12-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name BestSubscriber
  3. // @namespace https://greasyfork.org/es/scripts/15251-bestsubscriber
  4. // @version 0.1
  5. // @description Organiza tus suscripciones por juego y da like automáticamente a los vídeos que veas
  6. // @author DonNadie
  7. // @match https://*.youtube.com/feed/subscriptions
  8. // @match https://*.youtube.com/watch?v=*
  9. // @grant none
  10. // @require https://code.jquery.com/jquery-latest.js
  11. // @require https://cdn.jsdelivr.net/sortable/1.4.2/Sortable.min.js
  12. // @require https://greasyfork.org/scripts/15246-micro-templating/code/Micro%20templating.js?version=95830
  13. // @require https://greasyfork.org/scripts/1003-wait-for-key-elements/code/Wait%20for%20key%20elements.js?version=49342
  14. // ==/UserScript==
  15. /* jshint -W097 */
  16. 'use strict';
  17.  
  18. var BestSubscriber = function ()
  19. {
  20. var gameList,
  21. debugMode = false,
  22. skipWords = [
  23. 'gameplay',
  24. 'episode',
  25. 'episodio',
  26. 'ps4',
  27. 'xbox 360',
  28. 'xbox one',
  29. 'xbox',
  30. 'directo',
  31. ];
  32.  
  33. String.prototype.capitalizeFirstLetter = function() {
  34. return this.charAt(0).toUpperCase() + this.slice(1);
  35. };
  36. String.prototype.friendly = function () {
  37. var str = this;
  38. if (typeof max == "undefined") max = 32;
  39. var a_chars = new Array(
  40. new Array("a",/[áàâãªÁÀÂÃ]/g),
  41. new Array("e",/[éèêÉÈÊ]/g),
  42. new Array("i",/[íìîÍÌÎ]/g),
  43. new Array("o",/[òóôõºÓÒÔÕ]/g),
  44. new Array("u",/[úùûÚÙÛ]/g),
  45. new Array("c",/[çÇ]/g),
  46. new Array("n",/[Ññ]/g)
  47. );
  48. // Replace vowel with accent without them
  49. for(var i=0;i<a_chars.length;i++)
  50. str = str.replace(a_chars[i][1],a_chars[i][0]);
  51. // first replace whitespace by -, second remove repeated - by just one, third turn in low case the chars,
  52. // fourth delete all chars which are not between a-z or 0-9, fifth trim the string and
  53. // the last step truncate the string to 32 chars
  54. return str.replace(/\s+/g,'-').toLowerCase().replace(/[^a-z0-9\-]/g, '').replace(/\-{2,}/g,'-').replace(/(^\s*)|(\s*$)/g, '').substr(0, max);
  55. };
  56.  
  57. var init = function ()
  58. {
  59. var k,
  60. game;
  61.  
  62. gameList = getConfig();
  63. setupPanel();
  64.  
  65. // detect AJAX navigation
  66. var observer = new MutationObserver(function(mutations) {
  67. // We observe the title, so our key elements in body may not be loaded yet
  68. setTimeout(function () {
  69. firePageActions();
  70. }, 100);
  71. });
  72.  
  73. observer.observe(document.querySelector('title'), { childList: true});
  74.  
  75. firePageActions();
  76. }
  77.  
  78. var firePageActions = function ()
  79. {
  80. if (location.pathname.indexOf("feed/subscriptions") !== -1 && $('.best-subscriber-section').length == 0) {
  81. setupSubscriptionFeed();
  82. }
  83. else if (location.pathname.indexOf("watch") !== -1)
  84. {
  85. log("Inside a video");
  86. if ($(".video-stream.html5-main-video").length == 0) {
  87. waitForKeyElements (".video-stream.html5-main-video", function () {
  88. setTimeout(autoLike, 750);
  89. });
  90. } else {
  91. setTimeout(autoLike, 750);
  92. }
  93. }
  94. }
  95.  
  96. var getConfig = function ()
  97. {
  98. var config = localStorage.bestSubscriber;
  99.  
  100. if (config === undefined) {
  101. // default config
  102. return [{
  103. name: 'gta',
  104. display: false,
  105. },
  106. {
  107. name: 'hurtworld',
  108. display: true,
  109. },
  110. {
  111. name: 'h1z1',
  112. display: true,
  113. },
  114. {
  115. name: 'rust',
  116. display: true,
  117. }];
  118. }
  119.  
  120. return JSON.parse(config);
  121. }
  122.  
  123. var setConfig = function (config)
  124. {
  125. if (config === undefined || !Array.isArray(config)) {
  126. return;
  127. }
  128.  
  129. localStorage.bestSubscriber = JSON.stringify(config);
  130. }
  131.  
  132. var updateGame = function (name, key, val)
  133. {
  134. var k,
  135. list = getConfig();
  136.  
  137. for (k in list) {
  138. if (list[k].name.friendly() == name) {
  139. list[k][key] = val;
  140. setConfig(list);
  141. break;
  142. }
  143. }
  144. }
  145.  
  146. var setupPanel = function ()
  147. {
  148. $('#yt-masthead-user').append('<button class="yt-uix-button yt-uix-button-default" data-action="toggle-config-panel" style="margin-left: 20px">BestSubcriber</button>');
  149.  
  150. $('#masthead-positioner').prepend(parseTemplate(function () {/*
  151. <div id="best-subscriber-config">
  152. <h3>Orden:</h3>
  153. <ul>
  154. <%for (var i=0; i<list.length; i++){ %>
  155. <li><span data-name><%=list[i].name%></span> <span class="bs-delete-button" data-action="delete-game" title="Quitar de la lista">x</span></li>
  156. <% } %>
  157. </ul>
  158. <div>
  159. <label>
  160. <input type="text" name="new-game-name" placeholder="GTA 5">
  161. </label>
  162. <button data-action="add-game" class="add-button" title="Añadir a la lista">+</button>
  163. </div>
  164. <button data-action="save-config" class="yt-uix-button yt-uix-button-default bs-save-button">Guardar cambios</button>
  165. </div>
  166. <style>
  167. #best-subscriber-config {
  168. display: none;
  169. position: fixed;
  170. background: white;
  171. padding: 10px;
  172. border: 1px solid;
  173. left: 89%;
  174. top: 44%;
  175. z-index: 1999999999; // set by Youtube..
  176. }
  177. #best-subscriber-config li {
  178. cursor: all-scroll;
  179. border: 1px dashed;
  180. padding: 5px;
  181. }
  182. #best-subscriber-config li:hover {
  183. background: #F8F8F8;
  184. }
  185. #best-subscriber-config .bs-save-button {
  186. margin-top: 10px;
  187. float: right;
  188. }
  189. #best-subscriber-config .bs-delete-button {
  190. font-weight: bold;
  191. float: right;
  192. cursor: pointer;
  193. color: red;
  194. }
  195. #best-subscriber-config input {
  196. margin-top: 5px;
  197. }
  198. #best-subscriber-config .add-button {
  199. color: green;
  200. font-weight: bold;
  201. cursor: pointer;
  202. }
  203. </style>
  204. */}, {list: gameList.slice().reverse()})); // revert the arary to display it in the right order in config panel
  205.  
  206. //$("#best-subscriber-config ul").sortable();
  207. Sortable.create($("#best-subscriber-config ul")[0]);
  208.  
  209. $('[data-action="toggle-config-panel"]').on("click", function () {
  210. $("#best-subscriber-config").toggle();
  211. });
  212.  
  213. $('input[name="new-game-name"]').on("keydown", function(e) {
  214. if (e.keyCode == 13) {
  215. e.preventDefault();
  216.  
  217. addGame();
  218. }
  219. });
  220.  
  221. $('[data-action="add-game"]').on("click", addGame);
  222.  
  223. $('[data-action="delete-game"]').on("click", function () {
  224. var game = $(this).closest("[data-name]").text();
  225.  
  226. deleteGame(game);
  227. $(this).closest("li").remove();
  228. });
  229.  
  230. $('[data-action="save-config"]').on("click", function () {
  231. var k,
  232. list = [],
  233. tmpList = {},
  234. oldList = getConfig();
  235.  
  236. for (k in oldList) {
  237. tmpList[oldList[k].name] = oldList[k];
  238. }
  239.  
  240. $('#best-subscriber-config li span[data-name]').each(function () {
  241. var name = $(this).text();
  242. list.push(tmpList[name]);
  243. });
  244. list.reverse();
  245.  
  246. setConfig(list);
  247. location.reload();
  248. });
  249. }
  250. var setupSubscriptionFeed = function () {
  251. // create the categories (games)
  252. for (k in gameList)
  253. {
  254. game = gameList[k];
  255. $('#browse-items-primary ol.section-list').prepend(parseTemplate(function () {
  256. /*
  257. <li id="game-<%=game.name.friendly() %>" class="best-subscriber-section">
  258. <ol class="item-section">
  259. <li>
  260. <div class="feed-item-container browse-list-item-container yt-section-hover-container compact-shelf shelf-item branded-page-box clearfix">
  261. <div class="feed-item-dismissable">
  262. <div class="shelf-title-table">
  263. <div class="shelf-title-row">
  264. <h2 class="branded-page-module-title shelf-title-cell"><span class="branded-page-module-title-text"><%=game.name.capitalizeFirstLetter()%></span></h2>
  265. <div class="menu-container shelf-title-cell">
  266. <div class="yt-uix-menu-container feed-item-action-menu">
  267. <ul class="yt-uix-menu-top-level-button-container">
  268. <li class="yt-uix-menu-top-level-button yt-uix-menu-top-level-flow-button">
  269. <button data-action="toggle" data-game="<%=game.name.friendly()%>" class="yt-uix-button yt-uix-button-size-default yt-uix-button-opacity yt-uix-button-empty yt-uix-button-has-icon yt-uix-tooltip" onclick=";return false;" title="Mostrar/Ocultar">
  270. <span class="yt-uix-button-icon-wrapper">
  271. <span class="yt-uix-button-icon yt-sprite yt-uix-expander-arrow" <% if (game.display){ %>style="transform: rotate(180deg);"<% } %>>
  272. </span>
  273. </span>
  274. </button>
  275. </li>
  276. </ul>
  277. </div>
  278. </div>
  279. </div>
  280. </div>
  281. <div class="multirow-shelf">
  282. <ul class="shelf-content" data-section="videos" <% if (!game.display){ %>style="display:none"<% } %>>
  283.  
  284. </ul>
  285. </div>
  286. </div>
  287. <div class="feed-item-dismissal-notices"></div>
  288. </div>
  289. </li>
  290. </ol>
  291. </li>
  292. */}, { game : game }));
  293. }
  294.  
  295.  
  296. // move the videos
  297. $('.yt-shelf-grid-item').each(function () {
  298. var regex,
  299. title = $(".yt-lockup-title a", this).text();
  300.  
  301. for (k in gameList)
  302. {
  303. game = gameList[k];
  304. regex = new RegExp(game.name, "i");
  305.  
  306.  
  307. if (title.search(regex) !== -1) {
  308. $('#game-' + game.name.friendly() + ' [data-section="videos"]').append($(this));
  309. }
  310. }
  311. });
  312.  
  313. // show/hide categories
  314. $('[data-action="toggle"]').on("click", function () {
  315. var game = $(this).data("game"),
  316. isVisible = false,
  317. list = getConfig(),
  318. $videosContainer = $('#game-' + game + ' [data-section="videos"]');
  319.  
  320. $videosContainer.toggle();
  321. isVisible = $videosContainer.is(":visible")
  322.  
  323. if (isVisible) {
  324. $('.yt-uix-expander-arrow', this).css("transform", "rotate(180deg)");
  325. } else {
  326. $('.yt-uix-expander-arrow', this).css("transform", "");
  327. }
  328.  
  329. updateGame(game, "display", isVisible);
  330. });
  331. }
  332.  
  333. var autoLike = function ()
  334. {
  335. log("Liking");
  336. var $likeButton = $('.like-button-renderer-like-button-unclicked'),
  337. eventObject;
  338.  
  339. // is already liked or user is not subscribed
  340. if (!$likeButton.is(":visible") || !$('.yt-uix-button-subscribed-branded').is(":visible")) {
  341. log("not clicking");
  342. return;
  343. }
  344.  
  345. eventObject = document.createEvent('MouseEvents');
  346.  
  347. eventObject.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  348. $likeButton[0].dispatchEvent(eventObject);
  349. }
  350.  
  351. var deleteGame = function (name)
  352. {
  353. var k,
  354. list = getConfig();
  355.  
  356. for (k in list) {
  357. if (list[k].name == name) {
  358. list = list.splice(k, 1);
  359. break;
  360. }
  361. }
  362. setConfig(list);
  363. }
  364.  
  365. var addGame = function ()
  366. {
  367. var game = $('input[name="new-game-name"]').val(),
  368. list = getConfig();
  369.  
  370. if (game.length < 2) {
  371. return;
  372. }
  373.  
  374. $('input[name="new-game-name"]').val("");
  375. $('#best-subscriber-config ul').append('<li><span data-name>' + game + '</span> <span class="bs-delete-button" data-action="delete-game" title="Quitar de la lista">x</span></li>');
  376.  
  377. list.push({
  378. name: game,
  379. display: true
  380. });
  381.  
  382. setConfig(list);
  383. }
  384.  
  385. var parseTemplate = function(f, data)
  386. {
  387. var html = f.toString().replace(/^[^\/]+\/\*!?/, '').replace(/\*\/[^\/]+$/, '');
  388.  
  389. if (typeof data != 'undefined') {
  390. return _tmpl(html, data);
  391. } else {
  392. return html;
  393. }
  394. }
  395.  
  396. // Not finished, suggests game names for the list for the first time
  397. var suggestGameNames = function ()
  398. {
  399. $('.yt-shelf-grid-item').each(function () {
  400. var title = $(".yt-lockup-title a", this).text(),
  401. publisher = $(".yt-lockup-byline a", this).text();
  402.  
  403. console.log(title.replace(publisher, ''));
  404. });
  405. }
  406.  
  407. var log = function () {
  408. if (debugMode) {
  409. console.log.apply(console, arguments);
  410. }
  411. }
  412.  
  413. return {
  414. init : init,
  415. };
  416. }();
  417.  
  418.  
  419. $(document).ready(function() {
  420. BestSubscriber.init();
  421. });