YouTube Playlist Close

Allow quick closing of playlists

  1. // ==UserScript==
  2. // @name YouTube Playlist Close
  3. // @version 2.1
  4. // @description Allow quick closing of playlists
  5. // @author AjaxGb
  6. // @match http*://www.youtube.com/*
  7. // @run-at document-start
  8. // @resource buttonDark https://raw.githubusercontent.com/AjaxGb/YouTube-Playlist-Close/master/closeMediumDark.png
  9. // @resource buttonLight https://raw.githubusercontent.com/AjaxGb/YouTube-Playlist-Close/master/closeMediumLight.png
  10. // @grant GM_getResourceURL
  11. // @noframes
  12. // @namespace https://greasyfork.org/users/85711
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. function GM_addStyle(css) {
  19. const style = document.getElementById('GM_addStyleBy8626') || (function() {
  20. const style = document.createElement('style');
  21. style.type = 'text/css';
  22. style.id = 'GM_addStyleBy8626';
  23. document.head.appendChild(style);
  24. return style;
  25. })();
  26. const sheet = style.sheet;
  27. sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
  28. }
  29.  
  30. GM_addStyle(`
  31. #YT-Playlist-Close-By-AjaxGb-Close-Button-1234567890 {
  32. width: 44px;
  33. height: 40px;
  34. position: absolute;
  35. top: 0px;
  36. right: 0px;
  37. background-position: center;
  38. background-repeat: no-repeat;
  39. cursor: pointer;
  40. opacity: 0.5;
  41. }`);
  42. GM_addStyle(`
  43. #YT-Playlist-Close-By-AjaxGb-Close-Button-1234567890:hover {
  44. opacity: 0.6;
  45. }`);
  46. GM_addStyle(`
  47. #playlist #YT-Playlist-Close-By-AjaxGb-Close-Button-1234567890 {
  48. background-image: url("${GM_getResourceURL('buttonDark')}");
  49. }`);
  50. GM_addStyle(`
  51. #player-playlist #YT-Playlist-Close-By-AjaxGb-Close-Button-1234567890 {
  52. background-image: url("${GM_getResourceURL('buttonLight')}");
  53. }`);
  54. GM_addStyle(`
  55. #player-playlist .playlist-header, #playlist .header {
  56. position: relative;
  57. }`);
  58.  
  59. function getQueryArgs(query) {
  60. query = (query || window.location.search).substring(1);
  61. if(!query) return {};
  62. return query.split('&').reduce(function(prev, curr) {
  63. const p = curr.split('=');
  64. prev[decodeURIComponent(p[0])] = p[1] ? decodeURIComponent(p[1]) : p[1];
  65. return prev;
  66. }, {});
  67. }
  68.  
  69. function setQueryArgs(query) {
  70. if(!query) return;
  71. let search = '';
  72. for(let prop in query){
  73. if(query[prop] === undefined){
  74. search += '&'+encodeURIComponent(prop);
  75. }else{
  76. search += '&'+encodeURIComponent(prop)+'='+encodeURIComponent(query[prop]);
  77. }
  78. }
  79. return '?' + search.substr(1);
  80. }
  81.  
  82. let q;
  83. const b = document.createElement('a');
  84. b.id = 'YT-Playlist-Close-By-AjaxGb-Close-Button-1234567890';
  85. b.title = 'Close playlist';
  86.  
  87. function updateURL() {
  88. b.href = location.toString();
  89. q = getQueryArgs(b.search);
  90. delete q.list;
  91. delete q.index;
  92. delete q.t;
  93. b.search = setQueryArgs(q);
  94. }
  95.  
  96. b.onmouseenter = function() {
  97. updateURL();
  98. };
  99.  
  100. b.onmouseup = function() {
  101. updateURL();
  102. const t = document.getElementById('movie_player').getCurrentTime()|0;
  103. if (t > 0) {
  104. q.time_continue = t;
  105. b.search = setQueryArgs(q);
  106. setTimeout(resetQuery);
  107. }
  108. };
  109.  
  110. function resetQuery() {
  111. delete q.time_continue;
  112. b.search = setQueryArgs(q);
  113. }
  114.  
  115. function addButton(p) {
  116. updateURL();
  117. p.appendChild(b);
  118. }
  119.  
  120. const observer = new MutationObserver(function(mrs) {
  121. if(document.contains(b)) return;
  122.  
  123. const playlistHeader = document.querySelector([
  124. '#playlist:not(.ytd-miniplayer) .header',
  125. '#player-playlist .playlist-header',
  126. ].join(','));
  127.  
  128. if (playlistHeader) addButton(playlistHeader);
  129. });
  130. observer.observe(document.documentElement, {
  131. childList: true,
  132. subtree: true
  133. });
  134. })();