YouTube Extra!

Adds a button that lets you download YouTube videos as mp4 & mp3.

  1. // ==UserScript==
  2. // @name YouTube Extra!
  3. // @description Adds a button that lets you download YouTube videos as mp4 & mp3.
  4. // @author axelsmasher - gaming
  5. // @version 0.1
  6. // @date 2016-8-23
  7. // @namespace http://googlesystem.blogspot.com
  8. // @include http://www.youtube.com/*
  9. // @include https://www.youtube.com/*
  10. // @exclude http://www.youtube.com/embed/*
  11. // @exclude https://www.youtube.com/embed/*
  12. // @match http://www.youtube.com/*
  13. // @match https://www.youtube.com/*
  14. // @match http://s.ytimg.com/yts/jsbin/html5player*
  15. // @match https://s.ytimg.com/yts/jsbin/html5player*
  16. // @match http://manifest.googlevideo.com/*
  17. // @match https://manifest.googlevideo.com/*
  18. // @match http://*.googlevideo.com/videoplayback*
  19. // @match https://*.googlevideo.com/videoplayback*
  20. // @match http://*.youtube.com/videoplayback*
  21. // @match https://*.youtube.com/videoplayback*
  22. // @connect googlevideo.com
  23. // @connect ytimg.com
  24. // @grant GM_xmlhttpRequest
  25. // @grant GM_getValue
  26. // @grant GM_setValue
  27. // @run-at document-end
  28. // @license MIT License
  29. // @icon http://blog.storagemadeeasy.com/wp-content/uploads/2014/04/SME-Youtube.png
  30. // @match *://www.youtube.com/*
  31. // @exclude *://www.youtube.com/tv*
  32. // @exclude *://www.youtube.com/embed/*
  33. // @run-at document-start
  34. // @homepageURL https://github.com/ParticleCore/Particle
  35. // @supportURL https://github.com/ParticleCore/Particle/wiki
  36. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UMVQJJFG4BFHW
  37. // @grant GM_getValue
  38. // @grant GM_setValue
  39. // @noframes
  40. // ==/UserScript==
  41. (function () {
  42. "use strict";
  43. var particle = {
  44. inject: function(is_userscript) {
  45. function setLocale(content) {
  46. var i, j, list, temp, ytplabel;
  47. ytplabel = content.querySelectorAll("[data-p]");
  48. i = ytplabel.length;
  49. while (i--) {
  50. list = ytplabel[i].dataset.p.split("&");
  51. j = list.length;
  52. while (j--) {
  53. temp = list[j].split("|");
  54. if (temp[0] === "tnd") {
  55. ytplabel[i].appendChild(document.createTextNode(lang(temp[1])));
  56. } else if (temp[0] === "ttl") {
  57. ytplabel[i].setAttribute("title", lang(temp[1]));
  58. } else {
  59. ytplabel[i].dataset.tooltipText = lang(temp[1]);
  60. }
  61. }
  62. }
  63. return content;
  64. }
  65. function getLocale(data) {
  66. lang.fetching = false;
  67. data = document.documentElement.dataset.setlocale;
  68. data = data && JSON.parse(data);
  69. if (data) {
  70. user_settings.extLang[lang.ytlang] = JSON.parse(document.documentElement.dataset.setlocale);
  71. user_settings.extLang[lang.ytlang].lastMod = new Date().getTime();
  72. user_settings.extLang.nextCheck = new Date().getTime() + 6048E5;
  73. set("extLang", user_settings.extLang);
  74. }
  75. lang.observer.disconnect();
  76. }
  77. function getLanguage(data) {
  78. lang.fetching = false;
  79. if (data.target.readyState === 4 && data.target.status === 200) {
  80. user_settings.extLang[lang.ytlang] = JSON.parse(data.target.response);
  81. user_settings.extLang[lang.ytlang].lastMod = new Date(data.target.getResponseHeader("Last-Modified")).getTime();
  82. }
  83. user_settings.extLang.nextCheck = new Date().getTime() + 6048E5;
  84. set("extLang", user_settings.extLang);
  85. }
  86. function checkModified(data) {
  87. lang.fetching = false;
  88. if (data.target.readyState === 4 && data.target.status === 200) {
  89. lang.fetching = true;
  90. localXHR("GET", getLanguage, lang.urlBase + lang.ytlang + ".json", ["Accept", "application/vnd.github.raw"]);
  91. }
  92. }
  93. function lang(label) {
  94. lang.ytlang = window.yt && window.yt.config_ && window.yt.config_.GAPI_LOCALE;
  95. lang.urlBase = "https://api.github.com/repos/ParticleCore/Particle/contents/Locale/";
  96. if (!user_settings.extLang) {
  97. set("extLang", {});
  98. }
  99. if (user_settings.GEN_LOCL_LANG && user_settings.localLang && user_settings.localLang[label]) {
  100. if (JSON.stringify(user_settings.extLang) !== "{}") {
  101. set("extLang", {});
  102. }
  103. return user_settings.localLang[label];
  104. }
  105. if (!user_settings.GEN_LOCL_LANG && lang.ytlang && lang.ytlang !== "en_US") {
  106. if (user_settings.extLang[lang.ytlang] && user_settings.extLang[lang.ytlang][label]) {
  107. if (!lang.fetching && user_settings.extLang.nextCheck && user_settings.extLang.nextCheck <= new Date().getTime()) {
  108. lang.fetching = true;
  109. if (!is_userscript) {
  110. lang.observer = new MutationObserver(getLocale);
  111. lang.observer.observe(document.documentElement, {
  112. attributes: true,
  113. attributeFilter: ["data-setlocale"]
  114. });
  115. document.documentElement.dataset.getlocale = lang.ytlang;
  116. } else {
  117. localXHR("HEAD", checkModified, lang.urlBase + lang.ytlang + ".json", ["If-Modified-Since", new Date(user_settings.extLang[lang.ytlang].lastMod).toUTCString()]);
  118. }
  119. user_settings.extLang.nextCheck = new Date().getTime() + 6048E5;
  120. set("extLang", user_settings.extLang);
  121. }
  122. return user_settings.extLang[lang.ytlang][label];
  123. }
  124. if (!user_settings.extLang[lang.ytlang] && !lang.fetching && (!user_settings.extLang.nextCheck || user_settings.extLang.nextCheck <= new Date().getTime())) {
  125. lang.fetching = true;
  126. if (!is_userscript) {
  127. lang.observer = new MutationObserver(getLocale);
  128. lang.observer.observe(document.documentElement, {
  129. attributes: true,
  130. attributeFilter: ["data-setlocale"]
  131. });
  132. document.documentElement.dataset.getlocale = lang.ytlang;
  133. } else {
  134. localXHR("GET", getLanguage, lang.urlBase + lang.ytlang + ".json", ["Accept", "application/vnd.github.raw"]);
  135. }
  136. }
  137. }
  138. return language[label];
  139. }
  140. function setButton(obj) {
  141. var lnk, keys, temp;
  142. keys = Object.keys(obj);
  143. temp = document.createElement("template");
  144. temp.innerHTML = "<div class='blacklist'><button class='close ytplus_sprite'></button><a target='_blank'></a></div>";
  145. temp = temp.content.firstChild;
  146. lnk = temp.querySelector("a");
  147. lnk.href = "/channel/" + keys[0];
  148. lnk.setAttribute("title", obj[keys[0]]);
  149. lnk.textContent = obj[keys[0]];
  150. getBlacklist.blist.appendChild(temp);
  151. getBlacklist.blist.appendChild(document.createTextNode("\n"));
  152. }
  153. function sortList(previous, next){
  154. return previous[Object.keys(previous)[0]].localeCompare(next[Object.keys(next)[0]]);
  155. }
  156. function buildList(ytid) {
  157. var obj = {};
  158. obj[ytid] = getBlacklist.list[ytid];
  159. getBlacklist.sortAlpha.push(obj);
  160. }
  161. function getBlacklist(blist) {
  162. getBlacklist.blist = blist;
  163. getBlacklist.list = user_settings.blacklist;
  164. getBlacklist.sortAlpha = [];
  165. Object.keys(getBlacklist.list).forEach(buildList);
  166. getBlacklist.sortAlpha.sort(sortList).forEach(setButton);
  167. }
  168. function getValues(menu) {
  169. var i, ytp, list;
  170. if (user_settings) {
  171. list = menu.querySelector("#blacklist");
  172. if (list) {
  173. getBlacklist(list);
  174. }
  175. ytp = menu.querySelectorAll("input[id]");
  176. i = ytp.length;
  177. while (i--) {
  178. if (ytp[i].type === "checkbox" && user_settings[ytp[i].id] === true) {
  179. ytp[i].setAttribute("checked", "true");
  180. }
  181. if (ytp[i].type === "text" && typeof user_settings[ytp[i].id] === 'string') {
  182. ytp[i].setAttribute("value", user_settings[ytp[i].id]);
  183. }
  184. }
  185. ytp = menu.querySelectorAll("option");
  186. i = ytp.length;
  187. while (i--) {
  188. if (user_settings[ytp[i].parentNode.id] === ytp[i].value) {
  189. ytp[i].setAttribute("selected", "true");
  190. }
  191. }
  192. }
  193. return menu;
  194. }
  195. function getMenu(section) {
  196. var temp = document.createElement("template");
  197. if (section === "MEN") {
  198. temp.innerHTML = //
  199. `<div id='P-settings'>
  200. <div id='P-container'>
  201. <div id='P-sidebar'>
  202. <ul id='P-sidebar-list'>
  203. <li id='GEN' class='selected' data-p='tnd|GEN'></li>
  204. <li id='VID' data-p='tnd|VID'></li>
  205. <li id='BLK' data-p='tnd|BLK'></li>
  206. <li id='ABT' data-p='tnd|ABT'></li>
  207. <li id='HLP'><a target='_blank' href='https://github.com/ParticleCore/Particle/wiki' data-p='tnd|HLP'></a></li>
  208. <li id='DNT'><a title='PayPal' target='_blank' href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UMVQJJFG4BFHW' data-p='tnd|DNT'></a></li>
  209. </ul>
  210. </div>
  211. </div>
  212. </div>`;
  213. } else if (section === "GEN") {
  214. temp.innerHTML = //
  215. `<div id='P-content'>
  216. <div class='P-header'>
  217. <button class='P-save' data-p='tnd|GLB_SVE'></button>
  218. <button class='P-reset' data-p='tnd|GLB_RSET'></button>
  219. <button class='P-impexp ytplus_sprite' data-p='ttl|GLB_IMPR'></button>
  220. <button class='P-implang' data-p='ttl|GLB_LOCL_LANG&tnd|LOCALE'></button>
  221. <h2 data-p='tnd|GEN_TTL'></h2>
  222. </div>
  223. <hr class='P-horz'></hr>
  224. <h3 data-p='tnd|GEN_GEN'></h3>
  225. <div><input id='GEN_LOCL_LANG' type='checkbox'></input><label for='GEN_LOCL_LANG' data-p='tnd|GEN_LOCL_LANG'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#custom_lang' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  226. <div><input id='GEN_DSBL_ADS' type='checkbox'></input><label for='GEN_DSBL_ADS' data-p='tnd|GEN_DSBL_ADS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#outside_ads' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  227. <div><input id='GEN_YT_LOGO_LINK' type='checkbox'></input><label for='GEN_YT_LOGO_LINK' data-p='tnd|GEN_YT_LOGO_LINK'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#logo_redirect' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  228. <div><input id='GEN_SUB_LIST' type='checkbox'></input><label for='GEN_SUB_LIST' data-p='tnd|GEN_SUB_LIST'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#sub_playlist' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  229. <div><input id='GEN_INF_SCRL' type='checkbox'></input><label for='GEN_INF_SCRL' data-p='tnd|GEN_INF_SCRL'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#infinite_scroll' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  230. <div><input id='GEN_PPOT_ON' type='checkbox'></input><label for='GEN_PPOT_ON' data-p='tnd|GEN_PPOT_ON'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#popout_on' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  231. <div><input id='GEN_REM_APUN' type='checkbox'></input><label for='GEN_REM_APUN' data-p='tnd|GEN_REM_APUN'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#remove_autoplay' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  232. <div><input id='GEN_SPF_OFF' type='checkbox'></input><label for='GEN_SPF_OFF' data-p='tnd|GEN_SPF_OFF'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#spf_off' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  233. <div>
  234. <label for='GEN_CHN_DFLT_PAGE' data-p='tnd|GEN_CHN_DFLT_PAGE'></label>
  235. <div class='P-select'>
  236. <select id='GEN_CHN_DFLT_PAGE'>
  237. <option value='default' data-p='tnd|GEN_CHN_DFLT_PAGE_DFLT'></option>
  238. <option value='videos' data-p='tnd|GEN_CHN_DFLT_PAGE_VID'></option>
  239. <option value='playlists' data-p='tnd|GEN_CHN_DFLT_PAGE_PL'></option>
  240. <option value='channels' data-p='tnd|GEN_CHN_DFLT_PAGE_CHN'></option>
  241. <option value='discussion' data-p='tnd|GEN_CHN_DFLT_PAGE_DISC'></option>
  242. <option value='about' data-p='tnd|GEN_CHN_DFLT_PAGE_ABT'></option>
  243. </select>
  244. </div>\n
  245. <a href='https://github.com/ParticleCore/Particle/wiki/Features#channel_page' data-p='ttl|FTR_DESC' target='features'>?</a>
  246. </div>
  247. <h3 data-p='tnd|GEN_LYT'></h3>
  248. <div><input id='GEN_GRID_SUBS' type='checkbox'></input><label for='GEN_GRID_SUBS' data-p='tnd|GEN_GRID_SUBS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#sub_grid' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  249. <div><input id='GEN_GRID_SRCH' type='checkbox'></input><label for='GEN_GRID_SRCH' data-p='tnd|GEN_GRID_SRCH'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#search_grid' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  250. <div><input id='GEN_BTTR_NTF' type='checkbox'></input><label for='GEN_BTTR_NTF' data-p='tnd|GEN_BTTR_NTF'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#blue_box' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  251. <div><input id='GEN_DSB_HVRC' type='checkbox'></input><label for='GEN_DSB_HVRC' data-p='tnd|GEN_DSB_HVRC'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#hovercards_off' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  252. <div><input id='GEN_CMPT_TTLS' type='checkbox'></input><label for='GEN_CMPT_TTLS' data-p='tnd|GEN_CMPT_TTLS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#feed_titles' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  253. <div><input id='GEN_BLUE_GLOW' type='checkbox'></input><label for='GEN_BLUE_GLOW' data-p='tnd|GEN_BLUE_GLOW'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#blue_glow' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  254. <div><input id='GEN_HIDE_FTR' type='checkbox'></input><label for='GEN_HIDE_FTR' data-p='tnd|GEN_HIDE_FTR'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#hide_footer' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  255. <div><input id='GEN_HDE_RECM_SDBR' type='checkbox'></input><label for='GEN_HDE_RECM_SDBR' data-p='tnd|GEN_HDE_RECM_SDBR'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#hide_recom_sidebar' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  256. <div><input id='GEN_HDE_SRCH_SDBR' type='checkbox'></input><label for='GEN_HDE_SRCH_SDBR' data-p='tnd|GEN_HDE_SRCH_SDBR'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#hide_search_sidebar' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  257. <div><input id='GEN_HDE_CHN_SDBR' type='checkbox'></input><label for='GEN_HDE_CHN_SDBR' data-p='tnd|GEN_HDE_CHN_SDBR'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#hide_channel_sidebar' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  258. </div>`;
  259. if (user_settings.GEN_LOCL_LANG && user_settings.localLang) {
  260. temp.content.querySelector(".P-implang").dataset.p = "GLB_LOCL_LANG_CSTM";
  261. }
  262. } else if (section === "VID") {
  263. temp.innerHTML = //
  264. `<div id='P-content'>
  265. <div class='P-header'>
  266. <button class='P-save' data-p='tnd|GLB_SVE'></button>
  267. <button class='P-reset' data-p='tnd|GLB_RSET'></button>
  268. <button class='P-impexp ytplus_sprite' data-p='ttl|GLB_IMPR'></button>
  269. <button class='P-implang' data-p='ttl|GLB_LOCL_LANG&tnd|LOCALE'></button>
  270. <h2 data-p='tnd|VID_TTL'></h2>
  271. </div>
  272. <hr class='P-horz'></hr>
  273. <h3 data-p='tnd|VID_PLR'></h3>
  274. <div><input id='VID_PLR_ADS' type='checkbox'></input><label for='VID_PLR_ADS' data-p='tnd|VID_PLR_ADS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#video_ads' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  275. <div><input id='VID_SUB_ADS' type='checkbox'></input><label for='VID_SUB_ADS' data-p='tnd|VID_SUB_ADS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#subs_ads_on' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  276. <div><input id='VID_PLR_ALVIS' type='checkbox'></input><label for='VID_PLR_ALVIS' data-p='tnd|VID_PLR_ALVIS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#floating_player' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  277. <div><input id='VID_PLR_ALVIS_WDTH' type='text' placeholder='350' size='6'></input><label for='VID_PLR_ALVIS_WDTH' data-p='tnd|VID_PLR_ALVIS_WDTH'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#floating_player_width' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  278. <div><input id='VID_PLR_ATPL' type='checkbox'></input><label for='VID_PLR_ATPL' data-p='tnd|VID_PLR_ATPL'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#video_autoplay' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  279. <div><input id='VID_PLR_CC' type='checkbox'></input><label for='VID_PLR_CC' data-p='tnd|VID_PLR_CC'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#subtitles_off' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  280. <div><input id='VID_PLR_ANTS' type='checkbox'></input><label for='VID_PLR_ANTS' data-p='tnd|VID_PLR_ANTS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#annotations_off' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  281. <div><input id='VID_END_SHRE' type='checkbox'></input><label for='VID_END_SHRE' data-p='tnd|VID_END_SHRE'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#share_panel_off' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  282. <div><input id='VID_PLR_VOL_MEM' type='checkbox'></input><label for='VID_PLR_VOL_MEM' data-p='tnd|VID_PLR_VOL_MEM'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#remember_volume' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  283. <div><input id='VID_PLR_VOL_LDN' type='checkbox'></input><label for='VID_PLR_VOL_LDN' data-p='tnd|VID_PLR_VOL_LDN'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#disable_normalisation' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  284. <div><input id='VID_PLR_ALACT' type='checkbox'></input><label for='VID_PLR_ALACT' data-p='tnd|VID_PLR_ALACT'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#shortcuts_on' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  285. <div><input id='VID_PLR_SIZE_MEM' type='checkbox'></input><label for='VID_PLR_SIZE_MEM' data-p='tnd|VID_PLR_SIZE_MEM'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#remember_mode' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  286. <div><input id='VID_VOL_WHEEL' type='checkbox'></input><label for='VID_VOL_WHEEL' data-p='tnd|VID_VOL_WHEEL'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#wheel_volume' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  287. <div><input id='VID_PLR_DASH' type='checkbox'></input><label for='VID_PLR_DASH' data-p='tnd|VID_PLR_DASH'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#dash_off' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  288. <div><input id='VID_PLR_HFR' type='checkbox'></input><label for='VID_PLR_HFR' data-p='tnd|VID_PLR_HFR'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#hfr_off' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  289. <div><input id='VID_PLR_HTML5' type='checkbox'></input><label for='VID_PLR_HTML5' data-p='tnd|VID_PLR_HTML5'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#force_html5' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  290. <div>
  291. <label for='VID_DFLT_QLTY' data-p='tnd|VID_DFLT_QLTY'></label>
  292. <div class='P-select'>
  293. <select id='VID_DFLT_QLTY'>
  294. <option value='auto' data-p='tnd|VID_DFLT_QLTY_AUTO'></option>
  295. <option value='highres' data-p='tnd|VID_DFLT_QLTY_ORIG'></option>
  296. <option value='hd2880' data-p='tnd|VID_DFLT_QLTY_2880'></option>
  297. <option value='hd2160' data-p='tnd|VID_DFLT_QLTY_2160'></option>
  298. <option value='hd1440' data-p='tnd|VID_DFLT_QLTY_1440'></option>
  299. <option value='hd1080' data-p='tnd|VID_DFLT_QLTY_1080'></option>
  300. <option value='hd720' data-p='tnd|VID_DFLT_QLTY_720'></option>
  301. <option value='large' data-p='tnd|VID_DFLT_QLTY_LRG'></option>
  302. <option value='medium' data-p='tnd|VID_DFLT_QLTY_MDM'></option>
  303. <option value='small' data-p='tnd|VID_DFLT_QLTY_SML'></option>
  304. <option value='tiny' data-p='tnd|VID_DFLT_QLTY_TNY'></option>
  305. </select>
  306. </div>\n
  307. <a href='https://github.com/ParticleCore/Particle/wiki/Features#default_quality' data-p='ttl|FTR_DESC' target='features'>?</a>
  308. </div>
  309. <h3 data-p='tnd|VID_PLR_LYT'></h3>
  310. <div><input id='VID_PLR_INFO' type='checkbox'></input><label for='VID_PLR_INFO' data-p='tnd|VID_PLR_INFO'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#info_bar' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  311. <div><input id='VID_PLR_DYN_SIZE' type='checkbox'></input><label for='VID_PLR_DYN_SIZE' data-p='tnd|VID_PLR_DYN_SIZE'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#dynamic_size_off' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  312. <div><input id='VID_PLR_FIT' type='checkbox'></input><label for='VID_PLR_FIT' data-p='tnd|VID_PLR_FIT'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#fit_to_page' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  313. <div><input id='VID_PLR_FIT_WDTH' type='text' placeholder='1280px' size='6'></input><label for='VID_PLR_FIT_WDTH' data-p='tnd|VID_PLR_FIT_WDTH'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#fit_max_width' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  314. <h3 data-p='tnd|VID_PLST'></h3>
  315. <div><input id='VID_PLST_ATPL' type='checkbox'></input><label for='VID_PLST_ATPL' data-p='tnd|VID_PLST_ATPL'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#playlist_autoplay' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  316. <div><input id='VID_PLST_RVRS' type='checkbox'></input><label for='VID_PLST_RVRS' data-p='tnd|VID_PLST_RVRS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#playlist_reverse' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  317. <h3 data-p='tnd|VID_LAYT'></h3>
  318. <div><input id='VID_PPOT_SZ' type='text' placeholder='533' size='6'></input><label for='VID_PPOT_SZ' data-p='tnd|VID_PPOT_SZ'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#popout_size' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  319. <div>
  320. <label for='VID_HIDE_COMS' data-p='tnd|VID_HIDE_COMS'></label>
  321. <div class='P-select'>
  322. <select id='VID_HIDE_COMS'>
  323. <option value='0' data-p='tnd|VID_HIDE_COMS_SHOW'></option>
  324. <option value='1' data-p='tnd|VID_HIDE_COMS_HIDE'></option>
  325. <option value='2' data-p='tnd|VID_HIDE_COMS_REM'></option>
  326. </select>
  327. </div>\n
  328. <a href='https://github.com/ParticleCore/Particle/wiki/Features#comments' data-p='ttl|FTR_DESC' target='features'>?</a>
  329. </div>
  330. <div><input id='VID_TTL_CMPT' type='checkbox'></input><label for='VID_TTL_CMPT' data-p='tnd|VID_TTL_CMPT'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#video_title' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  331. <div><input id='VID_DESC_SHRT' type='checkbox'></input><label for='VID_DESC_SHRT' data-p='tnd|VID_DESC_SHRT'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#labelless_buttons' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  332. <div><input id='VID_VID_CNT' type='checkbox'></input><label for='VID_VID_CNT' data-p='tnd|VID_VID_CNT'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#upload_counter' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  333. <div><input id='VID_POST_TIME' type='checkbox'></input><label for='VID_POST_TIME' data-p='tnd|VID_POST_TIME'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#relative_upload_time' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  334. <div><input id='VID_HIDE_DETLS' type='checkbox'></input><label for='VID_HIDE_DETLS' data-p='tnd|VID_HIDE_DETLS'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#hide_video_details' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  335. <div><input id='VID_LAYT_AUTO_PNL' type='checkbox'></input><label for='VID_LAYT_AUTO_PNL' data-p='tnd|VID_LAYT_AUTO_PNL'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#expand_description' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  336. </div>`;
  337. if (user_settings.GEN_LOCL_LANG && user_settings.localLang) {
  338. temp.content.querySelector(".P-implang").dataset.p = "GLB_LOCL_LANG_CSTM";
  339. }
  340. } else if (section === "BLK") {
  341. temp.innerHTML = //
  342. `<div id='P-content'>
  343. <div class='P-header'>
  344. <button class='P-save' data-p='tnd|GLB_SVE'></button>
  345. <button class='P-reset' data-p='tnd|GLB_RSET'></button>
  346. <button class='P-impexp ytplus_sprite' data-p='ttl|GLB_IMPR'></button>
  347. <button class='P-implang' data-p='ttl|GLB_LOCL_LANG&tnd|LOCALE'></button>
  348. <h2 data-p='tnd|BLK_TTL'></h2>
  349. </div>
  350. <hr class='P-horz'></hr>
  351. <h3 data-p='tnd|BLK_BLK'></h3>
  352. <div><input id='BLK_ON' type='checkbox'></input><label for='BLK_ON' data-p='tnd|BLK_ON'></label>\n<a href='https://github.com/ParticleCore/Particle/wiki/Features#blacklist_on' data-p='ttl|FTR_DESC' target='features'>?</a></div>
  353. <div id='blacklist'>
  354. <div id='blacklist-controls'>
  355. <button id='blacklist-edit' class='yt-uix-button yt-uix-sessionlink yt-uix-button-default yt-uix-button-size-default'><span class='yt-uix-button-content' data-p='tnd|BLCK_EDIT'></span></button>
  356. <button id='blacklist-save' class='yt-uix-button yt-uix-sessionlink yt-uix-button-default yt-uix-button-size-default'><span class='yt-uix-button-content' data-p='tnd|BLCK_SAVE'></span></button>
  357. <button id='blacklist-close' class='yt-uix-button yt-uix-sessionlink yt-uix-button-default yt-uix-button-size-default'><span class='yt-uix-button-content' data-p='tnd|BLCK_CLSE'></span></button>
  358. </div>
  359. <textarea id='blacklist-edit-list'></textarea>
  360. </div>
  361. <br></br>
  362. </div>`;
  363. if (user_settings.GEN_LOCL_LANG && user_settings.localLang) {
  364. temp.content.querySelector(".P-implang").dataset.p = "ttl|GLB_LOCL_LANG&tnd|GLB_LOCL_LANG_CSTM";
  365. }
  366. } else if (section === "ABT") {
  367. temp.innerHTML = //
  368. `<div id='P-content'>
  369. <div class='P-header'>
  370. <h2 data-p='tnd|ABT_TTL'></h2>
  371. </div>
  372. <hr class='P-horz'></hr>
  373. <h3 data-p='tnd|ABT_THKS'></h3>
  374. <div><a target='_blank' href='https://github.com/YePpHa'>Jeppe Rune Mortensen</a><span data-p='tnd|ABT_THKS_YEPPHA'></span></div>
  375. <div><a target='_blank' href='http://www.greasespot.net/'>Greasemonkey</a> + <a href='http://tampermonkey.net/'>Tampermonkey</a><span data-p='tnd|ABT_THKS_USERSCRIPT'></span></div>
  376. <div><a target='_blank' href='http://stackoverflow.com/'>Stack Overflow</a><span data-p='tnd|ABT_THKS_STACKOV'></span></div>
  377. <h3 data-p='tnd|ABT_INFO'></h3>
  378. <div><a target='_blank' href='https://github.com/ParticleCore/Particle/'>GitHub</a></div>
  379. <div><a target='_blank' href='https://gf.qytechs.cn/en/users/8223-particlecore'>Greasy Fork镜像</a></div>
  380. <div><a target='_blank' href='http://openuserjs.org/scripts/ParticleCore/'>OpenUserJS</a></div>
  381. </div>`;
  382. }
  383. return setLocale(getValues(temp.content));
  384. }
  385. function exportSettings(target) {
  386. var expCont;
  387. if (target.classList.contains("P-impexp") || target.classList.contains("P-implang")) {
  388. expCont = document.getElementById("exp-cont");
  389. if (expCont) {
  390. expCont.remove();
  391. return;
  392. }
  393. expCont = document.createElement("template");
  394. expCont.innerHTML = //
  395. `<div id='exp-cont'>
  396. <button id='implang-save' class='yt-uix-button yt-uix-sessionlink yt-uix-button-default yt-uix-button-size-default'>
  397. <span class='yt-uix-button-content' data-p='tnd|GLB_IMPR_SAVE'></span>
  398. </button>
  399. <textarea id='impexp-list'></textarea>
  400. </div>`;
  401. if (target.classList.contains("P-impexp")) {
  402. expCont.content.querySelector("#implang-save").id = "impexp-save";
  403. }
  404. expCont = setLocale(expCont.content).firstChild;
  405. document.getElementById("P-content").appendChild(expCont);
  406. document.getElementById("impexp-list").value = JSON.stringify((target.classList.contains("P-impexp") && user_settings) || user_settings.localLang || language, undefined, 2);
  407. } else if (target.id === "impexp-save" || target.id === "implang-save") {
  408. if (target.id === "implang-save") {
  409. set("localLang", JSON.parse(document.getElementById("impexp-list").value));
  410. window.location.reload();
  411. } else {
  412. set("user_settings", JSON.parse(document.getElementById("impexp-list").value));
  413. settingsMenu.settingsButton.click();
  414. settingsMenu.settingsButton.click();
  415. }
  416. }
  417. }
  418. function setBlackList(target) {
  419. if (target.id === "blacklist-edit") {
  420. document.getElementById("blacklist").classList.add("edit");
  421. document.getElementById("blacklist-edit-list").value = JSON.stringify(user_settings.blacklist, undefined, 2);
  422. } else if (target.id === "blacklist-save") {
  423. set("blacklist", JSON.parse(document.getElementById("blacklist-edit-list").value));
  424. } else if (target.id === "blacklist-close") {
  425. document.getElementById("BLK").click();
  426. }
  427. }
  428. function delBlackList(event) {
  429. var newKey = user_settings.blacklist;
  430. delete newKey[event.target.nextSibling.href.split("/channel/")[1]];
  431. event.target.parentNode.remove();
  432. set("blacklist", newKey);
  433. }
  434. function delNotification() {
  435. document.body.classList.remove("show-guide-button-notification");
  436. }
  437. function saveSettings(salt) {
  438. var i, value, notification, navId, userSets, savedSets;
  439. navId = document.querySelector(".selected").id;
  440. userSets = document.getElementById("P-content").querySelectorAll("[id^='" + navId + "']");
  441. savedSets = user_settings;
  442. i = userSets.length;
  443. while (i--) {
  444. value = (userSets[i].checked && (userSets[i].value === "on" || userSets[i].value)) || (userSets[i].length && userSets[i].value) || (userSets[i].getAttribute("type") === "text" && userSets[i].value);
  445. if (value) {
  446. savedSets[userSets[i].name || userSets[i].id] = value;
  447. } else if (!value && userSets[i].type !== "radio") {
  448. savedSets[userSets[i].id] = false;
  449. }
  450. }
  451. set("user_settings", savedSets);
  452. customStyles();
  453. if (!salt) {
  454. notification = document.getElementById("appbar-main-guide-notification-container");
  455. if (notification.childNodes.length < 1) {
  456. notification.remove();
  457. notification = document.createElement("template");
  458. notification.innerHTML = //
  459. `<div id='appbar-main-guide-notification-container'>
  460. <div class='appbar-guide-notification' role='alert'>
  461. <span class='appbar-guide-notification-content-wrapper yt-valign'>
  462. <span class='appbar-guide-notification-icon yt-sprite'></span>
  463. <span class='appbar-guide-notification-text-content'></span>
  464. </span>
  465. </div>
  466. </div>`;
  467. notification = setLocale(notification.content).firstChild;
  468. document.querySelector(".yt-masthead-logo-container").appendChild(notification);
  469. }
  470. document.querySelector(".appbar-guide-notification-text-content").textContent = lang("GLB_SVE_SETS");
  471. document.body.classList.add("show-guide-button-notification");
  472. window.setTimeout(delNotification, 2000);
  473. }
  474. }
  475. function navigateSettings(event) {
  476. if (event.target.classList.contains("P-save")) {
  477. saveSettings();
  478. } else if (event.target.classList.contains("P-reset")) {
  479. set("user_settings", default_settings);
  480. settingsMenu.settingsButton.click();
  481. settingsMenu.settingsButton.click();
  482. } else if (event.target.classList.contains("close")) {
  483. delBlackList(event);
  484. } else if (event.target.classList.contains("P-impexp") || event.target.id === "impexp-save" || event.target.classList.contains("P-implang") || event.target.id === "implang-save") {
  485. exportSettings(event.target);
  486. } else if (event.target.id === "blacklist-edit" || event.target.id === "blacklist-save" || event.target.id === "blacklist-close") {
  487. setBlackList(event.target);
  488. } else if (event.target.id === "P-container" || event.target.id === "P-settings") {
  489. event = (event.target.id === "P-settings") ? event.target : event.target.parentNode;
  490. event.remove();
  491. document[(window.chrome && "body") || "documentElement"].scrollTop = 0;
  492. } else if (event.target.id !== "DNT" && event.target.tagName !== "A" && event.target.parentNode.id === "P-sidebar-list") {
  493. saveSettings("no-notification");
  494. document.getElementById("P-content").remove();
  495. document.getElementById("P-container").appendChild(getMenu(event.target.id));
  496. event.target.parentNode.querySelector(".selected").removeAttribute("class");
  497. event.target.className = "selected";
  498. }
  499. }
  500. function settingsTemplate(event) {
  501. var pWrapper;
  502. if (event.target.id === "P" && event.target.tagName !== "INPUT") {
  503. pWrapper = document.getElementById("P-settings");
  504. if (pWrapper) {
  505. pWrapper.remove();
  506. } else {
  507. if (document.documentElement.classList.contains("floater")) {
  508. document.documentElement.classList.remove("floater");
  509. document.getElementById("movie_player").removeAttribute("style");
  510. window.dispatchEvent(new Event("resize"));
  511. }
  512. pWrapper = getMenu("MEN");
  513. pWrapper.querySelector("#P-container").appendChild(getMenu("GEN"));
  514. document.getElementById("body-container").insertBefore(pWrapper, document.getElementById("page-container"));
  515. document.addEventListener("click", navigateSettings);
  516. }
  517. document[(window.chrome && "body") || "documentElement"].scrollTop = 0;
  518. }
  519. }
  520. function firstTime(event) {
  521. if (event && event.target && event.target.parentNode && event.target.parentNode.className === "par_closewlcm") {
  522. set("firstTime", false);
  523. document.removeEventListener("click", firstTime);
  524. settingsMenu.welcome.style.display = "none";
  525. }
  526. }
  527. function settingsMenu() {
  528. var notif_button, settings_button, welcome_message;
  529. if (settingsMenu.settingsButton) {
  530. return;
  531. }
  532. notif_button = document.querySelector(".notifications-container");
  533. settings_button = document.querySelector("#yt-masthead-user, #yt-masthead-signin");
  534. if (settings_button) {
  535. welcome_message = document.createElement("template");
  536. welcome_message.innerHTML = //
  537. `<div id='Psettings' style='display:inline-block;position:relative'>
  538. <button id='P' class='ytplus_sprite' data-p='ttl|YTSETS'></button>
  539. <div id='part_welcome' style='display:none;margin-left:-220px;top:38px;right:0'>
  540. <span data-p='tnd|WLCM'></span>
  541. <br></br>
  542. <span data-p='tnd|WLCMSTRT'></span>
  543. <br></br><br></br>
  544. <a data-p='tnd|WLCMFTRS' style='color:#FFF;' href='https://github.com/ParticleCore/Particle/wiki/Features' target='_blank'></a>
  545. <div class='par_closewlcm'><span>×</span></div>
  546. </div>
  547. </div>`;
  548. welcome_message = setLocale(welcome_message.content);
  549. document.addEventListener("click", settingsTemplate);
  550. if (notif_button) {
  551. settings_button.insertBefore(welcome_message, notif_button);
  552. } else {
  553. settings_button.appendChild(welcome_message);
  554. }
  555. settingsMenu.settingsButton = document.getElementById("P");
  556. settingsMenu.welcome = document.getElementById("part_welcome");
  557. if (user_settings.firstTime) {
  558. settingsMenu.welcome.style.display = "block";
  559. document.addEventListener("click", firstTime);
  560. }
  561. }
  562. }
  563. function modComment(original) {
  564. return function (a) {
  565. var comments, is_live;
  566. comments = document.getElementById("watch-discussion");
  567. is_live = window.ytplayer && window.ytplayer.config && window.ytplayer.config.args && window.ytplayer.config.args.livestream;
  568. if (a.split("comments").length > 1 && !is_live && comments && !comments.lazyload && user_settings.VID_HIDE_COMS === "1" && !comments.classList.contains("show")) {
  569. comments.lazyload = arguments;
  570. } else {
  571. return original.apply(this, arguments);
  572. }
  573. };
  574. }
  575. function modSetConfig(original) {
  576. return function (a) {
  577. if (typeof a === "object") {
  578. if ("SHARE_ON_VIDEO_END" in a) {
  579. a.SHARE_ON_VIDEO_END = !user_settings.VID_END_SHRE;
  580. }
  581. if ("UNIVERSAL_HOVERCARDS" in a) {
  582. a.UNIVERSAL_HOVERCARDS = !user_settings.GEN_DSB_HVRC;
  583. }
  584. }
  585. original.apply(scriptExit, arguments);
  586. };
  587. }
  588. function modEmbed(original) {
  589. return function (a, b) {
  590. var temp, player;
  591. b = modArgs(b);
  592. temp = original.apply(scriptExit, arguments);
  593. player = document.getElementById("movie_player");
  594. if (player) {
  595. player.setPlaybackQuality(user_settings.VID_DFLT_QLTY);
  596. }
  597. return temp;
  598. };
  599. }
  600. function modAutoplay(original) {
  601. return function (a, b) {
  602. if (!b || user_settings.plApl || (!user_settings.plApl && b.feature && b.feature !== "autoplay")) {
  603. original.apply(scriptExit, arguments);
  604. }
  605. };
  606. }
  607. function modAutoplayFullscreen(original) {
  608. return function () {
  609. var has_ended, next_button, next_clicked;
  610. has_ended = api && api.getCurrentTime && Math.round(api.getCurrentTime()) >= Math.floor(api.getDuration());
  611. next_clicked = document.activeElement.classList.contains("ytp-button-next") || document.activeElement.classList.contains("ytp-next-button");
  612. if (!user_settings.plApl && !next_clicked && has_ended) {
  613. next_button = document.querySelector(".ytp-next-button");
  614. if (next_button && next_button.getAttribute("aria-disabled") === "true") {
  615. next_button.onclick = api.nextVideo;
  616. document.addEventListener("click", api.nextVideo);
  617. next_button.setAttribute("aria-disabled", "false");
  618. }
  619. return false;
  620. }
  621. if (user_settings.plApl || next_clicked || !has_ended) {
  622. if (next_clicked) {
  623. document.getElementById("movie_player").focus();
  624. }
  625. return original.apply(this, arguments);
  626. }
  627. };
  628. }
  629. function iterateKeys(keys) {
  630. if (typeof player_instance[keys] === "object") {
  631. if (player_instance[keys] && player_instance[keys].hasNext) {
  632. player_instance[keys].hasNext = modAutoplayFullscreen(player_instance[keys].hasNext);
  633. return true;
  634. }
  635. }
  636. }
  637. function modPlayerCreate(original) {
  638. return function (a, b) {
  639. var player;
  640. b = modArgs(b);
  641. if (a.id === "upsell-video") {
  642. original.apply(scriptExit, arguments);
  643. } else if (typeof a === "object") {
  644. player_instance = original.apply(scriptExit, arguments);
  645. Object.keys(player_instance).some(iterateKeys);
  646. player = document.getElementById("movie_player");
  647. if (!user_settings.VID_PLR_ATPL && player) {
  648. if (window.ytplayer.config.args.dvmap && !user_settings.VID_PLR_ADS) {
  649. window.ytplayer.config.args.vmap = window.ytplayer.config.args.dvmap;
  650. }
  651. player.cueVideoByPlayerVars(window.ytplayer.config.args);
  652. }
  653. }
  654. };
  655. }
  656. function modSeekTo(original) {
  657. return function(time) {
  658. if (document.documentElement.classList.contains("floater")) {
  659. original.call(this, time, false);
  660. } else {
  661. original.apply(this, arguments);
  662. }
  663. };
  664. }
  665. function setMods(keys) {
  666. var str;
  667. if (typeof window._yt_www[keys] === "function") {
  668. str = String(window._yt_www[keys]);
  669. if (str.split("player-added").length > 1) {
  670. window._yt_www[keys] = modEmbed(window._yt_www[keys]);
  671. } else if (str.split("window.spf.navigate").length > 1) {
  672. window._yt_www[keys] = modAutoplay(window._yt_www[keys]);
  673. } else if (str.split(".set(\"\"+a,b,c,\"/\",d").length > 1) {
  674. window.ytpsetwide = window._yt_www[keys];
  675. }
  676. }
  677. }
  678. function scriptExit(event) {
  679. if (event && event.target) {
  680. if (event.target.getAttribute("name") === "www/base") {
  681. window.yt.setConfig = modSetConfig(window.yt.setConfig);
  682. Object.keys(window._yt_www).forEach(setMods);
  683. }
  684. if (event.target.getAttribute("name") === "www/watch") {
  685. window.yt.www.watch.player.seekTo = modSeekTo(window.yt.www.watch.player.seekTo);
  686. }
  687. if (event.target.getAttribute("name") === "spf/spf") {
  688. window.spf.load = modComment(window.spf.load);
  689. window.spf.prefetch = function(){return;};
  690. if (window.name === "popOut") {
  691. window.spf.navigate = function(){return;};
  692. }
  693. }
  694. }
  695. if ((event && event.target && event.target.getAttribute("name") === "player/base") || (!window.html5Patched && window.yt && window.yt.player && window.yt.player.Application && window.yt.player.Application.create)) {
  696. window.html5Patched = true;
  697. window.yt.player.Application.create = modPlayerCreate(window.yt.player.Application.create);
  698. }
  699. }
  700. function checkBounds(elm, X, Y) {
  701. var snapX, snapY;
  702. if (X > -1 && X + elm.offsetWidth < document.documentElement.offsetWidth) {
  703. snapX = false;
  704. } else if (X < 1) {
  705. X = 0;
  706. snapX = -1;
  707. } else {
  708. X = document.documentElement.offsetWidth - elm.offsetWidth;
  709. snapX = 1;
  710. }
  711. if (Y > 51 && Y + elm.offsetHeight < document.documentElement.offsetHeight) {
  712. snapY = false;
  713. } else if (Y < 52) {
  714. Y = 52;
  715. snapY = -1;
  716. } else {
  717. Y = document.documentElement.offsetHeight - elm.offsetHeight;
  718. snapY = 1;
  719. }
  720. return {X: X + "px", Y: Y + "px", snapX: snapX, snapY: snapY};
  721. }
  722. function updatePos() {
  723. var x, y, height, player, bounds;
  724. if (!document.documentElement.classList.contains("floater")) {
  725. window.removeEventListener("resize", updatePos);
  726. return;
  727. }
  728. height = parseInt(user_settings.VID_PLR_ALVIS_WDTH) || 350;
  729. player = document.getElementById("movie_player");
  730. bounds = checkBounds(player, user_settings.floaterX, user_settings.floaterY);
  731. height = (height < 350 ? 350 : height) / (16 / 9);
  732. if (user_settings.floaterSnapX === -1) {
  733. x = "0px";
  734. } else if (user_settings.floaterSnapX === 1) {
  735. x = document.documentElement.offsetWidth - player.offsetWidth + "px";
  736. } else {
  737. x = bounds.X;
  738. }
  739. if (user_settings.floaterSnapY === -1) {
  740. y = "52px";
  741. } else if (user_settings.floaterSnapY === 1) {
  742. y = document.documentElement.offsetHeight - player.offsetHeight + "px";
  743. } else {
  744. y = bounds.Y;
  745. }
  746. player.setAttribute("style", "width:" + (height * (16 / 9)) + "px;height:" + height + "px;left:" + x + ";top:" + y);
  747. }
  748. function dragFloater(event) {
  749. var excluded, isFScreen, isFloater, bounds, player;
  750. isFScreen = document.querySelector(".ytp-fullscreen");
  751. isFloater = document.documentElement.classList.contains("floater");
  752. if (event && !isFScreen && isFloater) {
  753. if (event.type === "click" && event.target.id === "part_floaterui_scrolltop") {
  754. document[(window.chrome && "body") || "documentElement"].scrollTop = 0;
  755. } else {
  756. player = document.getElementById("movie_player");
  757. if (event.buttons === 1) {
  758. excluded = document.querySelector(".ytp-chrome-bottom");
  759. if (event.type === "mousedown" && player.contains(event.target) && (!excluded || !excluded.contains(event.target))) {
  760. event.preventDefault();
  761. event.stopPropagation();
  762. document.addEventListener("mousemove", dragFloater);
  763. document.addEventListener("click", dragFloater, true);
  764. dragFloater.oldPos = {
  765. X: parseInt(player.style.left) - event.clientX,
  766. Y: parseInt(player.style.top) - event.clientY,
  767. orgX: event.clientX,
  768. orgY: event.clientY
  769. };
  770. } else if (event.type === "mousemove" && (dragFloater.hasMoved || Math.abs(event.clientX - dragFloater.oldPos.orgX) > 10 || Math.abs(event.clientY - dragFloater.oldPos.orgY) > 10)) {
  771. bounds = checkBounds(player, event.clientX + dragFloater.oldPos.X, event.clientY + dragFloater.oldPos.Y);
  772. player.style.left = bounds.X;
  773. player.style.top = bounds.Y;
  774. dragFloater.hasMoved = true;
  775. dragFloater.snapX = bounds.snapX;
  776. dragFloater.snapY = bounds.snapY;
  777. }
  778. }
  779. if (event.buttons !== 1 || event.type === "click") {
  780. if (dragFloater.hasMoved) {
  781. event.preventDefault();
  782. event.stopImmediatePropagation();
  783. dragFloater.oldPos = false;
  784. dragFloater.hasMoved = false;
  785. user_settings.floaterX = parseInt(player.style.left);
  786. user_settings.floaterY = parseInt(player.style.top);
  787. user_settings.floaterSnapX = dragFloater.snapX;
  788. user_settings.floaterSnapY = dragFloater.snapY;
  789. set("user_settings", user_settings);
  790. }
  791. document.removeEventListener("mousemove", dragFloater);
  792. document.removeEventListener("click", dragFloater, true);
  793. }
  794. }
  795. }
  796. }
  797. function iniFloater() {
  798. var player, plrApi, out_of_sight, isFloater, isFScreen, floaterUI, settings_open;
  799. player = document.getElementById("movie_player");
  800. plrApi = document.getElementById("player-api").getBoundingClientRect();
  801. settings_open = document.getElementById("P-settings");
  802. if (player) {
  803. out_of_sight = plrApi.bottom < ((plrApi.height / 2) + 52);
  804. isFloater = document.documentElement.classList.contains("floater");
  805. isFScreen = document.querySelector(".ytp-fullscreen");
  806. floaterUI = document.getElementById("part_floaterui");
  807. if (!floaterUI && !isFScreen) {
  808. floaterUI = document.createElement("template");
  809. floaterUI.innerHTML = //
  810. `<div id='part_floaterui'>
  811. <button id='part_floaterui_scrolltop' class='ytplus_sprite' data-p='ttl|VID_PLR_ALVIS_SCRL_TOP'></button>
  812. </div>`;
  813. floaterUI = setLocale(floaterUI.content).firstChild;
  814. document.addEventListener("mousedown", dragFloater);
  815. player.appendChild(floaterUI);
  816. }
  817. if (out_of_sight && !isFloater && !settings_open) {
  818. document.documentElement.classList.add("floater");
  819. window.addEventListener("resize", updatePos);
  820. updatePos();
  821. window.dispatchEvent(new Event("resize"));
  822. } else if ((!out_of_sight || settings_open) && isFloater) {
  823. document.documentElement.classList.remove("floater");
  824. window.removeEventListener("resize", updatePos);
  825. player.removeAttribute("style");
  826. window.dispatchEvent(new Event("resize"));
  827. }
  828. }
  829. }
  830. function alwaysVisible() {
  831. if (user_settings.VID_PLR_ALVIS) {
  832. if (window.location.pathname === "/watch") {
  833. window.addEventListener("scroll", iniFloater);
  834. } else if (window.location.pathname !== "/watch") {
  835. window.removeEventListener("scroll", iniFloater);
  836. }
  837. }
  838. }
  839. function alwaysActive(event) {
  840. var i, list, clear, length, eventClone;
  841. clear = window.location.pathname == "/watch" && api && api !== event.target && !api.contains(event.target) && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey && !event.target.isContentEditable;
  842. if (clear && ((event.which > 47 && event.which < 58) || (event.which > 95 && event.which < 106) || [27, 32, 35, 36, 37, 38, 39, 40, 66, 67, 79, 87, 187, 189].indexOf(event.which) > -1) && ["EMBED", "INPUT", "OBJECT", "TEXTAREA", "IFRAME"].indexOf(document.activeElement.tagName) < 0) {
  843. eventClone = new Event("keydown");
  844. list = Object.keys(Object.getPrototypeOf(event));
  845. length = list.length;
  846. for (i = 0; i < length; i++) {
  847. eventClone[list[i]] = event[list[i]];
  848. }
  849. event.preventDefault();
  850. api.dispatchEvent(eventClone);
  851. }
  852. }
  853. function playerState(event) {
  854. if (user_settings.fullBrs || user_settings.lightsOut) {
  855. document.documentElement.classList[(event < 5 && event > 0 && "add") || "remove"]((user_settings.fullBrs && "part_fullbrowser") || "0", (user_settings.lightsOut && "part_cinema_mode") || "0");
  856. }
  857. window.dispatchEvent(new Event("resize"));
  858. }
  859. function handleCustoms(event) {
  860. if (typeof event === "object") {
  861. set("volLev", event.volume);
  862. } else {
  863. set("theaterMode", event);
  864. }
  865. }
  866. function playerReady() {
  867. api = document.getElementById("movie_player");
  868. if (api && !document.getElementById("c4-player")) {
  869. api.addEventListener("onStateChange", playerState);
  870. if (user_settings.VID_PLR_VOL_MEM) {
  871. api.addEventListener("onVolumeChange", handleCustoms);
  872. }
  873. if (user_settings.VID_PLR_SIZE_MEM) {
  874. api.addEventListener("SIZE_CLICKED", handleCustoms);
  875. }
  876. if (user_settings.VID_PLR_VOL_MEM) {
  877. api.setVolume(user_settings.volLev);
  878. }
  879. if (user_settings.loopVid) {
  880. document.querySelector("video").loop = user_settings.loopVid;
  881. }
  882. if (user_settings.VID_PLR_ALACT) {
  883. document.addEventListener("keydown", alwaysActive);
  884. }
  885. }
  886. }
  887. function getThumb() {
  888. var args, base, thumb_url;
  889. args = window.ytplayer.config.args;
  890. base = (args.iurl_webp && "_webp") || "";
  891. thumb_url = args["iurlmaxres" + base] || args["iurlsd" + base] || args["iurl" + base];
  892. window.open(thumb_url);
  893. }
  894. function hideScreenshot(event) {
  895. if (event.target.id === "close-screenshot") {
  896. event.target.parentNode.remove();
  897. document.removeEventListener("click", hideScreenshot);
  898. }
  899. }
  900. function getScreenshot() {
  901. var width, height, aspectRatio, video, container, canvas, close, context;
  902. video = document.querySelector("video");
  903. container = document.getElementById("screenshot-result") || document.createElement("div");
  904. canvas = container.querySelector("canvas") || document.createElement("canvas");
  905. context = canvas.getContext("2d");
  906. aspectRatio = video.videoWidth / video.videoHeight;
  907. width = video.videoWidth;
  908. height = parseInt(width / aspectRatio, 10);
  909. canvas.width = width;
  910. canvas.height = height;
  911. context.drawImage(video, 0, 0, width, height);
  912. if (!container.id) {
  913. container.id = "screenshot-result";
  914. container.appendChild(canvas);
  915. close = document.createElement("div");
  916. close.id = "close-screenshot";
  917. close.textContent = lang("CNSL_SS_CLS");
  918. document.addEventListener("click", hideScreenshot);
  919. container.appendChild(close);
  920. document.body.appendChild(container);
  921. }
  922. }
  923. function exitFullBrowser(key) {
  924. if (document.documentElement.classList.contains("part_fullbrowser") && (key.keyCode === 27 || key.key === "Escape" || (key.target.className && key.target.className.split("ytp-size").length > 1))) {
  925. toggleFullBrowser(key);
  926. if (key.type === "mousedown") {
  927. document.removeEventListener("keydown", exitFullBrowser);
  928. document.removeEventListener("mousedown", exitFullBrowser);
  929. key.preventDefault();
  930. }
  931. }
  932. }
  933. function toggleFullBrowser(event) {
  934. var plrState = api && api.getPlayerState && api.getPlayerState();
  935. plrState = plrState < 5 && plrState > 0;
  936. document[(window.chrome && "body") || "documentElement"].scrollTop = 0;
  937. document.addEventListener("keydown", exitFullBrowser);
  938. document.addEventListener("mousedown", exitFullBrowser);
  939. set("fullBrs", event ? !user_settings.fullBrs : true);
  940. advancedOptions.full_browser.classList[(user_settings.fullBrs && "add") || "remove"]("active");
  941. if (event && (plrState || event.keyCode === 27 || event.key === "Escape")) {
  942. document.documentElement.classList[(user_settings.fullBrs && "add") || "remove"]("part_fullbrowser");
  943. window.dispatchEvent(new Event("resize"));
  944. }
  945. }
  946. function toggleFrames(event) {
  947. var i, pi, fps, temp;
  948. advancedOptions.frame_step = document.getElementById("framestep-button");
  949. if (event && ["EMBED", "INPUT", "OBJECT", "TEXTAREA"].indexOf(document.activeElement.tagName) < 0 && event.target.tagName !== "IFRAME" && !event.target.getAttribute("contenteditable")) {
  950. if ((event.keyCode === 37 || event.keyCode === 39) && event.shiftKey) {
  951. pi = api.getVideoStats().fmt;
  952. temp = window.ytplayer.config.args.adaptive_fmts.split(",");
  953. i = temp.length;
  954. while (i--) {
  955. if (temp[i].indexOf("itag=" + pi) > 0) {
  956. advancedOptions.fps = parseInt(temp[i].match(/fps=([\d]+)/)[1]);
  957. break;
  958. }
  959. }
  960. if (!advancedOptions.fps || advancedOptions.fps === 1) {
  961. advancedOptions.fps = 30;
  962. }
  963. fps = ((event.keyCode < 39 && -1) || 1) * ((advancedOptions.fps < 2 && 30) || advancedOptions.fps);
  964. if (fps && api) {
  965. if (!document.querySelector("video").paused) {
  966. api.pauseVideo();
  967. }
  968. api.seekBy(1 / fps);
  969. }
  970. event.preventDefault();
  971. event.stopImmediatePropagation();
  972. } else if (event.type === "click" && event.target.id === "framestep-button") {
  973. set("frame_step", !user_settings.frame_step);
  974. advancedOptions.frame_step.classList[(user_settings.frame_step && "add") || "remove"]("active");
  975. }
  976. }
  977. if (advancedOptions.frame_step && advancedOptions.frame_step.classList.contains("active")) {
  978. document.addEventListener("keydown", toggleFrames, true);
  979. } else if (!advancedOptions.frame_step || !advancedOptions.frame_step.classList.contains("active")) {
  980. document.removeEventListener("keydown", toggleFrames, true);
  981. }
  982. }
  983. function toggleConsole(event) {
  984. if (event.target.id === "console-button") {
  985. document.documentElement.classList.toggle("player-console");
  986. event.target.classList[document.documentElement.classList.contains("player-console") ? "add" : "remove"]("close");
  987. set("advOpts", document.documentElement.classList.contains("player-console"));
  988. }
  989. }
  990. function togglePlay() {
  991. set("VID_PLR_ATPL", !user_settings.VID_PLR_ATPL);
  992. document.documentElement.classList[(user_settings.VID_PLR_ATPL && "add") || "remove"]("part_autoplayon");
  993. document.getElementById("autoplay-button").classList[(user_settings.VID_PLR_ATPL && "add") || "remove"]("active");
  994. }
  995. function toggleLoop(event) {
  996. var videoPlayer = document.querySelector("video");
  997. if (videoPlayer) {
  998. videoPlayer.loop = event ? !user_settings.loopVid : user_settings.loopVid;
  999. if (event) {
  1000. advancedOptions.loop_button.classList[(!user_settings.loopVid && "add") || "remove"]("active");
  1001. }
  1002. }
  1003. set("loopVid", advancedOptions.loop_button.classList.contains("active"));
  1004. }
  1005. function toggleCinemaMode(event) {
  1006. var plrState = api && api.getPlayerState && api.getPlayerState() < 5 && api.getPlayerState() > 0;
  1007. set("lightsOut", event ? !user_settings.lightsOut : true);
  1008. advancedOptions.cinema_mode.classList[(user_settings.lightsOut && "add") || "remove"]("active");
  1009. if (event && plrState) {
  1010. document.documentElement.classList[(user_settings.lightsOut && "add") || "remove"]("part_cinema_mode");
  1011. }
  1012. }
  1013. function handleToggles(event) {
  1014. if (event.target.dataset && event.target.dataset.action) {
  1015. advancedOptions.actions[event.target.dataset.action](event);
  1016. }
  1017. }
  1018. function hookButtons() {
  1019. advancedOptions.popPlayer = popPlayer;
  1020. advancedOptions.full_browser = advancedOptions.controls.querySelector("#fullbrowser-button");
  1021. advancedOptions.cinema_mode = advancedOptions.controls.querySelector("#cinemamode-button");
  1022. advancedOptions.loop_button = advancedOptions.controls.querySelector("#loop-button");
  1023. advancedOptions.frame_step = advancedOptions.controls.querySelector("#framestep-button");
  1024. advancedOptions.actions = {
  1025. togglePlay: togglePlay,
  1026. toggleLoop: toggleLoop,
  1027. getThumb: getThumb,
  1028. getScreenshot: getScreenshot,
  1029. popPlayer: popPlayer,
  1030. toggleFullBrowser: toggleFullBrowser,
  1031. toggleCinemaMode: toggleCinemaMode,
  1032. toggleFrames: toggleFrames
  1033. };
  1034. document.addEventListener("click", handleToggles);
  1035. if (user_settings.loopVid && !advancedOptions.loop_button.classList.contains("active")) {
  1036. advancedOptions.loop_button.classList.add("active");
  1037. toggleLoop();
  1038. }
  1039. if (user_settings.fullBrs && !advancedOptions.full_browser.classList.contains("active")) {
  1040. advancedOptions.full_browser.classList.add("active");
  1041. toggleFullBrowser();
  1042. }
  1043. if (user_settings.lightsOut && !advancedOptions.cinema_mode.classList.contains("active")) {
  1044. advancedOptions.cinema_mode.classList.add("active");
  1045. toggleCinemaMode();
  1046. }
  1047. if (user_settings.frame_step && !advancedOptions.frame_step.classList.contains("active")) {
  1048. advancedOptions.frame_step.classList.add("active");
  1049. toggleFrames();
  1050. }
  1051. }
  1052. function advancedOptions() {
  1053. var header, cnslBtn, cnslCont;
  1054. header = document.getElementById("watch-header");
  1055. cnslBtn = document.getElementById("console-button");
  1056. advancedOptions.controls = document.getElementById("player-console");
  1057. if (window.location.pathname === "/watch" && header && !cnslBtn) {
  1058. cnslBtn = document.createElement("template");
  1059. cnslBtn.innerHTML = "<button id='console-button' class='ytplus_sprite' data-p='ttl|ADV_OPTS'></button>";
  1060. cnslBtn = setLocale(cnslBtn.content).firstChild;
  1061. document.addEventListener("click", toggleConsole);
  1062. cnslCont = document.createElement("template");
  1063. cnslCont.innerHTML = "<div id='advanced-options'></div>";
  1064. cnslCont = cnslCont.content.firstChild;
  1065. cnslCont.appendChild(cnslBtn);
  1066. header.appendChild(cnslCont);
  1067. if (advancedOptions.controls) {
  1068. advancedOptions.controls.remove();
  1069. }
  1070. advancedOptions.controls = document.createElement("template");
  1071. advancedOptions.controls.innerHTML = //
  1072. `<div id='player-console'>
  1073. <div id='autoplay-button' class='yt-uix-tooltip' data-p='ttp|CNSL_AP' data-action='togglePlay'></div>
  1074. <div id='loop-button' class='yt-uix-tooltip' data-p='ttp|CNSL_RPT' data-action='toggleLoop'></div>
  1075. <div id='save-thumbnail-button' class='yt-uix-tooltip' data-p='ttp|CNSL_SVTH' data-action='getThumb'></div>
  1076. <div id='screenshot-button' class='yt-uix-tooltip' data-p='ttp|CNSL_SS' data-action='getScreenshot'></div>
  1077. <div id='popout-button' class='yt-uix-tooltip' data-p='ttp|CNSL_PPOT' data-action='popPlayer'></div>
  1078. <div id='fullbrowser-button' class='yt-uix-tooltip' data-p='ttp|CNSL_FLBR' data-action='toggleFullBrowser'></div>
  1079. <div id='cinemamode-button' class='yt-uix-tooltip' data-p='ttp|CNSL_CINM_MD' data-action='toggleCinemaMode'></div>
  1080. <div id='framestep-button' class='yt-uix-tooltip' data-p='ttp|CNSL_FRME' data-action='toggleFrames'></div>
  1081. </div>`;
  1082. if (user_settings.VID_PLR_ATPL) {
  1083. advancedOptions.controls.content.querySelector("#autoplay-button").classList.add("active");
  1084. }
  1085. advancedOptions.controls = setLocale(advancedOptions.controls.content).firstChild;
  1086. cnslCont.appendChild(advancedOptions.controls);
  1087. hookButtons();
  1088. if (user_settings.advOpts) {
  1089. document.documentElement.classList.add("player-console");
  1090. cnslBtn.classList.add("close");
  1091. }
  1092. }
  1093. }
  1094. function iniAction(event) {
  1095. var observer, load_more, click_title;
  1096. load_more = document.querySelector("#watch-more-related, .load-more-button");
  1097. click_title = document.querySelector(".yt-uix-tile");
  1098. while (click_title) {
  1099. click_title.classList.remove("yt-uix-tile");
  1100. click_title = document.querySelector(".yt-uix-tile");
  1101. }
  1102. if (load_more && !load_more.classList.contains("modThumbs")) {
  1103. load_more.classList.add("modThumbs");
  1104. observer = new MutationObserver(modThumbs);
  1105. observer.observe(load_more, {
  1106. childList: true,
  1107. attributes: true,
  1108. attributeOldValue: true
  1109. });
  1110. }
  1111. if (event && /popoutmode|blacklist/.test(event.target.className)) {
  1112. event.preventDefault();
  1113. event = event.target;
  1114. if (event.classList.contains("popoutmode")) {
  1115. popPlayer(event.dataset.link);
  1116. } else if (event.dataset.ytid !== "undefined") {
  1117. user_settings.blacklist[event.dataset.ytid] = event.dataset.user;
  1118. set("blacklist", user_settings.blacklist);
  1119. modThumbs();
  1120. }
  1121. }
  1122. }
  1123. function setButtons() {
  1124. var i, j, list, temp, thumb, button;
  1125. list = Object.keys(modThumbs.thumbs);
  1126. i = list.length;
  1127. while (i--) {
  1128. temp = modThumbs.thumbs[list[i]];
  1129. j = temp.length;
  1130. while (j--) {
  1131. thumb = temp[j].querySelector(".yt-lockup-thumbnail, .thumb-wrapper");
  1132. if (thumb) {
  1133. if (user_settings.GEN_PPOT_ON && !thumb.querySelector(".popoutmode") && !/channel/.test(temp[j].firstChild.className)) {
  1134. button = document.createElement("template");
  1135. button.innerHTML = "<div data-p='ttl|PPOT_OPEN&ttp|PPOT_OPEN' class='yt-uix-tooltip popoutmode ytplus_sprite'></div>";
  1136. button.content.firstChild.dataset.link = temp[j].querySelector("a[href*='/watch?v']").href;
  1137. thumb.appendChild(setLocale(button.content).firstChild);
  1138. }
  1139. if (user_settings.BLK_ON && !thumb.querySelector(".blacklist") && window.yt.config_.PAGE_NAME !== "channel") {
  1140. button = document.createElement("template");
  1141. button.innerHTML = "<div data-p='ttl|BLCK_ADD&ttp|BLCK_ADD' class='yt-uix-tooltip blacklist ytplus_sprite'></div>";
  1142. button.content.firstChild.dataset.user = temp[j].username;
  1143. button.content.firstChild.dataset.ytid = list[i];
  1144. thumb.appendChild(setLocale(button.content).firstChild);
  1145. }
  1146. }
  1147. }
  1148. }
  1149. }
  1150. function delVideos() {
  1151. var i, j, temp, parent, blacklist, has_upnext;
  1152. has_upnext = document.querySelector(".autoplay-bar");
  1153. blacklist = Object.keys(user_settings.blacklist);
  1154. i = blacklist.length;
  1155. while (i--) {
  1156. temp = modThumbs.thumbs[blacklist[i]];
  1157. if (temp) {
  1158. j = temp.length;
  1159. while (j--) {
  1160. if (has_upnext && has_upnext.contains(temp[j])) {
  1161. has_upnext.parentNode.remove();
  1162. has_upnext = document.querySelector(".watch-sidebar-separation-line");
  1163. if (has_upnext) {
  1164. has_upnext.remove();
  1165. }
  1166. has_upnext = false;
  1167. parent = false;
  1168. } else {
  1169. parent = temp[j].parentNode;
  1170. temp[j].remove();
  1171. }
  1172. temp.splice(j, 1);
  1173. while (parent) {
  1174. if (parent.childElementCount) {
  1175. break;
  1176. }
  1177. parent = parent.parentNode;
  1178. parent.firstChild.remove();
  1179. }
  1180. }
  1181. if (!temp.length) {
  1182. delete modThumbs.thumbs[blacklist[i]];
  1183. }
  1184. temp = false;
  1185. }
  1186. }
  1187. temp = document.getElementsByClassName("feed-item-container");
  1188. i = temp.length;
  1189. while (i--) {
  1190. if (temp[i].querySelectorAll("ul").length < 2) {
  1191. parent = temp[i].parentNode;
  1192. temp[i].remove();
  1193. while (parent) {
  1194. if (parent.childElementCount) {
  1195. break;
  1196. }
  1197. parent = parent.parentNode;
  1198. parent.firstChild.remove();
  1199. }
  1200. }
  1201. }
  1202. }
  1203. function getVideos() {
  1204. var i, list, temp, channel_id;
  1205. modThumbs.thumbs = {};
  1206. list = document.querySelectorAll(`
  1207. .yt-lockup-byline > a,
  1208. .yt-lockup-content .g-hovercard,
  1209. .video-list-item .g-hovercard,
  1210. .channels-content-item .yt-lockup-title > a
  1211. `);
  1212. i = list.length;
  1213. while (i--) {
  1214. temp = list[i];
  1215. channel_id = temp.dataset.ytid;
  1216. while (temp) {
  1217. if (temp.tagName && temp.tagName === "LI") {
  1218. temp.username = list[i].textContent;
  1219. if (!modThumbs.thumbs[channel_id]) {
  1220. modThumbs.thumbs[channel_id] = [temp];
  1221. } else if (modThumbs.thumbs[channel_id].indexOf(temp) < 0) {
  1222. modThumbs.thumbs[channel_id].push(temp);
  1223. }
  1224. break;
  1225. }
  1226. temp = temp.parentNode;
  1227. }
  1228. }
  1229. }
  1230. function modThumbs() {
  1231. if ((user_settings.BLK_ON || user_settings.GEN_PPOT_ON) && !window.opener && window.yt && window.yt.config_ && /watch|index|feed|channel|results/.test(window.yt.config_.PAGE_NAME)) {
  1232. getVideos();
  1233. if (user_settings.BLK_ON && window.yt.config_.PAGE_NAME !== "channel") {
  1234. delVideos();
  1235. }
  1236. setButtons();
  1237. document.addEventListener("click", iniAction);
  1238. iniAction();
  1239. }
  1240. }
  1241. function setVideoCount() {
  1242. var span = document.createElement("span");
  1243. span.textContent = " · ";
  1244. enhancedDetails.username.appendChild(span);
  1245. enhancedDetails.link.href = window.location.origin + "/channel/" + enhancedDetails.user.dataset.ytid + "/videos";
  1246. enhancedDetails.username.appendChild(enhancedDetails.link);
  1247. }
  1248. function updateVideoCount(details) {
  1249. details = details.target.response.querySelector(".pl-header-details li:nth-child(2)");
  1250. if (details) {
  1251. enhancedDetails.link.className = "spf-link";
  1252. enhancedDetails.link.textContent = cid[enhancedDetails.user.dataset.ytid] = details.textContent;
  1253. setVideoCount();
  1254. }
  1255. }
  1256. function getVideoCount() {
  1257. enhancedDetails.username = document.querySelector(".yt-user-info");
  1258. if (!document.getElementById("uploaded-videos") && enhancedDetails.username) {
  1259. enhancedDetails.link = document.createElement("a");
  1260. enhancedDetails.link.id = "uploaded-videos";
  1261. enhancedDetails.username.appendChild(enhancedDetails.link);
  1262. enhancedDetails.user = enhancedDetails.username.querySelector("a");
  1263. if (cid[enhancedDetails.user.dataset.ytid]) {
  1264. enhancedDetails.link.textContent = cid[enhancedDetails.user.dataset.ytid];
  1265. setVideoCount();
  1266. } else {
  1267. localXHR("GET", updateVideoCount, "/playlist?list=" + enhancedDetails.user.dataset.ytid.replace("UC", "UU"), "doc");
  1268. }
  1269. }
  1270. }
  1271. function getChannelInfo(details) {
  1272. var retry, isLive;
  1273. isLive = details.target.response.querySelector(".yt-badge-live");
  1274. if (!isLive) {
  1275. retry = details.target.responseURL.split("/videos").length < 2;
  1276. details = details.target.response.querySelectorAll("[data-context-item-id='" + window.ytplayer.config.args.video_id + "'] .yt-lockup-meta-info li");
  1277. if (details && details.length > 0 && enhancedDetails.watchTime.textContent.split("·").length < 2) {
  1278. enhancedDetails.watchTime.textContent += " · " + details[retry ? 0 : 1].textContent;
  1279. } else if (retry) {
  1280. localXHR("GET", getChannelInfo, "/channel/" + window.ytplayer.config.args.ucid + "/videos", "doc");
  1281. }
  1282. }
  1283. }
  1284. function getPublishedTime() {
  1285. enhancedDetails.watchTime = document.querySelector(".watch-time-text");
  1286. if (enhancedDetails.watchTime && !enhancedDetails.watchTime.fetching && window.ytplayer && window.ytplayer.config) {
  1287. enhancedDetails.watchTime.fetching = true;
  1288. localXHR("GET", getChannelInfo, "/channel/" + window.ytplayer.config.args.ucid + "/search?query=%22" + window.ytplayer.config.args.video_id + "%22", "doc");
  1289. }
  1290. }
  1291. function enhancedDetails() {
  1292. if (window.location.pathname === "/watch") {
  1293. if (user_settings.VID_VID_CNT) {
  1294. getVideoCount();
  1295. }
  1296. if (user_settings.VID_POST_TIME) {
  1297. getPublishedTime();
  1298. }
  1299. }
  1300. }
  1301. function reverseControl() {
  1302. var i, temp, prev, next, list, videos;
  1303. prev = document.querySelector(".prev-playlist-list-item");
  1304. next = document.querySelector(".next-playlist-list-item");
  1305. list = document.getElementById("playlist-autoscroll-list");
  1306. videos = list.getElementsByTagName("li");
  1307. i = videos.length;
  1308. while (i--) {
  1309. list.appendChild(videos[i]);
  1310. }
  1311. temp = prev.href;
  1312. prev.href = next.href;
  1313. next.href = temp;
  1314. list.scrollTop = document.querySelector(".currently-playing").offsetTop;
  1315. if (api) {
  1316. api.updatePlaylist();
  1317. }
  1318. }
  1319. function reverseButton(event) {
  1320. if (event.target.id === "reverse") {
  1321. event.target.classList.toggle("yt-uix-button-toggled");
  1322. set("plRev", (event.target.classList.contains("yt-uix-button-toggled")) ? window.yt.config_.LIST_ID : false);
  1323. reverseControl();
  1324. }
  1325. }
  1326. function autoplayButton(event) {
  1327. if (event.target.id === "autoplay") {
  1328. event.target.classList.toggle("yt-uix-button-toggled");
  1329. set("plApl", event.target.classList.contains("yt-uix-button-toggled"));
  1330. }
  1331. }
  1332. function createButton(type, label, bool, call) {
  1333. var button = document.createElement("template");
  1334. button.innerHTML = //
  1335. `<button class='yt-uix-button yt-uix-button-player-controls yt-uix-button-opacity yt-uix-tooltip' type='button'>
  1336. <span class='yt-uix-button-icon'></span>
  1337. </button>`;
  1338. if (bool === true || window.location.href.split(bool).length > 1) {
  1339. button.content.querySelector("button").classList.add("yt-uix-button-toggled");
  1340. }
  1341. button.content.firstChild.id = type;
  1342. button.content.firstChild.dataset.p = "ttp|" + label + "&ttl|" + label;
  1343. button.content.firstChild.classList.add("yt-uix-button-icon-watch-appbar-" + type + "-video-list", "ytplus_sprite");
  1344. button = setLocale(button.content).firstChild;
  1345. playlistControls.plBar.className = playlistControls.plBar.className.replace("radio-playlist", "");
  1346. document.addEventListener("click", call);
  1347. document.querySelector(".playlist-nav-controls").appendChild(button);
  1348. }
  1349. function playlistControls() {
  1350. playlistControls.plBar = document.getElementById("watch-appbar-playlist");
  1351. if (playlistControls.plBar) {
  1352. if (document.readyState === "complete" && user_settings.plRev && window.location.href.split(user_settings.plRev).length > 1) {
  1353. reverseControl();
  1354. }
  1355. if (user_settings.VID_PLST_RVRS && !document.getElementById("reverse")) {
  1356. createButton("reverse", "PLST_RVRS", user_settings.plRev, reverseButton);
  1357. }
  1358. if (user_settings.VID_PLST_ATPL && !document.getElementById("autoplay")) {
  1359. createButton("autoplay", "PLST_AP", user_settings.plApl, autoplayButton);
  1360. }
  1361. }
  1362. }
  1363. function xhrPatch(event) {
  1364. var temp, player;
  1365. if (this.readyState === 4) {
  1366. temp = {args: JSON.parse(
  1367. "{\"" +
  1368. decodeURIComponent(this.responseText
  1369. .replace(/%5C/g, "%5C%5C")
  1370. .replace(/%22/g, "%5C%22")
  1371. .replace(/&/g, "\",\"")
  1372. .replace(/\=/g, "\":\"")
  1373. .replace(/\+/g, "%20")
  1374. ) +
  1375. "\"}"
  1376. )};
  1377. temp = modArgs(temp);
  1378. temp = encodeURIComponent(JSON.stringify(temp.args).split(/\{"([\w\W]*?)"\}/)[1])
  1379. .replace(/%5C%5C/g, "%5C")
  1380. .replace(/%5C%22/g, "%22")
  1381. .replace(/%22%2C%22/g, "&")
  1382. .replace(/%22%3A%22/g, "=")
  1383. .replace(/%20/g, "+");
  1384. Object.defineProperty(this, "responseText", {writable: true});
  1385. this.responseText = temp;
  1386. player = document.getElementById("movie_player");
  1387. if (player) {
  1388. player.setPlaybackQuality(user_settings.VID_DFLT_QLTY);
  1389. }
  1390. }
  1391. }
  1392. function checkXHR(original) {
  1393. return function(method, url) {
  1394. if (url.match("get_video_info")) {
  1395. this.addEventListener("readystatechange", xhrPatch);
  1396. }
  1397. return original.apply(this, arguments);
  1398. };
  1399. }
  1400. function hideVolume() {
  1401. if (volumeWheel.cBottom && volumeWheel.cBottom.classList.contains("ytp-volume-slider-active")) {
  1402. volumeWheel.cBottom.classList.remove("ytp-volume-slider-active");
  1403. delete volumeWheel.cBottom.timer;
  1404. }
  1405. }
  1406. function volumeWheel(event) {
  1407. var fsPl, pSets, ivCard, player, canScroll, direction;
  1408. player = document.querySelector("video");
  1409. fsPl = document.querySelector(".ytp-playlist-menu");
  1410. pSets = document.querySelector(".ytp-settings-menu");
  1411. ivCard = document.querySelector(".iv-drawer");
  1412. canScroll = event && (!fsPl || (fsPl && !fsPl.contains(event.target))) && (!ivCard || (ivCard && !ivCard.contains(event.target))) && (!pSets || (pSets && !pSets.contains(event.target)));
  1413. if (event && api && player && canScroll && (event.target.id === api || api.contains(event.target))) {
  1414. event.preventDefault();
  1415. volumeWheel.cBottom = document.querySelector(".ytp-chrome-bottom");
  1416. if (volumeWheel.cBottom) {
  1417. if (!volumeWheel.cBottom.classList.contains("ytp-volume-slider-active")) {
  1418. volumeWheel.cBottom.classList.add("ytp-volume-slider-active");
  1419. }
  1420. if (volumeWheel.cBottom.timer) {
  1421. window.clearTimeout(volumeWheel.cBottom.timer);
  1422. }
  1423. if (api) {
  1424. api.dispatchEvent(new Event("mousemove"));
  1425. }
  1426. volumeWheel.cBottom.timer = window.setTimeout(hideVolume, 4000);
  1427. }
  1428. direction = event && (event.deltaY || event.wheelDeltaY);
  1429. api.setVolume(player.volume * 100 - (Math.sign(direction) * 5));
  1430. } else if (!event && user_settings.VID_VOL_WHEEL) {
  1431. document.addEventListener("wheel", volumeWheel);
  1432. } else if (window.location.pathname !== "/watch") {
  1433. document.removeEventListener("wheel", volumeWheel);
  1434. }
  1435. }
  1436. function dragPopOut(event) {
  1437. var excluded, isFScreen;
  1438. excluded = document.querySelector(".ytp-chrome-bottom");
  1439. isFScreen = document.querySelector(".ytp-fullscreen");
  1440. if (event && !isFScreen && (!excluded || (event.target !== excluded && !excluded.contains(event.target)))) {
  1441. if (event.buttons === 1) {
  1442. if (event.type === "mousedown") {
  1443. event.preventDefault();
  1444. event.stopPropagation();
  1445. document.addEventListener("mousemove", dragPopOut);
  1446. document.addEventListener("click", dragPopOut, true);
  1447. window.oldPos = {
  1448. X: event.clientX,
  1449. Y: event.clientY,
  1450. orgX: event.clientX,
  1451. orgY: event.clientY
  1452. };
  1453. } else if (event.type === "mousemove" && (window.hasMoved || Math.abs(event.clientX - window.oldPos.orgX) > 10 || Math.abs(event.clientY - window.oldPos.orgY) > 10)) {
  1454. window.moveBy(event.clientX - window.oldPos.X, event.clientY - window.oldPos.Y);
  1455. window.hasMoved = true;
  1456. }
  1457. }
  1458. if (event.buttons !== 1 || event.type === "click") {
  1459. if (window.hasMoved) {
  1460. event.preventDefault();
  1461. event.stopImmediatePropagation();
  1462. delete window.oldPos;
  1463. delete window.hasMoved;
  1464. }
  1465. document.removeEventListener("mousemove", dragPopOut);
  1466. document.removeEventListener("click", dragPopOut, true);
  1467. }
  1468. } else if (!event && window.name === "popOut") {
  1469. document.addEventListener("mousedown", dragPopOut);
  1470. }
  1471. }
  1472. function popPlayer(url) {
  1473. var popOut, width, height, pop_url, video;
  1474. width = parseInt(user_settings.VID_PPOT_SZ) || 533;
  1475. height = Math.round(width / (16 / 9));
  1476. video = document.querySelector("video");
  1477. pop_url = (!url.target && url) || window.location.href.split(/&t=[0-9]+|#t=[0-9]+|&time=[0-9]+/).join("");
  1478. if (url.target && video && video.currentTime && video.currentTime < video.duration) {
  1479. pop_url += "#t=" + video.currentTime;
  1480. window.ytplayer.config.args.start = video.currentTime;
  1481. api.cueVideoByPlayerVars(window.ytplayer.config.args);
  1482. }
  1483. popOut = window.open(pop_url, "popOut", "width=" + width + ",height=" + height);
  1484. popOut.focus();
  1485. }
  1486. function setSubPlaylist(event) {
  1487. var i, list, button;
  1488. list = [];
  1489. if (event.target && event.target.parentNode && event.target.parentNode.id === "subscription-playlist") {
  1490. i = subPlaylist.video_list.length;
  1491. while (i--) {
  1492. if (i > -1) {
  1493. list.push(subPlaylist.video_list[i].dataset.videoIds);
  1494. }
  1495. }
  1496. list.reverse().join("%2C");
  1497. subPlaylist.list_title = subPlaylist.list_title && subPlaylist.list_title.querySelector(".epic-nav-item-heading").textContent.trim();
  1498. button = document.getElementById("subscription-playlist");
  1499. button.href = "/watch_videos?title=" + subPlaylist.list_title + "&video_ids=" + list;
  1500. }
  1501. }
  1502. function subPlaylist() {
  1503. var button, nav_menu;
  1504. nav_menu = document.querySelector(".appbar-nav-menu");
  1505. button = document.getElementById("subscription-playlist");
  1506. subPlaylist.list_title = document.querySelector(".appbar-nav-menu");
  1507. subPlaylist.video_list = document.getElementsByClassName("addto-watch-later-button");
  1508. if (user_settings.GEN_SUB_LIST && nav_menu && window.location.href.split("/feed/subscriptions").length > 1 && !button && subPlaylist.list_title && subPlaylist.video_list) {
  1509. button = document.createElement("template");
  1510. button.innerHTML = //
  1511. `<li id='subscription-playlist-icon'>
  1512. <a id='subscription-playlist' data-p='ttl|SUB_PLST' class='yt-uix-button spf-link yt-uix-sessionlink yt-uix-button-epic-nav-item yt-uix-button-size-default'>
  1513. <span class='yt-uix-button-content ytplus_sprite'></span>
  1514. </a>
  1515. </li>`;
  1516. button = setLocale(button.content).firstChild;
  1517. nav_menu.appendChild(button);
  1518. document.addEventListener("click", setSubPlaylist);
  1519. }
  1520. }
  1521. function loadComments(event) {
  1522. if (event.target && event.target.parentNode && event.target.parentNode.id === "P-show-comments") {
  1523. if (modComments.comments.lazyload) {
  1524. window.spf.load.apply(main, modComments.comments.lazyload);
  1525. }
  1526. modComments.comments.classList.toggle("show");
  1527. modComments.wrapper.querySelector("button").textContent = lang((modComments.comments.classList.contains("show") && "HIDE_CMTS") || "SHOW_CMTS");
  1528. }
  1529. }
  1530. function modComments() {
  1531. var is_live = window.ytplayer && window.ytplayer.config && window.ytplayer.config.args && window.ytplayer.config.args.livestream;
  1532. modComments.comments = document.getElementById("watch-discussion");
  1533. if (!is_live && modComments.comments && !document.getElementById("P-show-comments") && user_settings.VID_HIDE_COMS === "1") {
  1534. modComments.wrapper = document.createElement("template");
  1535. modComments.wrapper.innerHTML = //
  1536. `<div id='P-show-comments' class='yt-card'>
  1537. <button class='yt-uix-button yt-uix-button-expander' data-p='tnd|SHOW_CMTS'></button>
  1538. </div>`;
  1539. modComments.wrapper = setLocale(modComments.wrapper.content).firstChild;
  1540. document.addEventListener("click", loadComments);
  1541. modComments.comments.parentNode.insertBefore(modComments.wrapper, modComments.comments);
  1542. }
  1543. }
  1544. function setCustomStyles(clss) {
  1545. document.documentElement.classList[user_settings[clss] ? "add" : "remove"](customStyles.custom_styles[clss]);
  1546. }
  1547. function customStyles() {
  1548. var plr_api, comments, sidebar, ytGrid, adverts, ads_list;
  1549. comments = document.getElementById("watch-discussion");
  1550. ytGrid = document.querySelector(".yt-uix-menu-top-level-flow-button:last-child a");
  1551. customStyles.custom_styles = {
  1552. GEN_DSBL_ADS : "part_no_ads",
  1553. GEN_BLUE_GLOW : "part_dsbl_glow",
  1554. GEN_HIDE_FTR : "part_hide_footer",
  1555. GEN_BTTR_NTF : "part_notif_button",
  1556. GEN_GRID_SUBS : "part_grid_subs",
  1557. GEN_GRID_SRCH : "part_grid_search",
  1558. GEN_CMPT_TTLS : "part_compact_titles",
  1559. VID_PLR_ATPL : "part_autoplayon",
  1560. VID_PLR_FIT : "part_fit_theater",
  1561. VID_PLR_DYN_SIZE: "part_static_size",
  1562. VID_HIDE_DETLS : "part_hide_details",
  1563. VID_TTL_CMPT : "part_compact_title",
  1564. VID_DESC_SHRT : "part_labelless_buttons"
  1565. };
  1566. if (window.yt && window.yt.config_ && window.yt.config_.PAGE_NAME === "shared_conversation") {
  1567. window.stop();
  1568. window.location = document.querySelector("[rel='shortlink']").href;
  1569. return;
  1570. }
  1571. if (window.name === "popOut") {
  1572. document.documentElement.classList.add("part_popout");
  1573. }
  1574. if (ytGrid && user_settings.GEN_GRID_SUBS) {
  1575. ytGrid.click();
  1576. } else {
  1577. plr_api = document.getElementById("player-api");
  1578. sidebar = document.querySelector(".branded-page-v2-secondary-col");
  1579. ads_list = //
  1580. `#masthead_child,
  1581. #feed-pyv-container,
  1582. #watch7-sidebar-ads,
  1583. #watch7-sidebar-offer,
  1584. .ad-div,
  1585. .pyv-afc-ads-container,
  1586. .video-list-item:not(.related-list-item):not(.dashboard-widget-item)`;
  1587. adverts = user_settings.GEN_DSBL_ADS && document.querySelector(ads_list);
  1588. while (adverts) {
  1589. adverts.remove();
  1590. adverts = document.querySelector(ads_list);
  1591. }
  1592. if ((window.location.pathname === "/results" && sidebar && sidebar.querySelectorAll("*").length < 10) || (sidebar && ((user_settings.GEN_HDE_RECM_SDBR && window.location.href.split("/feed/subscriptions").length > 1) || (user_settings.GEN_HDE_SRCH_SDBR && window.location.pathname === "/results") || (user_settings.GEN_HDE_CHN_SDBR && window.location.href.split(/\/(channel|user|c)\//).length > 1)))) {
  1593. sidebar.remove();
  1594. }
  1595. if (window.location.pathname === "/watch" && user_settings.VID_HIDE_COMS > 1 && comments) {
  1596. comments.remove();
  1597. }
  1598. if (user_settings.VID_HIDE_COMS === "1") {
  1599. document.documentElement.classList.add("part_hide_comments");
  1600. } else if (user_settings.VID_HIDE_COMS !== "1") {
  1601. document.documentElement.classList.remove("part_hide_comments");
  1602. }
  1603. if (user_settings.VID_PLR_FIT && plr_api && (!!plr_api.style.maxWidth || plr_api.style.maxWidth !== user_settings.VID_PLR_FIT_WDTH)) {
  1604. plr_api.style.maxWidth = user_settings.VID_PLR_FIT_WDTH || "1280px";
  1605. }
  1606. Object.keys(customStyles.custom_styles).forEach(setCustomStyles);
  1607. if (window.location.href.split("/feed/subscriptions").length < 2) {
  1608. document.documentElement.classList.remove("part_grid_subs");
  1609. }
  1610. }
  1611. }
  1612. function defaultChannelPage(event) {
  1613. var parentNode;
  1614. if (user_settings.GEN_CHN_DFLT_PAGE !== "default") {
  1615. if (event && event.target) {
  1616. parentNode = event.target;
  1617. if (event.target.tagName !== "A") {
  1618. while (parentNode) {
  1619. parentNode = parentNode.parentNode;
  1620. if (parentNode && parentNode.tagName === "A") {
  1621. break;
  1622. }
  1623. }
  1624. }
  1625. if (parentNode && parentNode.href && parentNode.href.split(user_settings.GEN_CHN_DFLT_PAGE).length < 2 && (parentNode.href.split("/channel/").length > 1 || parentNode.href.split("/user/").length > 1) && parentNode.href.split(/[a-z0-9]\/[a-z0-9]/i).length < 4) {
  1626. parentNode.href += "/" + user_settings.GEN_CHN_DFLT_PAGE;
  1627. }
  1628. } else if (!event) {
  1629. document.addEventListener("mouseup", defaultChannelPage);
  1630. }
  1631. }
  1632. }
  1633. function modArgs(config) {
  1634. var i, temp, list, length, videos, new_list, can_share;
  1635. if (config.args.video_id) {
  1636. if (window.name === "popOut") {
  1637. can_share = document.querySelector(".playlist-header-content");
  1638. if (can_share && can_share.dataset.shareable === "False" && !config.args.video) {
  1639. config.args.video = [];
  1640. videos = document.querySelectorAll("li[data-video-id]");
  1641. length = videos.length;
  1642. for (i = 0; i < length; i++) {
  1643. config.args.video[i] = {"encrypted_id": videos[i].getAttribute("data-video-id")};
  1644. }
  1645. }
  1646. document.title = config.args.title;
  1647. config.args.el = "embedded";
  1648. }
  1649. config.args.dash = (user_settings.VID_PLR_DASH && "0") || config.args.dash;
  1650. config.args.vq = user_settings.VID_DFLT_QLTY;
  1651. if (user_settings.VID_DFLT_QLTY !== "auto") {
  1652. try {
  1653. window.localStorage["yt-player-quality"] = JSON.stringify({
  1654. "data": user_settings.VID_DFLT_QLTY,
  1655. "expiration": new Date().getTime() + 864E5,
  1656. "creation": new Date().getTime()
  1657. });
  1658. } catch (ignore) {}
  1659. }
  1660. if (config.args.caption_audio_tracks && user_settings.VID_PLR_CC) {
  1661. config.args.caption_audio_tracks = config.args.caption_audio_tracks.split(/&d=[0-9]|d=[0-9]&/).join("");
  1662. }
  1663. if (user_settings.VID_PLR_VOL_LDN) {
  1664. delete config.args.loudness;
  1665. }
  1666. if (user_settings.VID_PLR_HTML5) {
  1667. config.html5 = true;
  1668. }
  1669. if (user_settings.VID_PLR_INFO) {
  1670. config.args.showinfo = "1";
  1671. }
  1672. if (!user_settings.VID_PLR_ATPL) {
  1673. config.args.autoplay = "0";
  1674. }
  1675. if (user_settings.VID_PLR_SIZE_MEM) {
  1676. config.args.player_wide = (user_settings.theaterMode && "1") || "0";
  1677. if (window.ytpsetwide) {
  1678. window.ytpsetwide("wide", config.args.player_wide, -1);
  1679. }
  1680. }
  1681. if (config.args.iv_load_policy && user_settings.VID_PLR_ANTS) {
  1682. config.args.iv_load_policy = "3";
  1683. }
  1684. if (user_settings.VID_PLR_ADS && (!user_settings.VID_SUB_ADS || (user_settings.VID_SUB_ADS && !config.args.subscribed))) {
  1685. delete config.args.ad3_module;
  1686. }
  1687. if (config.args.vmap && !user_settings.VID_PLR_ATPL && !user_settings.VID_PLR_ADS) {
  1688. config.args.dvmap = config.args.vmap;
  1689. delete config.args.vmap;
  1690. }
  1691. if (config.args.adaptive_fmts && user_settings.VID_PLR_HFR) {
  1692. new_list = [];
  1693. list = config.args.adaptive_fmts.split(",");
  1694. i = list.length;
  1695. while (i--) {
  1696. temp = list[i].split(/fps\=([0-9]{2})/)[1];
  1697. if (!temp || temp < 31) {
  1698. new_list.push(list[i]);
  1699. }
  1700. }
  1701. config.args.adaptive_fmts = new_list.join(",");
  1702. }
  1703. if (window.ytplayer) {
  1704. if (window.ytplayer.config === null) {
  1705. window.ytplayer.config = config;
  1706. } else if (window.ytplayer.config) {
  1707. window.ytplayer.config.args = config.args;
  1708. }
  1709. }
  1710. }
  1711. return config;
  1712. }
  1713. function generalChanges() {
  1714. var logo, checkbox, autoplaybar, description;
  1715. autoplaybar = document.querySelector(".autoplay-bar");
  1716. description = document.getElementById("action-panel-details");
  1717. if (user_settings.GEN_YT_LOGO_LINK && window.yt && window.yt.config_ && window.yt.config_.LOGGED_IN) {
  1718. logo = document.querySelector("map[name='doodle'] > area, #logo-container");
  1719. if (logo && logo.href === window.location.origin + "/") {
  1720. logo.href = "/feed/subscriptions";
  1721. }
  1722. }
  1723. if (user_settings.GEN_REM_APUN && window.location.pathname === "/watch" && autoplaybar) {
  1724. checkbox = document.querySelector(".checkbox-on-off");
  1725. if (checkbox) {
  1726. checkbox.remove();
  1727. }
  1728. }
  1729. if (user_settings.VID_LAYT_AUTO_PNL && window.location.pathname === "/watch" && description) {
  1730. description.classList.remove("yt-uix-expander-collapsed");
  1731. }
  1732. if (user_settings.GEN_SPF_OFF && window.spf && window.spf.dispose) {
  1733. window.spf.dispose();
  1734. }
  1735. }
  1736. function localXHR(method, call, url, head) {
  1737. var request = new XMLHttpRequest();
  1738. request.addEventListener("load", call);
  1739. request.open(method, url, true);
  1740. if (head && head !== "doc") {
  1741. request.setRequestHeader(head[0], head[1]);
  1742. } else {
  1743. request.responseType = "document";
  1744. }
  1745. request.send();
  1746. }
  1747. function playerMode() {
  1748. var pageElement, playerElement;
  1749. if (user_settings.VID_PLR_SIZE_MEM) {
  1750. pageElement = document.getElementById("page");
  1751. playerElement = document.getElementById("player");
  1752. if (window.ytpsetwide) {
  1753. window.ytpsetwide("wide", (user_settings.theaterMode ? "1" : "0"), -1);
  1754. }
  1755. if (playerElement && window.location.pathname === "/watch") {
  1756. pageElement.classList[user_settings.theaterMode ? "add" : "remove"]("watch-wide");
  1757. pageElement.className = pageElement.className.replace(user_settings.theaterMode ? "non-" : "watch-stage", user_settings.theaterMode ? "" : "watch-non-stage");
  1758. playerElement.className = user_settings.theaterMode ? playerElement.className.replace("small", "large") : playerElement.className.replace("large", "small").replace("medium", "small");
  1759. }
  1760. }
  1761. }
  1762. function infiniteScroll() {
  1763. var observer, loadMore;
  1764. loadMore = document.querySelector(".load-more-button");
  1765. if (loadMore && user_settings.GEN_INF_SCRL) {
  1766. if (!loadMore.classList.contains("infiniteScroll")) {
  1767. loadMore.classList.add("infiniteScroll");
  1768. observer = new MutationObserver(infiniteScroll);
  1769. observer.observe(loadMore, {attributes: true});
  1770. }
  1771. if (!loadMore.classList.contains("scrolldetect")) {
  1772. loadMore.classList.add("scrolldetect");
  1773. loadMore.dataset.scrolldetectCallback = "load-more-auto";
  1774. }
  1775. }
  1776. }
  1777. function checkNewFeatures() {
  1778. var i, keys;
  1779. keys = Object.keys(default_settings);
  1780. i = keys.length;
  1781. while (i) {
  1782. i -= 1;
  1783. if (!user_settings.hasOwnProperty(keys[i])) {
  1784. set(keys[i], default_settings[keys[i]]);
  1785. }
  1786. }
  1787. }
  1788. function shareApi(original) {
  1789. return function () {
  1790. playerReady();
  1791. if (original) {
  1792. return original.apply(this, arguments);
  1793. }
  1794. };
  1795. }
  1796. function request(event) {
  1797. var video_player = document.getElementById("movie_player");
  1798. document.documentElement.classList.remove("floater");
  1799. if (video_player) {
  1800. video_player.removeAttribute("style");
  1801. if (!user_settings.VID_PLR_ATPL || event.detail.url.split("/watch").length < 2) {
  1802. if (window.ytplayer && window.ytplayer.config && window.ytplayer.config.loaded) {
  1803. delete window.ytplayer.config.loaded;
  1804. }
  1805. api.destroy();
  1806. }
  1807. }
  1808. }
  1809. function pageScriptMessages() {
  1810. var key, gate, sets, observer;
  1811. key = "parreceive";
  1812. gate = document.documentElement;
  1813. sets = JSON.parse(gate.dataset[key] || null);
  1814. if (!gate.pagescript) {
  1815. gate.pagescript = true;
  1816. observer = new MutationObserver(pageScriptMessages);
  1817. return observer.observe(gate, {
  1818. attributes:true,
  1819. attributeFilter: ["data-" + key]
  1820. });
  1821. }
  1822. if (sets) {
  1823. user_settings = sets;
  1824. gate.dataset[key] = null;
  1825. customStyles();
  1826. document.documentElement.removeAttribute("data-parreceive");
  1827. }
  1828. }
  1829. function set(setting, new_value) {
  1830. if (setting !== "user_settings") {
  1831. user_settings[setting] = new_value;
  1832. } else {
  1833. user_settings = new_value;
  1834. }
  1835. document.documentElement.dataset.parsend = JSON.stringify(user_settings);
  1836. }
  1837. function main() {
  1838. pageScriptMessages();
  1839. customStyles();
  1840. settingsMenu();
  1841. infiniteScroll();
  1842. playlistControls();
  1843. playerMode();
  1844. advancedOptions();
  1845. volumeWheel();
  1846. subPlaylist();
  1847. alwaysVisible();
  1848. modThumbs();
  1849. enhancedDetails();
  1850. modComments();
  1851. defaultChannelPage();
  1852. generalChanges();
  1853. dragPopOut();
  1854. }
  1855. function isMaterial() {
  1856. var i, temp;
  1857. temp = document.querySelectorAll("link");
  1858. i = temp.length;
  1859. while (i--) {
  1860. if (temp[i].href.match("olymer")) {
  1861. temp = document.createElement("template");
  1862. temp.innerHTML = //
  1863. `<div style='border-radius:2px;color:#FFF;padding:10px;background-color:#09F;box-shadow:0 0 3px rgba(0,0,0,.5);font-size:12px;position:fixed;bottom:20px;right:20px;z-index:99999'>
  1864. YouTube Plus is not yet compatible with the YouTube beta Material Layout<br>
  1865. <a href='https://github.com/ParticleCore/Particle/wiki/Restore-classic-YouTube' target='_blank' style='color:#FFF;font-weight:bold;'>Click here</a> for instructions to restore classic YouTube and continue using YT+<br>
  1866. To keep using the current layout without this message please disable YT+
  1867. </div>`;
  1868. document.documentElement.appendChild(temp.content.firstChild);
  1869. return true;
  1870. }
  1871. }
  1872. }
  1873. function closeMigrationInstructions(event) {
  1874. if (event && event.target && event.target.id === "close_migration_instructions") {
  1875. document.removeEventListener("click", closeMigrationInstructions);
  1876. event.target.parentNode.remove();
  1877. set("migration_instructions", true);
  1878. }
  1879. }
  1880. function migrationInstructions() {
  1881. var temp = document.createElement("template");
  1882. temp.innerHTML = //
  1883. `<div style='border-radius: 2px; color: #FFF; font: 12px Roboto,arial,sans-serif; padding: 10px; background-color: rgb(0, 153, 255); box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5); position: fixed; z-index: 99999; top: 50%; left: 50%; transform: translate(-50%, -50%);'>
  1884. YouTube Plus has been updated and is now a Webextension. Unfortunately this means that your previous settings need to be imported manually.<br>
  1885. <a href='https://github.com/ParticleCore/Particle/wiki/Restore-settings' target='_blank' style='color:#FFF;font-weight:bold;'>Click here</a> if you wish to import the previous settings or know more about this change.<br>
  1886. <button id='close_migration_instructions' style='background-color: rgba(255, 255, 255, 0.3); color: #FFF; cursor: pointer; margin-top: 10px; border-radius: 2px; padding: 4px; float: right;'>CLOSE</button>
  1887. </div>`;
  1888. document.documentElement.appendChild(temp.content.firstChild);
  1889. document.addEventListener("click", closeMigrationInstructions);
  1890. }
  1891. var api, cid, events, language, user_settings, player_instance, default_settings;
  1892. if (isMaterial()) {
  1893. return;
  1894. }
  1895. cid = {};
  1896. events = {};
  1897. user_settings = JSON.parse(document.documentElement.dataset.user_settings || null);
  1898. if (document.documentElement.dataset.user_settings) {
  1899. document.documentElement.removeAttribute("data-user_settings");
  1900. }
  1901. default_settings = {
  1902. GEN_BTTR_NTF : true,
  1903. GEN_SUB_LIST : true,
  1904. GEN_INF_SCRL : true,
  1905. GEN_BLUE_GLOW : true,
  1906. GEN_PPOT_ON : true,
  1907. VID_END_SHRE : true,
  1908. VID_DFLT_QLTY : "auto",
  1909. VID_PLST_ATPL : true,
  1910. VID_PLST_RVRS : true,
  1911. VID_PLR_ATPL : true,
  1912. VID_PLR_ALVIS : true,
  1913. VID_PLR_SIZE_MEM: true,
  1914. VID_PLR_FIT : true,
  1915. VID_PLR_VOL_LDN : true,
  1916. VID_POST_TIME : true,
  1917. VID_VID_CNT : true,
  1918. VID_DESC_SHRT : true,
  1919. VID_PPOT_SZ : 533,
  1920. VID_PLR_HTML5 : true,
  1921. BLK_ON : true,
  1922. floaterX : 2000,
  1923. floaterY : 2000,
  1924. firstTime : true,
  1925. volLev : 50,
  1926. advOpts : true,
  1927. blacklist : {}
  1928. };
  1929. language = {
  1930. YTSETS : "YouTube+ settings",
  1931. ADV_OPTS : "Advanced options",
  1932. SUB_PLST : "Play recent uploads",
  1933. PPOT_OPEN : "Open in pop-out",
  1934. BLCK_ADD : "Add to blacklist",
  1935. BLCK_EDIT : "Edit",
  1936. BLCK_SAVE : "Save",
  1937. BLCK_CLSE : "Close",
  1938. CNSL_AP : "Autoplay",
  1939. CNSL_RPT : "Repeat video",
  1940. CNSL_SVTH : "Open thumbnail",
  1941. CNSL_SS : "Take screenshot",
  1942. CNSL_SS_CLS : "CLOSE",
  1943. CNSL_PPOT : "Pop-out video",
  1944. CNSL_FLBR : "Fullbrowser mode",
  1945. CNSL_CINM_MD : "Cinema mode",
  1946. CNSL_FRME : "Frame by frame (Shift + ← or →)",
  1947. PLST_AP : "Autoplay",
  1948. PLST_RVRS : "Reverse",
  1949. SHOW_CMTS : "Show comments",
  1950. HIDE_CMTS : "Hide comments",
  1951. GLB_IMPR : "Import/export settings",
  1952. GLB_LOCL_LANG : "Click to edit YT+ language",
  1953. GLB_LOCL_LANG_CSTM : "Local",
  1954. GLB_IMPR_SAVE : "Save and load",
  1955. GLB_RSET : "Reset",
  1956. GLB_SVE : "Save",
  1957. GLB_SVE_SETS : "Settings saved",
  1958. FTR_DESC : "Find out what this does",
  1959. GEN : "General",
  1960. VID : "Video",
  1961. CHN : "Channels",
  1962. BLK : "Blacklist",
  1963. ABT : "About",
  1964. HLP : "Help",
  1965. DNT : "Donate",
  1966. GEN_TTL : "General settings",
  1967. GEN_GEN : "General",
  1968. GEN_LYT : "Layout",
  1969. GEN_LOCL_LANG : "Use modified YT+ language",
  1970. GEN_PPOT_ON : "Enable pop-out mode",
  1971. GEN_DSBL_ADS : "Disable advertisements outside the video page",
  1972. GEN_INF_SCRL : "Enable infinite scroll in feeds",
  1973. GEN_YT_LOGO_LINK : "YouTube logo redirects to subscriptions",
  1974. GEN_SUB_LIST : "Enable subscription playlist",
  1975. GEN_REM_APUN : "Remove autoplay up next",
  1976. GEN_SPF_OFF : "Disable SPF",
  1977. GEN_HIDE_FTR : "Hide footer",
  1978. GEN_BLUE_GLOW : "Remove blue glow around clicked buttons",
  1979. GEN_HDE_RECM_SDBR : "Hide recommended channels sidebar",
  1980. GEN_HDE_SRCH_SDBR : "Hide search results sidebar",
  1981. GEN_HDE_CHN_SDBR : "Hide channel sidebar",
  1982. GEN_CMPT_TTLS : "Compact titles in feeds",
  1983. GEN_DSB_HVRC : "Disable hovercards",
  1984. GEN_BTTR_NTF : "Improved blue notification box",
  1985. GEN_GRID_SUBS : "Grid layout in subscriptions",
  1986. GEN_GRID_SRCH : "Grid layout in search results",
  1987. VID_TTL : "Video settings",
  1988. VID_PLR : "Player settings",
  1989. VID_PLR_LYT : "Player layout",
  1990. VID_DFLT_QLTY : "Default video quality:",
  1991. VID_DFLT_QLTY_AUTO : "Auto",
  1992. VID_DFLT_QLTY_TNY : "144p",
  1993. VID_DFLT_QLTY_SML : "240p",
  1994. VID_DFLT_QLTY_MDM : "360p",
  1995. VID_DFLT_QLTY_LRG : "480p",
  1996. VID_DFLT_QLTY_720 : "720p",
  1997. VID_DFLT_QLTY_1080 : "1080p",
  1998. VID_DFLT_QLTY_1440 : "1440p",
  1999. VID_DFLT_QLTY_2160 : "2160p (4k)",
  2000. VID_DFLT_QLTY_2880 : "2880p (5k)",
  2001. VID_DFLT_QLTY_ORIG : "4320p (8k)",
  2002. VID_PLR_ALVIS : "Player always visible when reading comments",
  2003. VID_PLR_ALVIS_WDTH : "Floating player width",
  2004. VID_PLR_ALVIS_SCRL_TOP: "Go to top",
  2005. VID_PLR_ATPL : "Autoplay videos",
  2006. VID_LAYT : "Layout",
  2007. VID_VID_CNT : "Show link with number of uploaded videos",
  2008. VID_POST_TIME : "Show how long the video has been published",
  2009. VID_HIDE_DETLS : "Hide video details",
  2010. VID_HIDE_COMS : "Comment section",
  2011. VID_HIDE_COMS_SHOW : "Show",
  2012. VID_HIDE_COMS_HIDE : "Hide",
  2013. VID_HIDE_COMS_REM : "Remove",
  2014. VID_END_SHRE : "Disable share panel when video ends",
  2015. VID_PLST : "Playlists",
  2016. VID_PLST_ATPL : "Enable playlist autoplay control",
  2017. VID_PLST_RVRS : "Enable reverse playlist control",
  2018. VID_PLR_SIZE_MEM : "Memorize player mode",
  2019. VID_VOL_WHEEL : "Change volume with mouse wheel",
  2020. VID_PLR_VOL_MEM : "Memorize audio volume",
  2021. VID_PLR_VOL_LDN : "Disable YouTube loudness normalisation",
  2022. VID_PLR_ADS : "Disable advertisements in the video page",
  2023. VID_PLR_ALACT : "Player shortcuts always active",
  2024. VID_SUB_ADS : "Enable advertisements only in videos from subscribed channels",
  2025. VID_PLR_ANTS : "Disable annotations",
  2026. VID_PLR_DASH : "Disable DASH playback",
  2027. VID_PLR_HFR : "Disable HFR (60fps)",
  2028. VID_PLR_HTML5 : "Use the HTML5 player when possible",
  2029. VID_PLR_CC : "Disable subtitles and CC",
  2030. VID_PLR_INFO : "Enable info bar with watch later button",
  2031. VID_PLR_FIT : "Fit to page in theater mode",
  2032. VID_PLR_FIT_WDTH : "Fit to page max width:",
  2033. VID_PLR_DYN_SIZE : "Disable dynamic player size in default view",
  2034. VID_DESC_SHRT : "Short video description buttons",
  2035. VID_TTL_CMPT : "Compact title in video description",
  2036. VID_PPOT_SZ : "Pop-out player size",
  2037. VID_LAYT_AUTO_PNL : "Auto expand video description",
  2038. GEN_CHN_DFLT_PAGE : "Default channel page:",
  2039. GEN_CHN_DFLT_PAGE_DFLT: "Default",
  2040. GEN_CHN_DFLT_PAGE_VID : "Videos",
  2041. GEN_CHN_DFLT_PAGE_PL : "Playlists",
  2042. GEN_CHN_DFLT_PAGE_CHN : "Channels",
  2043. GEN_CHN_DFLT_PAGE_DISC: "Discussion",
  2044. GEN_CHN_DFLT_PAGE_ABT : "About",
  2045. BLK_TTL : "Blacklist settings",
  2046. BLK_BLK : "Blacklist",
  2047. BLK_ON : "Enable blacklist",
  2048. ABT_TTL : "Information and useful links",
  2049. ABT_THKS : "Thanks to:",
  2050. ABT_THKS_YEPPHA : ", who's work inspired the creation of this project, without whom none of this would exist today.",
  2051. ABT_THKS_USERSCRIPT : " for making the task of developing and shipping third party software incredibly easier.",
  2052. ABT_THKS_STACKOV : " for all of its priceless information which greatly contributes for software development.",
  2053. ABT_INFO : "Official pages",
  2054. ABT_LNK_GHB : "GitHub",
  2055. ABT_LNK_GRFK : "Greasy Fork镜像",
  2056. ABT_LNK_OPNU : "OpenUserJS",
  2057. WLCM : "Thank you for installing YouTube+",
  2058. WLCMSTRT : "You can customize your settings by clicking the button above",
  2059. WLCMFTRS : "Click here to see all the features",
  2060. LOCALE : "English (US)"
  2061. };
  2062. if (!user_settings || Object.keys(user_settings).length < 1) {
  2063. user_settings = default_settings;
  2064. } else {
  2065. checkNewFeatures();
  2066. }
  2067. if (window.chrome) {
  2068. document.documentElement.addEventListener("load", scriptExit, true);
  2069. } else {
  2070. document.addEventListener("afterscriptexecute", scriptExit);
  2071. }
  2072. if (!is_userscript && !window.chrome && !user_settings.migration_instructions) {
  2073. migrationInstructions();
  2074. }
  2075. document.addEventListener("spfdone", main);
  2076. document.addEventListener("spfrequest", request);
  2077. document.addEventListener("readystatechange", main, true);
  2078. XMLHttpRequest.prototype.open = checkXHR(XMLHttpRequest.prototype.open);
  2079. window.onYouTubePlayerReady = shareApi(window.onYouTubePlayerReady);
  2080. window.matchMedia = false;
  2081. main();
  2082. },
  2083. contentScriptMessages: function() {
  2084. var key1, key2, gate, sets, locs, observer;
  2085. key1 = "parsend";
  2086. key2 = "getlocale";
  2087. gate = document.documentElement;
  2088. sets = JSON.parse(gate.dataset[key1] || null);
  2089. locs = gate.dataset[key2] || null;
  2090. if (!gate.contentscript) {
  2091. gate.contentscript = true;
  2092. observer = new MutationObserver(particle.contentScriptMessages);
  2093. return observer.observe(gate, {
  2094. attributes: true,
  2095. attributeFilter: ["data-" + key1, "data-" + key2]
  2096. });
  2097. }
  2098. if (sets) {
  2099. if (particle.is_userscript) {
  2100. particle.GM_setValue(particle.id, JSON.stringify(sets));
  2101. } else {
  2102. chrome.storage.local.set({particleSettings: sets});
  2103. }
  2104. document.documentElement.removeAttribute("data-parsend");
  2105. } else if (locs) {
  2106. document.documentElement.dataset.setlocale = chrome.i18n.getMessage(locs);
  2107. }
  2108. },
  2109. filterChromeKeys: function(keys) {
  2110. if (keys[particle.id] && keys[particle.id].new_value) {
  2111. document.documentElement.dataset.parreceive = JSON.stringify(
  2112. (keys[particle.id].new_value && keys[particle.id].new_value[particle.id]) || keys[particle.id].new_value || {}
  2113. );
  2114. }
  2115. },
  2116. main: function(event) {
  2117. var holder;
  2118. if (!event && particle.is_userscript) {
  2119. event = JSON.parse(particle.GM_getValue(particle.id, "{}"));
  2120. }
  2121. if (event) {
  2122. event = JSON.stringify(event[particle.id] || event);
  2123. document.documentElement.dataset.user_settings = event;
  2124. if (particle.is_userscript) {
  2125. holder = document.createElement("link");
  2126. holder.rel = "stylesheet";
  2127. holder.type = "text/css";
  2128. holder.href = "https://particlecore.github.io/Particle/stylesheets/YouTubePlus.css";
  2129. document.documentElement.appendChild(holder);
  2130. } else if (window.chrome) {
  2131. holder = document.createElement("style");
  2132. holder.textContent = //
  2133. `.ytplus_sprite,
  2134. #DNT:hover:after,
  2135. #player-console > div,
  2136. #P-content input[type='radio']:checked + label:before,
  2137. #P-content input[type='checkbox']:checked + label:before{
  2138. background-image: url(chrome-extension://` + window.chrome.runtime.id + `/images/sprite.png);
  2139. }`;
  2140. document.documentElement.appendChild(holder);
  2141. }
  2142. holder = document.createElement("script");
  2143. holder.textContent = "(" + particle.inject + "(" + particle.is_userscript + "))";
  2144. document.documentElement.appendChild(holder);
  2145. if (!particle.is_userscript) {
  2146. chrome.storage.onChanged.addListener(particle.filterChromeKeys);
  2147. }
  2148. }
  2149. },
  2150. ini: function() {
  2151. particle.id = "particleSettings";
  2152. particle.is_userscript = typeof GM_info === "object" ? true : false;
  2153. if (particle.is_userscript) {
  2154. particle.GM_getValue = GM_getValue;
  2155. particle.GM_setValue = GM_setValue;
  2156. particle.main();
  2157. } else {
  2158. chrome.storage.local.get(particle.id, particle.main);
  2159. }
  2160. particle.contentScriptMessages();
  2161. }
  2162. };
  2163. particle.ini();
  2164. }());
  2165. start ();
  2166.  
  2167. function start() {
  2168. var pagecontainer=document.getElementById('page-container');
  2169. if (!pagecontainer) return;
  2170. if (/^https?:\/\/www\.youtube.com\/watch\?/.test(window.location.href)) run();
  2171. var isAjax=/class[\w\s"'-=]+spf\-link/.test(pagecontainer.innerHTML);
  2172. var logocontainer=document.getElementById('logo-container');
  2173. if (logocontainer && !isAjax) { // fix for blocked videos
  2174. isAjax=(' '+logocontainer.className+' ').indexOf(' spf-link ')>=0;
  2175. }
  2176. var content=document.getElementById('content');
  2177. if (isAjax && content) { // Ajax UI
  2178. var mo=window.MutationObserver||window.WebKitMutationObserver;
  2179. if(typeof mo!=='undefined') {
  2180. var observer=new mo(function(mutations) {
  2181. mutations.forEach(function(mutation) {
  2182. if(mutation.addedNodes!==null) {
  2183. for (var i=0; i<mutation.addedNodes.length; i++) {
  2184. if (mutation.addedNodes[i].id=='watch7-container' ||
  2185. mutation.addedNodes[i].id=='watch7-main-container') { // old value: movie_player
  2186. run();
  2187. break;
  2188. }
  2189. }
  2190. }
  2191. });
  2192. });
  2193. observer.observe(content, {childList: true, subtree: true}); // old value: pagecontainer
  2194. } else { // MutationObserver fallback for old browsers
  2195. pagecontainer.addEventListener('DOMNodeInserted', onNodeInserted, false);
  2196. }
  2197. }
  2198. }
  2199.  
  2200. function onNodeInserted(e) {
  2201. if (e && e.target && (e.target.id=='watch7-container' ||
  2202. e.target.id=='watch7-main-container')) { // old value: movie_player
  2203. run();
  2204. }
  2205. }
  2206.  
  2207. function finalButton(){
  2208.  
  2209. var buttonIframeDownload = document.createElement("iframe");
  2210. buttonIframeDownload.src = '//www.youtubeinmp3.com/widget/button/?color=ba1717&amp;video=' + window.location.href;
  2211. buttonIframeDownload.scrolling = "no";
  2212. buttonIframeDownload.id = "buttonIframe";
  2213. buttonIframeDownload.style = "width:100%;height:60px;padding-top:20px;padding-bottom:20px;";
  2214.  
  2215. document.getElementById("watch-header").appendChild(buttonIframeDownload);
  2216.  
  2217. }
  2218.  
  2219. function run(){
  2220.  
  2221. if(document.getElementById("buttonIframe") === null && window.location.href.substring(0, 25).indexOf("youtube.com") > -1 && window.location.href.indexOf("watch?v=") > -1){
  2222.  
  2223. var parentButton = document.createElement("div");
  2224.  
  2225. parentButton.className = "yt-uix-button yt-uix-button-default";
  2226. parentButton.id = "parentButton";
  2227. parentButton.style = "height: 23px;margin-left: 28px;padding-bottom:1px;";
  2228.  
  2229. parentButton.onclick = function () {
  2230.  
  2231. this.style = "display:none";
  2232. finalButton();
  2233.  
  2234. };
  2235.  
  2236. document.getElementById("watch7-user-header").appendChild(parentButton);
  2237.  
  2238. var childButton = document.createElement("span");
  2239.  
  2240. childButton.appendChild(document.createTextNode("Download MP3"));
  2241.  
  2242. childButton.className = "yt-uix-button-content";
  2243. childButton.style = "line-height: 25px;font-size: 12px;";
  2244.  
  2245. parentButton.appendChild(childButton);
  2246.  
  2247. }
  2248.  
  2249. }
  2250.  
  2251. (function () {
  2252. var FORMAT_LABEL={'5':'FLV 240p','18':'MP4 360p','22':'MP4 720p','34':'FLV 360p','35':'FLV 480p','37':'MP4 1080p','38':'MP4 2160p','43':'WebM 360p','44':'WebM 480p','45':'WebM 720p','46':'WebM 1080p','135':'MP4 480p - no audio','137':'MP4 1080p - no audio','138':'MP4 2160p - no audio','139':'M4A 48kbps - audio','140':'M4A 128kbps - audio','141':'M4A 256kbps - audio','264':'MP4 1440p - no audio','266':'MP4 2160p - no audio','298':'MP4 720p60 - no audio','299':'MP4 1080p60 - no audio'};
  2253. var FORMAT_TYPE={'5':'flv','18':'mp4','22':'mp4','34':'flv','35':'flv','37':'mp4','38':'mp4','43':'webm','44':'webm','45':'webm','46':'webm','135':'mp4','137':'mp4','138':'mp4','139':'m4a','140':'m4a','141':'m4a','264':'mp4','266':'mp4','298':'mp4','299':'mp4'};
  2254. var FORMAT_ORDER=['5','18','34','43','35','135','44','22','298','45','37','299','46','264','38','266','139','140','141'];
  2255. var FORMAT_RULE={'flv':'max','mp4':'all','webm':'none','m4a':'max'};
  2256. // all=display all versions, max=only highest quality version, none=no version
  2257. // the default settings show all MP4 videos, the highest quality FLV and no WebM
  2258. var SHOW_DASH_FORMATS=false;
  2259. var BUTTON_TEXT={'ar':'تنزيل','cs':'Stáhnout','de':'Herunterladen','en':'Download','es':'Descargar','fr':'Télécharger','hi':'डाउनलोड','hu':'Letöltés','id':'Unduh','it':'Scarica','ja':'ダウンロード','ko':'내려받기','pl':'Pobierz','pt':'Baixar','ro':'Descărcați','ru':'Скачать','tr':'İndir','zh':'下载','zh-TW':'下載'};
  2260. var BUTTON_TOOLTIP={'ar':'تنزيل هذا الفيديو','cs':'Stáhnout toto video','de':'Dieses Video herunterladen','en':'Download this video','es':'Descargar este vídeo','fr':'Télécharger cette vidéo','hi':'वीडियो डाउनलोड करें','hu':'Videó letöltése','id':'Unduh video ini','it':'Scarica questo video','ja':'このビデオをダウンロードする','ko':'이 비디오를 내려받기','pl':'Pobierz plik wideo','pt':'Baixar este vídeo','ro':'Descărcați acest videoclip','ru':'Скачать это видео','tr': 'Bu videoyu indir','zh':'下载此视频','zh-TW':'下載此影片'};
  2261. var DECODE_RULE=[];
  2262. var RANDOM=7489235179; // Math.floor(Math.random()*1234567890);
  2263. var CONTAINER_ID='download-youtube-video'+RANDOM;
  2264. var LISTITEM_ID='download-youtube-video-fmt'+RANDOM;
  2265. var BUTTON_ID='download-youtube-video-button'+RANDOM;
  2266. var DEBUG_ID='download-youtube-video-debug-info';
  2267. var STORAGE_URL='download-youtube-script-url';
  2268. var STORAGE_CODE='download-youtube-signature-code';
  2269. var STORAGE_DASH='download-youtube-dash-enabled';
  2270. var isDecodeRuleUpdated=false;
  2271. start();
  2272. function start() {
  2273. var pagecontainer=document.getElementById('page-container');
  2274. if (!pagecontainer) return;
  2275. if (/^https?:\/\/www\.youtube.com\/watch\?/.test(window.location.href)) run();
  2276. var isAjax=/class[\w\s"'-=]+spf\-link/.test(pagecontainer.innerHTML);
  2277. var logocontainer=document.getElementById('logo-container');
  2278. if (logocontainer && !isAjax) { // fix for blocked videos
  2279. isAjax=(' '+logocontainer.className+' ').indexOf(' spf-link ')>=0;
  2280. }
  2281. var content=document.getElementById('content');
  2282. if (isAjax && content) { // Ajax UI
  2283. var mo=window.MutationObserver||window.WebKitMutationObserver;
  2284. if(typeof mo!=='undefined') {
  2285. var observer=new mo(function(mutations) {
  2286. mutations.forEach(function(mutation) {
  2287. if(mutation.addedNodes!==null) {
  2288. for (var i=0; i<mutation.addedNodes.length; i++) {
  2289. if (mutation.addedNodes[i].id=='watch7-container' ||
  2290. mutation.addedNodes[i].id=='watch7-main-container') { // old value: movie_player
  2291. run();
  2292. break;
  2293. }
  2294. }
  2295. }
  2296. });
  2297. });
  2298. observer.observe(content, {childList: true, subtree: true}); // old value: pagecontainer
  2299. } else { // MutationObserver fallback for old browsers
  2300. pagecontainer.addEventListener('DOMNodeInserted', onNodeInserted, false);
  2301. }
  2302. }
  2303. }
  2304.  
  2305. function onNodeInserted(e) {
  2306. if (e && e.target && (e.target.id=='watch7-container' ||
  2307. e.target.id=='watch7-main-container')) { // old value: movie_player
  2308. run();
  2309. }
  2310. }
  2311. function run() {
  2312. if (document.getElementById(CONTAINER_ID)) return; // check download container
  2313. if (document.getElementById('p') && document.getElementById('vo')) return; // Feather not supported
  2314.  
  2315. var videoID, videoFormats, videoAdaptFormats, videoManifestURL, scriptURL=null;
  2316. var isSignatureUpdatingStarted=false;
  2317. var operaTable=new Array();
  2318. var language=document.documentElement.getAttribute('lang');
  2319. var textDirection='left';
  2320. if (document.body.getAttribute('dir')=='rtl') {
  2321. textDirection='right';
  2322. }
  2323. if (document.getElementById('watch7-action-buttons')) { // old UI
  2324. fixTranslations(language, textDirection);
  2325. }
  2326. // obtain video ID, formats map
  2327. var args=null;
  2328. var usw=(typeof this.unsafeWindow !== 'undefined')?this.unsafeWindow:window; // Firefox, Opera<15
  2329. if (usw.ytplayer && usw.ytplayer.config && usw.ytplayer.config.args) {
  2330. args=usw.ytplayer.config.args;
  2331. }
  2332. if (args) {
  2333. videoID=args['video_id'];
  2334. videoFormats=args['url_encoded_fmt_stream_map'];
  2335. videoAdaptFormats=args['adaptive_fmts'];
  2336. videoManifestURL=args['dashmpd'];
  2337. debug('DYVAM - Info: Standard mode. videoID '+(videoID?videoID:'none')+'; ');
  2338. }
  2339. if (usw.ytplayer && usw.ytplayer.config && usw.ytplayer.config.assets) {
  2340. scriptURL=usw.ytplayer.config.assets.js;
  2341. }
  2342. if (videoID==null) { // unsafeWindow workaround (Chrome, Opera 15+)
  2343. var buffer=document.getElementById(DEBUG_ID+'2');
  2344. if (buffer) {
  2345. while (buffer.firstChild) {
  2346. buffer.removeChild(buffer.firstChild);
  2347. }
  2348. } else {
  2349. buffer=createHiddenElem('pre', DEBUG_ID+'2');
  2350. }
  2351. injectScript ('if(ytplayer&&ytplayer.config&&ytplayer.config.args){document.getElementById("'+DEBUG_ID+'2").appendChild(document.createTextNode(\'"video_id":"\'+ytplayer.config.args.video_id+\'", "js":"\'+ytplayer.config.assets.js+\'", "dashmpd":"\'+ytplayer.config.args.dashmpd+\'", "url_encoded_fmt_stream_map":"\'+ytplayer.config.args.url_encoded_fmt_stream_map+\'", "adaptive_fmts":"\'+ytplayer.config.args.adaptive_fmts+\'"\'));}');
  2352. var code=buffer.innerHTML;
  2353. if (code) {
  2354. videoID=findMatch(code, /\"video_id\":\s*\"([^\"]+)\"/);
  2355. videoFormats=findMatch(code, /\"url_encoded_fmt_stream_map\":\s*\"([^\"]+)\"/);
  2356. videoFormats=videoFormats.replace(/&amp;/g,'\\u0026');
  2357. videoAdaptFormats=findMatch(code, /\"adaptive_fmts\":\s*\"([^\"]+)\"/);
  2358. videoAdaptFormats=videoAdaptFormats.replace(/&amp;/g,'\\u0026');
  2359. videoManifestURL=findMatch(code, /\"dashmpd\":\s*\"([^\"]+)\"/);
  2360. scriptURL=findMatch(code, /\"js\":\s*\"([^\"]+)\"/);
  2361. }
  2362. debug('DYVAM - Info: Injection mode. videoID '+(videoID?videoID:'none')+'; ');
  2363. }
  2364. if (videoID==null) { // if all else fails
  2365. var bodyContent=document.body.innerHTML;
  2366. if (bodyContent!=null) {
  2367. videoID=findMatch(bodyContent, /\"video_id\":\s*\"([^\"]+)\"/);
  2368. videoFormats=findMatch(bodyContent, /\"url_encoded_fmt_stream_map\":\s*\"([^\"]+)\"/);
  2369. videoAdaptFormats=findMatch(bodyContent, /\"adaptive_fmts\":\s*\"([^\"]+)\"/);
  2370. videoManifestURL=findMatch(bodyContent, /\"dashmpd\":\s*\"([^\"]+)\"/);
  2371. if (scriptURL==null) {
  2372. scriptURL=findMatch(bodyContent, /\"js\":\s*\"([^\"]+)\"/);
  2373. if (scriptURL) {
  2374. scriptURL=scriptURL.replace(/\\/g,'');
  2375. }
  2376. }
  2377. }
  2378. debug('DYVAM - Info: Brute mode. videoID '+(videoID?videoID:'none')+'; ');
  2379. }
  2380. debug('DYVAM - Info: url '+window.location.href+'; useragent '+window.navigator.userAgent);
  2381. if (videoID==null || videoFormats==null || videoID.length==0 || videoFormats.length==0) {
  2382. debug('DYVAM - Error: No config information found. YouTube must have changed the code.');
  2383. return;
  2384. }
  2385. // Opera 12 extension message handler
  2386. if (typeof window.opera !== 'undefined' && window.opera && typeof opera.extension !== 'undefined') {
  2387. opera.extension.onmessage = function(event) {
  2388. var index=findMatch(event.data.action, /xhr\-([0-9]+)\-response/);
  2389. if (index && operaTable[parseInt(index,10)]) {
  2390. index=parseInt(index,10);
  2391. var trigger=(operaTable[index])['onload'];
  2392. if (typeof trigger === 'function' && event.data.readyState == 4) {
  2393. if (trigger) {
  2394. trigger(event.data);
  2395. }
  2396. }
  2397. }
  2398. }
  2399. }
  2400. if (!isDecodeRuleUpdated) {
  2401. DECODE_RULE=getDecodeRules(DECODE_RULE);
  2402. isDecodeRuleUpdated=true;
  2403. }
  2404. if (scriptURL) {
  2405. if (scriptURL.indexOf('//')==0) {
  2406. var protocol=(document.location.protocol=='http:')?'http:':'https:';
  2407. scriptURL=protocol+scriptURL;
  2408. }
  2409. fetchSignatureScript(scriptURL);
  2410. }
  2411. // video title
  2412. var videoTitle=document.title || 'video';
  2413. videoTitle=videoTitle.replace(/\s*\-\s*YouTube$/i, '').replace(/'/g, '\'').replace(/^\s+|\s+$/g, '').replace(/\.+$/g, '');
  2414. videoTitle=videoTitle.replace(/[:"\?\*]/g, '').replace(/[\|\\\/]/g, '_'); // Mac, Linux, Windows
  2415. if (((window.navigator.userAgent || '').toLowerCase()).indexOf('windows') >= 0) {
  2416. videoTitle=videoTitle.replace(/#/g, '').replace(/&/g, '_'); // Windows
  2417. } else {
  2418. videoTitle=videoTitle.replace(/#/g, '%23').replace(/&/g, '%26'); // Mac, Linux
  2419. }
  2420. // parse the formats map
  2421. var sep1='%2C', sep2='%26', sep3='%3D';
  2422. if (videoFormats.indexOf(',')>-1) {
  2423. sep1=',';
  2424. sep2=(videoFormats.indexOf('&')>-1)?'&':'\\u0026';
  2425. sep3='=';
  2426. }
  2427. var videoURL=new Array();
  2428. var videoSignature=new Array();
  2429. if (videoAdaptFormats) {
  2430. videoFormats=videoFormats+sep1+videoAdaptFormats;
  2431. }
  2432. var videoFormatsGroup=videoFormats.split(sep1);
  2433. for (var i=0;i<videoFormatsGroup.length;i++) {
  2434. var videoFormatsElem=videoFormatsGroup[i].split(sep2);
  2435. var videoFormatsPair=new Array();
  2436. for (var j=0;j<videoFormatsElem.length;j++) {
  2437. var pair=videoFormatsElem[j].split(sep3);
  2438. if (pair.length==2) {
  2439. videoFormatsPair[pair[0]]=pair[1];
  2440. }
  2441. }
  2442. if (videoFormatsPair['url']==null) continue;
  2443. var url=unescape(unescape(videoFormatsPair['url'])).replace(/\\\//g,'/').replace(/\\u0026/g,'&');
  2444. if (videoFormatsPair['itag']==null) continue;
  2445. var itag=videoFormatsPair['itag'];
  2446. var sig=videoFormatsPair['sig']||videoFormatsPair['signature'];
  2447. if (sig) {
  2448. url=url+'&signature='+sig;
  2449. videoSignature[itag]=null;
  2450. } else if (videoFormatsPair['s']) {
  2451. url=url+'&signature='+decryptSignature(videoFormatsPair['s']);
  2452. videoSignature[itag]=videoFormatsPair['s'];
  2453. }
  2454. if (url.toLowerCase().indexOf('ratebypass')==-1) { // speed up download for dash
  2455. url=url+'&ratebypass=yes';
  2456. }
  2457. if (url.toLowerCase().indexOf('http')==0) { // validate URL
  2458. videoURL[itag]=url+'&title='+videoTitle;
  2459. }
  2460. }
  2461. var showFormat=new Array();
  2462. for (var category in FORMAT_RULE) {
  2463. var rule=FORMAT_RULE[category];
  2464. for (var index in FORMAT_TYPE){
  2465. if (FORMAT_TYPE[index]==category) {
  2466. showFormat[index]=(rule=='all');
  2467. }
  2468. }
  2469. if (rule=='max') {
  2470. for (var i=FORMAT_ORDER.length-1;i>=0;i--) {
  2471. var format=FORMAT_ORDER[i];
  2472. if (FORMAT_TYPE[format]==category && videoURL[format]!=undefined) {
  2473. showFormat[format]=true;
  2474. break;
  2475. }
  2476. }
  2477. }
  2478. }
  2479. var dashPref=getPref(STORAGE_DASH);
  2480. if (dashPref=='1') {
  2481. SHOW_DASH_FORMATS=true;
  2482. } else if (dashPref!='0') {
  2483. setPref(STORAGE_DASH,'0');
  2484. }
  2485. var downloadCodeList=[];
  2486. for (var i=0;i<FORMAT_ORDER.length;i++) {
  2487. var format=FORMAT_ORDER[i];
  2488. if (format=='37' && videoURL[format]==undefined) { // hack for dash 1080p
  2489. if (videoURL['137']) {
  2490. format='137';
  2491. }
  2492. showFormat[format]=showFormat['37'];
  2493. } else if (format=='38' && videoURL[format]==undefined) { // hack for dash 4K
  2494. if (videoURL['138'] && !videoURL['266']) {
  2495. format='138';
  2496. }
  2497. showFormat[format]=showFormat['38'];
  2498. }
  2499. if (!SHOW_DASH_FORMATS && format.length>2) continue;
  2500. if (videoURL[format]!=undefined && FORMAT_LABEL[format]!=undefined && showFormat[format]) {
  2501. downloadCodeList.push({url:videoURL[format],sig:videoSignature[format],format:format,label:FORMAT_LABEL[format]});
  2502. debug('DYVAM - Info: itag'+format+' url:'+videoURL[format]);
  2503. }
  2504. }
  2505. if (downloadCodeList.length==0) {
  2506. debug('DYVAM - Error: No download URL found. Probably YouTube uses encrypted streams.');
  2507. return; // no format
  2508. }
  2509. // find parent container
  2510. var newWatchPage=false;
  2511. var parentElement=document.getElementById('watch7-action-buttons');
  2512. if (parentElement==null) {
  2513. parentElement=document.getElementById('watch8-secondary-actions');
  2514. if (parentElement==null) {
  2515. debug('DYVAM Error - No container for adding the download button. YouTube must have changed the code.');
  2516. return;
  2517. } else {
  2518. newWatchPage=true;
  2519. }
  2520. }
  2521. // get button labels
  2522. var buttonText=(BUTTON_TEXT[language])?BUTTON_TEXT[language]:BUTTON_TEXT['en'];
  2523. var buttonLabel=(BUTTON_TOOLTIP[language])?BUTTON_TOOLTIP[language]:BUTTON_TOOLTIP['en'];
  2524. // generate download code for regular interface
  2525. var mainSpan=document.createElement('span');
  2526.  
  2527. if (newWatchPage) {
  2528. var spanIcon=document.createElement('span');
  2529. spanIcon.setAttribute('class', 'yt-uix-button-icon-wrapper');
  2530. var imageIcon=document.createElement('img');
  2531. imageIcon.setAttribute('src', '//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif');
  2532. imageIcon.setAttribute('class', 'yt-uix-button-icon');
  2533. imageIcon.setAttribute('style', 'width:20px;height:20px;background-size:20px 20px;background-repeat:no-repeat;background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABG0lEQVRYR+2W0Q3CMAxE2wkYAdiEEWADmIxuACMwCmzABpCTEmRSO7YTQX+ChECV43t2nF7GYeHPuLD+0AKwC/DnWMAp/N5qimkBuAfBdRTF/+2/AV6ZYFUxVYuicAfoHegd6B3oHfhZB+ByF+JyV8FkrAB74pqH3DU5L3iGoBURhdVODIQF4EjEkWLmmhYALOQgNIBcHHke4buhxXAAaFnaAhqbQ5QAOHHkwhZ8balkx1ICCiEBWNZ+CivdB7REHIC2ZjZK2oWklDDdB1NSdCd/Js2PqQMpSIKYVcM8kE6QCwDBNRCqOBJrW0CL8kCYxL0A1k6YxWsANAiXeC2ABOEWbwHAWrwxpzgkmA/JtIqnxTOElmPnjlkc4A3FykAhA42AxwAAAABJRU5ErkJggg==);');
  2534. spanIcon.appendChild(imageIcon);
  2535. mainSpan.appendChild(spanIcon);
  2536. }
  2537.  
  2538. var spanButton=document.createElement('span');
  2539. spanButton.setAttribute('class', 'yt-uix-button-content');
  2540. spanButton.appendChild(document.createTextNode(buttonText+' '));
  2541. mainSpan.appendChild(spanButton);
  2542. if (!newWatchPage) { // old UI
  2543. var imgButton=document.createElement('img');
  2544. imgButton.setAttribute('class', 'yt-uix-button-arrow');
  2545. imgButton.setAttribute('src', '//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif');
  2546. mainSpan.appendChild(imgButton);
  2547. }
  2548.  
  2549. var listItems=document.createElement('ol');
  2550. listItems.setAttribute('style', 'display:none;');
  2551. listItems.setAttribute('class', 'yt-uix-button-menu');
  2552. for (var i=0;i<downloadCodeList.length;i++) {
  2553. var listItem=document.createElement('li');
  2554. var listLink=document.createElement('a');
  2555. listLink.setAttribute('style', 'text-decoration:none;');
  2556. listLink.setAttribute('href', downloadCodeList[i].url);
  2557. listLink.setAttribute('download', videoTitle+'.'+FORMAT_TYPE[downloadCodeList[i].format]);
  2558. var listButton=document.createElement('span');
  2559. listButton.setAttribute('class', 'yt-uix-button-menu-item');
  2560. listButton.setAttribute('loop', i+'');
  2561. listButton.setAttribute('id', LISTITEM_ID+downloadCodeList[i].format);
  2562. listButton.appendChild(document.createTextNode(downloadCodeList[i].label));
  2563. listLink.appendChild(listButton);
  2564. listItem.appendChild(listLink);
  2565. listItems.appendChild(listItem);
  2566. }
  2567. mainSpan.appendChild(listItems);
  2568. var buttonElement=document.createElement('button');
  2569. buttonElement.setAttribute('id', BUTTON_ID);
  2570. if (newWatchPage) {
  2571. buttonElement.setAttribute('class', 'yt-uix-button yt-uix-button-size-default yt-uix-button-opacity yt-uix-tooltip');
  2572. } else { // old UI
  2573. buttonElement.setAttribute('class', 'yt-uix-button yt-uix-tooltip yt-uix-button-empty yt-uix-button-text');
  2574. buttonElement.setAttribute('style', 'margin-top:4px; margin-left:'+((textDirection=='left')?5:10)+'px;');
  2575. }
  2576. buttonElement.setAttribute('data-tooltip-text', buttonLabel);
  2577. buttonElement.setAttribute('type', 'button');
  2578. buttonElement.setAttribute('role', 'button');
  2579. buttonElement.addEventListener('click', function(){return false;}, false);
  2580. buttonElement.appendChild(mainSpan);
  2581. var containerSpan=document.createElement('span');
  2582. containerSpan.setAttribute('id', CONTAINER_ID);
  2583. containerSpan.appendChild(document.createTextNode(' '));
  2584. containerSpan.appendChild(buttonElement);
  2585. // add the button
  2586. if (!newWatchPage) { // watch7
  2587. parentElement.appendChild(containerSpan);
  2588. } else { // watch8
  2589. parentElement.insertBefore(containerSpan, parentElement.firstChild);
  2590. }
  2591. // REPLACEWITH if (!isSignatureUpdatingStarted) {
  2592. for (var i=0;i<downloadCodeList.length;i++) {
  2593. addFileSize(downloadCodeList[i].url, downloadCodeList[i].format);
  2594. }
  2595. // }
  2596. if (typeof GM_download !== 'undefined') {
  2597. for (var i=0;i<downloadCodeList.length;i++) {
  2598. var downloadFMT=document.getElementById(LISTITEM_ID+downloadCodeList[i].format);
  2599. var url=(downloadCodeList[i].url).toLowerCase();
  2600. if (url.indexOf('clen=')>0 && url.indexOf('dur=')>0 && url.indexOf('gir=')>0
  2601. && url.indexOf('lmt=')>0) {
  2602. downloadFMT.addEventListener('click', downloadVideoNatively, false);
  2603. }
  2604. }
  2605. }
  2606. addFromManifest('140', '141'); // replace fmt140 with fmt141 if found in manifest
  2607. function downloadVideoNatively(e) {
  2608. var elem=e.currentTarget;
  2609. e.returnValue=false;
  2610. if (e.preventDefault) {
  2611. e.preventDefault();
  2612. }
  2613. var loop=elem.getAttribute('loop');
  2614. if (loop) {
  2615. GM_download(downloadCodeList[loop].url, videoTitle+'.'+FORMAT_TYPE[downloadCodeList[loop].format]);
  2616. }
  2617. return false;
  2618. }
  2619. function addFromManifest(oldFormat, newFormat) { // find newFormat URL in manifest
  2620. if (videoManifestURL && videoURL[newFormat]==undefined && SHOW_DASH_FORMATS && FORMAT_RULE['m4a']!='none') {
  2621. var matchSig=findMatch(videoManifestURL, /\/s\/([a-zA-Z0-9\.]+)\//i);
  2622. if (matchSig) {
  2623. var decryptedSig=decryptSignature(matchSig);
  2624. if (decryptedSig) {
  2625. videoManifestURL=videoManifestURL.replace('/s/'+matchSig+'/','/signature/'+decryptedSig+'/');
  2626. }
  2627. }
  2628. if (videoManifestURL.indexOf('//')==0) {
  2629. var protocol=(document.location.protocol=='http:')?'http:':'https:';
  2630. videoManifestURL=protocol+videoManifestURL;
  2631. }
  2632. debug('DYVAM - Info: manifestURL '+videoManifestURL);
  2633. crossXmlHttpRequest({
  2634. method:'GET',
  2635. url:videoManifestURL, // check if URL exists
  2636. onload:function(response) {
  2637. if (response.readyState === 4 && response.status === 200 && response.responseText) {
  2638. var regexp = new RegExp('<BaseURL.+>(http[^<]+itag='+newFormat+'[^<]+)<\\/BaseURL>','i');
  2639. var matchURL=findMatch(response.responseText, regexp);
  2640. debug('DYVAM - Info: matchURL '+matchURL);
  2641. if (!matchURL) return;
  2642. matchURL=matchURL.replace(/&amp\;/g,'&');
  2643. if (FORMAT_RULE['m4a']=='max') {
  2644. for (var i=0;i<downloadCodeList.length;i++) {
  2645. if (downloadCodeList[i].format==oldFormat) {
  2646. downloadCodeList[i].format==newFormat;
  2647. var downloadFMT=document.getElementById(LISTITEM_ID+oldFormat);
  2648. downloadFMT.setAttribute('id', LISTITEM_ID+newFormat);
  2649. downloadFMT.parentNode.setAttribute('href', matchURL);
  2650. downloadCodeList[i].url=matchURL;
  2651. downloadFMT.firstChild.nodeValue=FORMAT_LABEL[newFormat];
  2652. addFileSize(matchURL, newFormat);
  2653. }
  2654. }
  2655. } else if (FORMAT_RULE['m4a']=='all') {
  2656. downloadCodeList.push(
  2657. {url:matchURL,sig:videoSignature[newFormat],format:newFormat,label:FORMAT_LABEL[newFormat]});
  2658. var downloadFMT=document.getElementById(LISTITEM_ID+oldFormat);
  2659. var clone=downloadFMT.parentNode.parentNode.cloneNode(true);
  2660. clone.firstChild.firstChild.setAttribute('id', LISTITEM_ID+newFormat);
  2661. clone.firstChild.setAttribute('href', matchURL);
  2662. downloadFMT.parentNode.parentNode.parentNode.appendChild(clone);
  2663. downloadFMT=document.getElementById(LISTITEM_ID+newFormat);
  2664. downloadFMT.firstChild.nodeValue=FORMAT_LABEL[newFormat];
  2665. addFileSize(matchURL, newFormat);
  2666. }
  2667. }
  2668. }
  2669. });
  2670. }
  2671. }
  2672. function injectStyle(code) {
  2673. var style=document.createElement('style');
  2674. style.type='text/css';
  2675. style.appendChild(document.createTextNode(code));
  2676. document.getElementsByTagName('head')[0].appendChild(style);
  2677. }
  2678. function injectScript(code) {
  2679. var script=document.createElement('script');
  2680. script.type='application/javascript';
  2681. script.textContent=code;
  2682. document.body.appendChild(script);
  2683. document.body.removeChild(script);
  2684. }
  2685. function debug(str) {
  2686. var debugElem=document.getElementById(DEBUG_ID);
  2687. if (!debugElem) {
  2688. debugElem=createHiddenElem('div', DEBUG_ID);
  2689. }
  2690. debugElem.appendChild(document.createTextNode(str+' '));
  2691. }
  2692. function createHiddenElem(tag, id) {
  2693. var elem=document.createElement(tag);
  2694. elem.setAttribute('id', id);
  2695. elem.setAttribute('style', 'display:none;');
  2696. document.body.appendChild(elem);
  2697. return elem;
  2698. }
  2699. function fixTranslations(language, textDirection) {
  2700. if (/^af|bg|bn|ca|cs|de|el|es|et|eu|fa|fi|fil|fr|gl|hi|hr|hu|id|it|iw|kn|lv|lt|ml|mr|ms|nl|pl|ro|ru|sl|sk|sr|sw|ta|te|th|uk|ur|vi|zu$/.test(language)) { // fix international UI
  2701. var likeButton=document.getElementById('watch-like');
  2702. if (likeButton) {
  2703. var spanElements=likeButton.getElementsByClassName('yt-uix-button-content');
  2704. if (spanElements) {
  2705. spanElements[0].style.display='none'; // hide like text
  2706. }
  2707. }
  2708. var marginPixels=10;
  2709. if (/^bg|ca|cs|el|eu|hr|it|ml|ms|pl|sl|sw|te$/.test(language)) {
  2710. marginPixels=1;
  2711. }
  2712. injectStyle('#watch7-secondary-actions .yt-uix-button{margin-'+textDirection+':'+marginPixels+'px!important}');
  2713. }
  2714. }
  2715. function findMatch(text, regexp) {
  2716. var matches=text.match(regexp);
  2717. return (matches)?matches[1]:null;
  2718. }
  2719. function isString(s) {
  2720. return (typeof s==='string' || s instanceof String);
  2721. }
  2722. function isInteger(n) {
  2723. return (typeof n==='number' && n%1==0);
  2724. }
  2725. function getPref(name) { // cross-browser GM_getValue
  2726. var a='', b='';
  2727. try {a=typeof GM_getValue.toString; b=GM_getValue.toString()} catch(e){}
  2728. if (typeof GM_getValue === 'function' &&
  2729. (a === 'undefined' || b.indexOf('not supported') === -1)) {
  2730. return GM_getValue(name, null); // Greasemonkey, Tampermonkey, Firefox extension
  2731. } else {
  2732. var ls=null;
  2733. try {ls=window.localStorage||null} catch(e){}
  2734. if (ls) {
  2735. return ls.getItem(name); // Chrome script, Opera extensions
  2736. }
  2737. }
  2738. return;
  2739. }
  2740. function setPref(name, value) { // cross-browser GM_setValue
  2741. var a='', b='';
  2742. try {a=typeof GM_setValue.toString; b=GM_setValue.toString()} catch(e){}
  2743. if (typeof GM_setValue === 'function' &&
  2744. (a === 'undefined' || b.indexOf('not supported') === -1)) {
  2745. GM_setValue(name, value); // Greasemonkey, Tampermonkey, Firefox extension
  2746. } else {
  2747. var ls=null;
  2748. try {ls=window.localStorage||null} catch(e){}
  2749. if (ls) {
  2750. return ls.setItem(name, value); // Chrome script, Opera extensions
  2751. }
  2752. }
  2753. }
  2754. function crossXmlHttpRequest(details) { // cross-browser GM_xmlhttpRequest
  2755. if (typeof GM_xmlhttpRequest === 'function') { // Greasemonkey, Tampermonkey, Firefox extension, Chrome script
  2756. GM_xmlhttpRequest(details);
  2757. } else if (typeof window.opera !== 'undefined' && window.opera && typeof opera.extension !== 'undefined' &&
  2758. typeof opera.extension.postMessage !== 'undefined') { // Opera 12 extension
  2759. var index=operaTable.length;
  2760. opera.extension.postMessage({'action':'xhr-'+index, 'url':details.url, 'method':details.method});
  2761. operaTable[index]=details;
  2762. } else if (typeof window.opera === 'undefined' && typeof XMLHttpRequest === 'function') { // Opera 15+ extension
  2763. var xhr=new XMLHttpRequest();
  2764. xhr.onreadystatechange = function() {
  2765. if (xhr.readyState == 4) {
  2766. if (details['onload']) {
  2767. details['onload'](xhr);
  2768. }
  2769. }
  2770. }
  2771. xhr.open(details.method, details.url, true);
  2772. xhr.send();
  2773. }
  2774. }
  2775. function addFileSize(url, format) {
  2776. function updateVideoLabel(size, format) {
  2777. var elem=document.getElementById(LISTITEM_ID+format);
  2778. if (elem) {
  2779. size=parseInt(size,10);
  2780. if (size>=1073741824) {
  2781. size=parseFloat((size/1073741824).toFixed(1))+' GB';
  2782. } else if (size>=1048576) {
  2783. size=parseFloat((size/1048576).toFixed(1))+' MB';
  2784. } else {
  2785. size=parseFloat((size/1024).toFixed(1))+' KB';
  2786. }
  2787. if (elem.childNodes.length>1) {
  2788. elem.lastChild.nodeValue=' ('+size+')';
  2789. } else if (elem.childNodes.length==1) {
  2790. elem.appendChild(document.createTextNode(' ('+size+')'));
  2791. }
  2792. }
  2793. }
  2794. var matchSize=findMatch(url, /[&\?]clen=([0-9]+)&/i);
  2795. if (matchSize) {
  2796. updateVideoLabel(matchSize, format);
  2797. } else {
  2798. try {
  2799. crossXmlHttpRequest({
  2800. method:'HEAD',
  2801. url:url,
  2802. onload:function(response) {
  2803. if (response.readyState == 4 && response.status == 200) { // add size
  2804. var size=0;
  2805. if (typeof response.getResponseHeader === 'function') {
  2806. size=response.getResponseHeader('Content-length');
  2807. } else if (response.responseHeaders) {
  2808. var regexp = new RegExp('^Content\-length: (.*)$','im');
  2809. var match = regexp.exec(response.responseHeaders);
  2810. if (match) {
  2811. size=match[1];
  2812. }
  2813. }
  2814. if (size) {
  2815. updateVideoLabel(size, format);
  2816. }
  2817. }
  2818. }
  2819. });
  2820. } catch(e) { }
  2821. }
  2822. }
  2823. function findSignatureCode(sourceCode) {
  2824. debug('DYVAM - Info: signature start '+getPref(STORAGE_CODE));
  2825. var signatureFunctionName =
  2826. findMatch(sourceCode,
  2827. /\.set\s*\("signature"\s*,\s*([a-zA-Z0-9_$][\w$]*)\(/)
  2828. || findMatch(sourceCode,
  2829. /\.sig\s*\|\|\s*([a-zA-Z0-9_$][\w$]*)\(/)
  2830. || findMatch(sourceCode,
  2831. /\.signature\s*=\s*([a-zA-Z_$][\w$]*)\([a-zA-Z_$][\w$]*\)/); //old
  2832. if (signatureFunctionName == null) return setPref(STORAGE_CODE, 'error');
  2833. signatureFunctionName=signatureFunctionName.replace('$','\\$');
  2834. var regCode = new RegExp(signatureFunctionName + '\\s*=\\s*function' +
  2835. '\\s*\\([\\w$]*\\)\\s*{[\\w$]*=[\\w$]*\\.split\\(""\\);\n*(.+);return [\\w$]*\\.join');
  2836. var regCode2 = new RegExp('function \\s*' + signatureFunctionName +
  2837. '\\s*\\([\\w$]*\\)\\s*{[\\w$]*=[\\w$]*\\.split\\(""\\);\n*(.+);return [\\w$]*\\.join');
  2838. var functionCode = findMatch(sourceCode, regCode) || findMatch(sourceCode, regCode2);
  2839. debug('DYVAM - Info: signaturefunction ' + signatureFunctionName + ' -- ' + functionCode);
  2840. if (functionCode == null) return setPref(STORAGE_CODE, 'error');
  2841. var reverseFunctionName = findMatch(sourceCode,
  2842. /([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.reverse\s*\(\s*\)\s*}/);
  2843. debug('DYVAM - Info: reversefunction ' + reverseFunctionName);
  2844. if (reverseFunctionName) reverseFunctionName=reverseFunctionName.replace('$','\\$');
  2845. var sliceFunctionName = findMatch(sourceCode,
  2846. /([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*,\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.(?:slice|splice)\(.+\)\s*}/);
  2847. debug('DYVAM - Info: slicefunction ' + sliceFunctionName);
  2848. if (sliceFunctionName) sliceFunctionName=sliceFunctionName.replace('$','\\$');
  2849. var regSlice = new RegExp('\\.(?:'+'slice'+(sliceFunctionName?'|'+sliceFunctionName:'')+
  2850. ')\\s*\\(\\s*(?:[a-zA-Z_$][\\w$]*\\s*,)?\\s*([0-9]+)\\s*\\)'); // .slice(5) sau .Hf(a,5)
  2851. var regReverse = new RegExp('\\.(?:'+'reverse'+(reverseFunctionName?'|'+reverseFunctionName:'')+
  2852. ')\\s*\\([^\\)]*\\)'); // .reverse() sau .Gf(a,45)
  2853. var regSwap = new RegExp('[\\w$]+\\s*\\(\\s*[\\w$]+\\s*,\\s*([0-9]+)\\s*\\)');
  2854. var regInline = new RegExp('[\\w$]+\\[0\\]\\s*=\\s*[\\w$]+\\[([0-9]+)\\s*%\\s*[\\w$]+\\.length\\]');
  2855. var functionCodePieces=functionCode.split(';');
  2856. var decodeArray=[];
  2857. for (var i=0; i<functionCodePieces.length; i++) {
  2858. functionCodePieces[i]=functionCodePieces[i].trim();
  2859. var codeLine=functionCodePieces[i];
  2860. if (codeLine.length>0) {
  2861. var arrSlice=codeLine.match(regSlice);
  2862. var arrReverse=codeLine.match(regReverse);
  2863. debug(i+': '+codeLine+' --'+(arrSlice?' slice length '+arrSlice.length:'') +' '+(arrReverse?'reverse':''));
  2864. if (arrSlice && arrSlice.length >= 2) { // slice
  2865. var slice=parseInt(arrSlice[1], 10);
  2866. if (isInteger(slice)){
  2867. decodeArray.push(-slice);
  2868. } else return setPref(STORAGE_CODE, 'error');
  2869. } else if (arrReverse && arrReverse.length >= 1) { // reverse
  2870. decodeArray.push(0);
  2871. } else if (codeLine.indexOf('[0]') >= 0) { // inline swap
  2872. if (i+2<functionCodePieces.length &&
  2873. functionCodePieces[i+1].indexOf('.length') >= 0 &&
  2874. functionCodePieces[i+1].indexOf('[0]') >= 0) {
  2875. var inline=findMatch(functionCodePieces[i+1], regInline);
  2876. inline=parseInt(inline, 10);
  2877. decodeArray.push(inline);
  2878. i+=2;
  2879. } else return setPref(STORAGE_CODE, 'error');
  2880. } else if (codeLine.indexOf(',') >= 0) { // swap
  2881. var swap=findMatch(codeLine, regSwap);
  2882. swap=parseInt(swap, 10);
  2883. if (isInteger(swap) && swap>0){
  2884. decodeArray.push(swap);
  2885. } else return setPref(STORAGE_CODE, 'error');
  2886. } else return setPref(STORAGE_CODE, 'error');
  2887. }
  2888. }
  2889. if (decodeArray) {
  2890. setPref(STORAGE_URL, scriptURL);
  2891. setPref(STORAGE_CODE, decodeArray.toString());
  2892. DECODE_RULE=decodeArray;
  2893. debug('DYVAM - Info: signature '+decodeArray.toString()+' '+scriptURL);
  2894. // update download links and add file sizes
  2895. for (var i=0;i<downloadCodeList.length;i++) {
  2896. var elem=document.getElementById(LISTITEM_ID+downloadCodeList[i].format);
  2897. var url=downloadCodeList[i].url;
  2898. var sig=downloadCodeList[i].sig;
  2899. if (elem && url && sig) {
  2900. url=url.replace(/\&signature=[\w\.]+/, '&signature='+decryptSignature(sig));
  2901. elem.parentNode.setAttribute('href', url);
  2902. addFileSize(url, downloadCodeList[i].format);
  2903. }
  2904. }
  2905. }
  2906. }
  2907. function isValidSignatureCode(arr) { // valid values: '5,-3,0,2,5', 'error'
  2908. if (!arr) return false;
  2909. if (arr=='error') return true;
  2910. arr=arr.split(',');
  2911. for (var i=0;i<arr.length;i++) {
  2912. if (!isInteger(parseInt(arr[i],10))) return false;
  2913. }
  2914. return true;
  2915. }
  2916. function fetchSignatureScript(scriptURL) {
  2917. var storageURL=getPref(STORAGE_URL);
  2918. var storageCode=getPref(STORAGE_CODE);
  2919. if (!(/,0,|^0,|,0$|\-/.test(storageCode))) storageCode=null; // hack for only positive items
  2920. if (storageCode && isValidSignatureCode(storageCode) && storageURL &&
  2921. scriptURL.replace(/^https?/i,'')==storageURL.replace(/^https?/i,'')) return;
  2922. try {
  2923. debug('DYVAM fetch '+scriptURL);
  2924. isSignatureUpdatingStarted=true;
  2925. crossXmlHttpRequest({
  2926. method:'GET',
  2927. url:scriptURL,
  2928. onload:function(response) {
  2929. debug('DYVAM fetch status '+response.status);
  2930. if (response.readyState === 4 && response.status === 200 && response.responseText) {
  2931. findSignatureCode(response.responseText);
  2932. }
  2933. }
  2934. });
  2935. } catch(e) { }
  2936. }
  2937. function getDecodeRules(rules) {
  2938. var storageCode=getPref(STORAGE_CODE);
  2939. if (storageCode && storageCode!='error' && isValidSignatureCode(storageCode)) {
  2940. var arr=storageCode.split(',');
  2941. for (var i=0; i<arr.length; i++) {
  2942. arr[i]=parseInt(arr[i], 10);
  2943. }
  2944. rules=arr;
  2945. debug('DYVAM - Info: signature '+arr.toString()+' '+scriptURL);
  2946. }
  2947. return rules;
  2948. }
  2949. function decryptSignature(sig) {
  2950. function swap(a,b){var c=a[0];a[0]=a[b%a.length];a[b]=c;return a};
  2951. function decode(sig, arr) { // encoded decryption
  2952. if (!isString(sig)) return null;
  2953. var sigA=sig.split('');
  2954. for (var i=0;i<arr.length;i++) {
  2955. var act=arr[i];
  2956. if (!isInteger(act)) return null;
  2957. sigA=(act>0)?swap(sigA, act):((act==0)?sigA.reverse():sigA.slice(-act));
  2958. }
  2959. var result=sigA.join('');
  2960. return result;
  2961. }
  2962. if (sig==null) return '';
  2963. var arr=DECODE_RULE;
  2964. if (arr) {
  2965. var sig2=decode(sig, arr);
  2966. if (sig2) return sig2;
  2967. } else {
  2968. setPref(STORAGE_URL, '');
  2969. setPref(STORAGE_CODE, '');
  2970. }
  2971. return sig;
  2972. }
  2973. }
  2974. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址