Export Youtube Playlist in plaintext

Shows a list of the playlist video names/channels/URLs in plaintext to be easily copied

当前为 2023-07-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Export Youtube Playlist in plaintext
  3. // @namespace 1N07
  4. // @version 0.7.2
  5. // @description Shows a list of the playlist video names/channels/URLs in plaintext to be easily copied
  6. // @author 1N07
  7. // @icon https://www.google.com/s2/favicons?domain=youtube.com
  8. // @require https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
  9. // @match https://www.youtube.com/*
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. var getVideoTitle = GM_getValue("getVideoTitle", true);
  18. var getVideoChannel = GM_getValue("getVideoChannel", false);
  19. var getVideoURL = GM_getValue("getVideoURL", false);
  20. var videoListSeperator = GM_getValue("videoListSeperator", "|:|");
  21.  
  22. var listCreationAllowed = true;
  23. var urlAtLastCheck = "";
  24.  
  25. //add some CSS
  26. if(true) {
  27. addGlobalStyle(`
  28. tp-yt-paper-listbox#items { overflow-x: hidden; }
  29.  
  30. #exportPlainTextList {
  31. cursor: pointer;
  32. height: 36px;
  33. width: 100%;
  34. display: flex;
  35. align-items: center;
  36. }
  37. #exportPlainTextList > img {
  38. height: 24px; width: 24px;
  39. color: rgb(144, 144, 144);
  40. padding: 0 13px 0 16px;
  41. filter: contrast(0%);
  42. }
  43. #exportPlainTextList > span {
  44. font-family: "Roboto","Arial",sans-serif;
  45. color: var(--yt-spec-text-primary);
  46. white-space: nowrap;
  47. font-size: 1.4rem;
  48. line-height: 2rem;
  49. font-weight: 400;
  50. }
  51.  
  52. #exportPlainTextList:hover { background-color: rgba(255,255,255,0.1); }
  53. ytd-menu-popup-renderer.ytd-popup-container { overflow-x: hidden !important; max-height: none !important; }
  54.  
  55. #listDisplayContainer {
  56. position: fixed;
  57. z-index: 9999;
  58. margin: 0 auto;
  59. background-color: #464646;
  60. padding: 10px;
  61. border-radius: 5px;
  62. left: 0;
  63. right: 0;
  64. max-width: 100vw;
  65. width: 1200px;
  66. height: 900px;
  67. max-height: 90vh;
  68. top: 5vh;
  69. resize: both;
  70. overflow: hidden;
  71. }
  72. #listDisplayContainer > textarea {
  73. box-sizing: border-box;
  74. width: 100%;
  75. margin: 10px 0;
  76. height: calc(100% - 40px);
  77. background-color: #262626;
  78. border: none;
  79. color: #EEE;
  80. border-radius: 5px;
  81. resize: none;
  82. }
  83. #closeTheListThing {
  84. float: right;
  85. font-weight: bold;
  86. background-color: RGBA(255,255,255,0.25);
  87. border: none;
  88. font-size: 17px;
  89. border-radius: 10px;
  90. height: 25px;
  91. width: 25px;
  92. cursor: pointer;
  93. }
  94.  
  95. #closeTheListThing:hover { background-color: rgba(255,255,255,0.5); }
  96. `);
  97. }
  98.  
  99. setInterval(function(){
  100. if(urlAtLastCheck != window.location.href)
  101. {
  102. urlAtLastCheck = window.location.href;
  103. if(urlAtLastCheck.includes("/playlist?list="))
  104. InsertButtonASAP();
  105. }
  106. }, 100);
  107.  
  108.  
  109. function InsertButtonASAP()
  110. {
  111. let buttonInsertInterval = setInterval(function(){
  112. //wait for possible previous buttons to stop existing (due to how youtube loads pages) and for the space for the new button to be available
  113. if($("#exportPlainTextList").length == 0 && $("tp-yt-paper-listbox#items").length > 0)
  114. {
  115. $("tp-yt-paper-listbox#items").append(`
  116. <div id="exportPlainTextList">
  117. <img src="https://i.imgur.com/emlur3a.png">
  118. <span>Export Playlist</span>
  119. </div>
  120. `);
  121. $("#exportPlainTextList").click(ScrollUntillAllVisible);
  122. clearInterval(buttonInsertInterval);
  123. }
  124. }, 100);
  125. }
  126.  
  127. function ScrollUntillAllVisible()
  128. {
  129. if(!listCreationAllowed)
  130. return;
  131.  
  132. $("ytd-browse[page-subtype='playlist']").click();
  133.  
  134. listCreationAllowed = false;
  135. $("#exportPlainTextList").after(`<p id="listBuildMessage" style="color: red; font-size: 1.33em;">Getting list...<br>please click out of the popup to continue autoscrolling...</p>`);
  136. let scrollInterval = setInterval(function(){
  137. if($("ytd-continuation-item-renderer.ytd-playlist-video-list-renderer").length)
  138. $(document).scrollTop($(document).height());
  139. else
  140. {
  141. $("#listBuildMessage").remove();
  142. DisplayListOptions();
  143. clearInterval(scrollInterval);
  144. }
  145. }, 100);
  146. }
  147.  
  148. function DisplayListOptions() {
  149. $("body").append(`
  150. <div id="listDisplayContainer">
  151. <p style="text-align: center;">
  152. <span style="font-size: 21px; font-weight: bold; color: #d9d9d9;">Playlist in plain text</span>
  153. <button id="closeTheListThing">X</button>
  154. </p>
  155. <textarea style="display: none;">`+list+`</textarea>
  156. <ul id="listDisplayOptions" style="list-style: none; font-size: 12px; scale: 1.4; color: #d9d9d9; width: -moz-fit-content; width: fit-content; margin: 40px auto;">
  157. <li><label><input type="checkbox" ` + (getVideoTitle ? `checked` : ``) + ` id="getVideoTitleCB" name="getVideoTitleCB" value="getVideoTitle"> Get titles</label></li>
  158. <li><label><input type="checkbox" ` + (getVideoChannel ? `checked` : ``) + ` id="getVideoChannelCB" name="getVideoChannelCB" value="getVideoChannel"> Get channel names</label></li>
  159. <li><label><input type="checkbox" ` + (getVideoURL ? `checked` : ``) + ` id="getVideoURLCB" name="getVideoURLCB" value="getVideoURL"> Get URLs</label></li>
  160. <li><label><input type="text" style="width: 40px; text-align: center;" id="videoListSeperatorInput" name="videoListSeperatorInput" value="`+videoListSeperator+`"> Name/Author/URL seperator</label></li>
  161. <li><button id="listDisplayGetListButton" style="position: relative; margin: 10px 0; font-size: 13px; left: 50%; -ms-transform: translateX(-50%); transform: translateX(-50%);">Get list</button></li>
  162. </ul>
  163. </div>
  164. `);
  165.  
  166. $("#getVideoTitleCB").change(function() {
  167. getVideoTitle = $(this).is(":checked");
  168. GM_setValue("getVideoTitle", getVideoTitle);
  169. });
  170. $("#getVideoChannelCB").change(function() {
  171. getVideoChannel = $(this).is(":checked");
  172. GM_setValue("getVideoChannel", getVideoChannel);
  173. });
  174. $("#getVideoURLCB").change(function() {
  175. getVideoURL = $(this).is(":checked");
  176. GM_setValue("getVideoURL", getVideoURL);
  177. });
  178. $("#videoListSeperatorInput").change(function() {
  179. videoListSeperator = $(this).val();
  180. GM_setValue("videoListSeperator", videoListSeperator);
  181. });
  182. $("#listDisplayGetListButton").click(BuildAndDisplayList);
  183. $("#closeTheListThing").click(function(){
  184. $("#listDisplayContainer").remove();
  185. listCreationAllowed = true;
  186. });
  187. }
  188.  
  189. function BuildAndDisplayList()
  190. {
  191. $("#listDisplayOptions").hide();
  192. $("#listDisplayContainer > textarea").show();
  193.  
  194. let videoTitleArr = [];
  195. let videoChannelArr = [];
  196. let videoURLArr = [];
  197. let videoCount = 0;
  198.  
  199. $("ytd-playlist-video-list-renderer > #contents.ytd-playlist-video-list-renderer > ytd-playlist-video-renderer #content").each(function(){
  200. if(getVideoTitle)
  201. videoTitleArr.push($(this).find("#video-title").attr("title"));
  202.  
  203. if(getVideoURL)
  204. videoURLArr.push("https://www.youtube.com" + $(this).find("#video-title").attr("href").split("&")[0]);
  205.  
  206. if(getVideoChannel)
  207. videoChannelArr.push($(this).find("#channel-name yt-formatted-string.ytd-channel-name > a").text());
  208.  
  209. videoCount++;
  210. });
  211.  
  212.  
  213. let list = "";
  214. for(let i = 0; i < videoCount; i++)
  215. {
  216. if(getVideoTitle)
  217. list += videoTitleArr[i];
  218.  
  219. if(getVideoChannel)
  220. list += (getVideoTitle ? " "+videoListSeperator+" " : "") + videoChannelArr[i];
  221.  
  222. if(getVideoURL)
  223. list += (getVideoTitle || getVideoChannel ? " "+videoListSeperator+" " : "") + videoURLArr[i];
  224.  
  225. list += "\n";
  226. }
  227.  
  228. $("#listDisplayContainer > textarea").html(list);
  229. $("#listBuildMessage").remove();
  230. }
  231.  
  232. function addGlobalStyle(css)
  233. {
  234. var head, style;
  235. head = document.getElementsByTagName('head')[0];
  236. if (!head) { return; }
  237. style = document.createElement('style');
  238. style.type = 'text/css';
  239. style.innerHTML = css;
  240. head.appendChild(style);
  241. }
  242. })();