Youtube polymer engine fixes

Some fixes for Youtube polymer engine

目前为 2022-06-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube polymer engine fixes
  3. // @description Some fixes for Youtube polymer engine
  4. // @namespace bo.gd.an[at]rambler.ru
  5. // @version 2.17.0
  6. // @match https://www.youtube.com/*
  7. // @compatible firefox 56
  8. // @author Bogudan
  9. // @grant GM_info
  10. // @grant GM.info
  11. // @grant GM_getValue
  12. // @grant GM.getValue
  13. // @grant GM_setValue
  14. // @grant GM.setValue
  15. // @noframes
  16. // @run-at document-start
  17. // @license For personal use only
  18. // ==/UserScript==
  19.  
  20. (function () {
  21. 'use strict';
  22. if (document.location.pathname == '/error') // нам нечего делать на страницах с ошибками
  23. return;
  24. function setDefault (obj, key, value) {
  25. if (!(key in obj))
  26. obj [key] = value;
  27. }
  28. // test local storage availability and load settings from there first
  29. let settings, ls, saver;
  30. try {
  31. function lsTest (st, v) {
  32. st.setItem ('__fix_test__', v);
  33. return st.getItem ('__fix_test__') == v;
  34. };
  35. const _s = window.localStorage;
  36. if (lsTest (_s, 'qwe') && lsTest (_s, 'rty')) { // do 2 times just in case LS stored value once, but does not let change it later
  37. ls = _s;
  38. ls.removeItem ('__fix_test__');
  39. settings = JSON.parse (ls.getItem ('__fix__settings__'));
  40. }
  41. }
  42. catch (e) { }
  43. // select storage: GM_*/GM.* or local storage (in case userscript manager does not grant us GMs)
  44. if (typeof (GM_getValue) !== 'undefined' && typeof (GM_setValue) !== 'undefined' && GM_getValue && GM_setValue) {
  45. saver = function () {
  46. GM_setValue ('settings', settings);
  47. }
  48. if (!settings)
  49. settings = GM_getValue ('settings', {});
  50. else {
  51. saver ();
  52. ls.removeItem ('__fix__settings__');
  53. }
  54. settings.storage = 'GM_*Value';
  55. }
  56. else if (typeof (GM) !== 'undefined' && GM && GM.getValue && GM.setValue) {
  57. saver = function () {
  58. (async () => await GM.setValue ('settings', settings)) ();
  59. };
  60. if (!settings)
  61. settings = (async () => await GM.getValue ('settings', {})) ();
  62. else {
  63. saver ();
  64. ls.removeItem ('__fix__settings__');
  65. }
  66. settings.storage = 'GM.*Value';
  67. }
  68. else if (ls) {
  69. if (!settings)
  70. settings = {};
  71. saver = function () {
  72. ls.setItem ('__fix__settings__', JSON.stringify (settings));
  73. };
  74. settings.storage = 'window.localStorage.*Item';
  75. }
  76. else
  77. settings = {};
  78. // delete old settings
  79. if ("default_player_640" in settings) { // удалено в 0.5
  80. settings.default_player = settings.default_player_640 ? 3 : 0;
  81. delete settings.default_player_640;
  82. }
  83. if ("reduce_thumbnail" in settings) { // удалено в 0.6.0
  84. settings.thumbnail_size = settings.reduce_thumbnail ? 2 : 0;
  85. delete settings.reduce_thumbnail;
  86. }
  87. if ("reduce_font" in settings) { // удалено в 2.5.8: размеры текста уменьшились на стороне YT
  88. settings.fix_removed_placeholder = settings.reduce_font;
  89. delete settings.reduce_font;
  90. }
  91. if ("wide_description" in settings) { // удалено в 2.9.1
  92. settings.description_width = settings.wide_description ? 1 : 0;
  93. delete settings.wide_description;
  94. }
  95. if ("restore_dislikes" in settings) // удалено в 2.14.3 -- данные бульше не предоставляются
  96. delete settings.restore_dislikes;
  97. if ("exact_view_count" in settings) { // удалено в 2.16.0
  98. settings.view_count_mod = settings.exact_view_count ? 2 : 1;
  99. delete settings.exact_view_count;
  100. }
  101. // set default values
  102. const gminfo = typeof (GM_info) !== 'undefined' && GM_info || typeof (GM) !== 'undefined' && GM && GM.info;
  103. const fix_version = gminfo && gminfo.script && gminfo.script.version;
  104. let load_version = settings.version;
  105. if (fix_version) {
  106. settings.version = fix_version;
  107. setDefault (settings, "inst_ver", fix_version);
  108. }
  109. setDefault (settings, "align_player", 0);
  110. setDefault (settings, "default_player", 0);
  111. setDefault (settings, "hide_guide", true);
  112. setDefault (settings, "hide_yt_suggested_blocks", true);
  113. setDefault (settings, "logo_target", "");
  114. setDefault (settings, "fix_removed_placeholder", true);
  115. setDefault (settings, "theater_player", 0);
  116. setDefault (settings, "thumbnail_size", 2);
  117. setDefault (settings, "thumbnail_size_m", 720);
  118. setDefault (settings, "unfix_header", true);
  119. setDefault (settings, "search_thumbnail", 0);
  120. setDefault (settings, "clear_search", false);
  121. setDefault (settings, "channel_top", 0);
  122. setDefault (settings, "try_load_more", false);
  123. setDefault (settings, "unbound_video_title", false);
  124. setDefault (settings, "video_quality", 0);
  125. setDefault (settings, "no_resume_time", false);
  126. setDefault (settings, "remove_yt_redirect", false);
  127. setDefault (settings, "resume_bar_handling", settings.no_resume_time ? 1 : 0);
  128. setDefault (settings, "watched_grayscale", 0);
  129. setDefault (settings, "watched_blur", 0);
  130. setDefault (settings, "disable_player_click_overlay", false);
  131. setDefault (settings, "description_width", 0);
  132. setDefault (settings, "simpler_fullscreen", false);
  133. setDefault (settings, 'clear_link_pp', false);
  134. setDefault (settings, 'exact_likes', false);
  135. setDefault (settings, 'disable_video_preview', false);
  136. setDefault (settings, 'hide_engagement_panel_transcript', false);
  137. setDefault (settings, 'hide_engagement_panel_structured_description', false);
  138. setDefault (settings, 'short_to_full', false);
  139. setDefault (settings, 'update_alert', 0);
  140. setDefault (settings, 'view_count_mod', 1);
  141. setDefault (settings, 'hide_metadata', false);
  142. console.log ('fix settings:', settings);
  143. // catch "settings" page
  144. if (document.location.pathname == '/fix-settings') {
  145. window.addEventListener ('DOMContentLoaded', () => { document.title = "YouTube Polymer Fixes: Settings"; });
  146. const back = document.createElement ('div');
  147. back.className = 'ytfixback';
  148. const plane = document.createElement ('div');
  149. plane.className = 'ytfix';
  150. const style = document.createElement ('style');
  151. style.type = 'text/css';
  152. style.innerHTML = `
  153. .ytfix{position:absolute;left:0;top:0;right:0;background:#eee;padding:3em}
  154. .ytfix_line{margin:1em}
  155. .ytfix_line span,.ytfix_line input,.ytfix_line select{margin-right:1em}
  156. .ytfix_field{padding:0.2em;border:1px solid #888}
  157. .ytfix_button{padding:0.4em;border:1px solid #888}
  158. .ytfix_line fieldset {border:1px solid #ccc;padding:0 0.5em}
  159. .ytfix_line legend {padding:0 0.5em}
  160. .ytfix_line img {margin:0 1em}
  161. .ytfix_hide{display:none}
  162. .ytfixback{position:absolute;left:0;top:0;right:0;height:100%;background:#eee}
  163. .ytfix_slide_base {position:relative;margin-right:1em;display:inline-block;height:1em}
  164. .ytfix_slide_bar {position:absolute;left:0;right:0;top:0.3em;bottom:0.3em;background:#ddd;border:1px solid #ccc}
  165. .ytfix_slide_stroke {position:absolute;width:1px;top:0.1em;bottom:0.1em;background:#ccc}
  166. .ytfix_slide_arrow {position:absolute;width:9px;bottom:0;top:0;background:#ddd;border:1px solid #aaa;border-radius:0.5em}
  167. .ytfix_tabs {position:relative}
  168. .ytfix_tabs > div {display:none;border:1px solid #ccc}
  169. .ytfix_tabs > input {display:none}
  170. .ytfix_tabs > label {display:inline-block;background:#ddd;border:1px solid #ccc;padding:0.5em 1em;position:relative;top:1px}
  171. .ytfix_tabs > label ~ label {border-left:none}
  172. .ytfix_tabs > input:checked + label {background-color:#eee;border-bottom:1px solid #eee}
  173. `;
  174. plane.appendChild (style);
  175. function AddLine (plane) {
  176. const q = document.createElement ('div');
  177. q.className = 'ytfix_line';
  178. for (let i = 1, L = arguments.length; i < L; ++i)
  179. q.appendChild (arguments [i]);
  180. plane.appendChild (q);
  181. return q;
  182. }
  183. let e1, e2;
  184. e1 = document.createElement ('b');
  185. e1.appendChild (document.createTextNode ('YouTube Polymer Fixes: Settings'));
  186. AddLine (plane, e1);
  187. if (fix_version) {
  188. e1 = document.createElement ('b');
  189. e1.appendChild (document.createTextNode (`Version: ${fix_version}`));
  190. AddLine (plane, e1);
  191. }
  192. if (!saver) {
  193. e1 = document.createElement ('span');
  194. e1.style = 'color:red';
  195. e1.appendChild (document.createTextNode ('Cannot edit settings: no access to any storage.'));
  196. AddLine (plane, e1);
  197. e1 = document.createElement ('span');
  198. e1.appendChild (document.createTextNode ('If you are using Firefox, allow cookies for this site.'));
  199. AddLine (plane, e1);
  200. }
  201. else {
  202. const ess = {};
  203. function MakeDesc (desc, extra) {
  204. const e = document.createElement ('span');
  205. e.appendChild (document.createTextNode (desc));
  206. if (extra) {
  207. if (extra.style)
  208. e.style = style;
  209. if (extra.note) {
  210. const n = document.createElement ('sup');
  211. n.appendChild (document.createTextNode (extra.note));
  212. e.appendChild (n);
  213. }
  214. }
  215. return e;
  216. }
  217. function MakeNote (num, desc, extra) {
  218. const e = document.createElement ('span');
  219. if (num) {
  220. const n = document.createElement ('sup');
  221. n.appendChild (document.createTextNode (num));
  222. e.appendChild (n);
  223. }
  224. e.appendChild (document.createTextNode (desc));
  225. if (extra && extra.style)
  226. e.style = 'font-size:0.75em;' + extra.style;
  227. else
  228. e.style = 'font-size:0.75em';
  229. return e;
  230. }
  231. function MakeBoolElement (nm) {
  232. const e = document.createElement ('input');
  233. e.type = 'checkbox';
  234. e.checked = settings [nm];
  235. ess [nm] = e;
  236. return e;
  237. }
  238. function MakeListElement (nm, opts) {
  239. const e = document.createElement ('select');
  240. e.className = 'ytfix_field';
  241. ess [nm] = e;
  242. for (let i = 0, L = opts.length; i < L; ++i) {
  243. const o = document.createElement ('option');
  244. o.appendChild (document.createTextNode (opts [i]));
  245. e.appendChild (o);
  246. }
  247. e.selectedIndex = settings [nm];
  248. return e;
  249. }
  250. function MakeTextElement (nm) {
  251. const e = document.createElement ('input');
  252. e.className = 'ytfix_field';
  253. e.value = settings [nm];
  254. ess [nm] = e;
  255. return e;
  256. }
  257. function MakeSlider (nm, width, snap, steps) {
  258. let desc = { value : -1, mouse : false };
  259. const e = document.createElement ('div');
  260. e.className = 'ytfix_slide_base';
  261. e.style.width = `${width*snap*steps+1}px`;
  262. const b = document.createElement ('div');
  263. b.className = 'ytfix_slide_bar';
  264. e.appendChild (b);
  265. for (let x = width * snap * steps; x >= 0; x -= width * snap) {
  266. const s = document.createElement ('div');
  267. s.className = 'ytfix_slide_stroke';
  268. s.style.left = `${x}px`;
  269. e.appendChild (s);
  270. }
  271. const a = document.createElement ('div');
  272. a.className = 'ytfix_slide_arrow';
  273. e.appendChild (a);
  274. const i = document.createElement ('input');
  275. i.className = 'ytfix_field';
  276. i.type = 'number';
  277. i.style.width = `${(snap*steps).toString().length+2}em`;
  278. i.min = 0;
  279. i.max = snap * steps;
  280. i.step = 1;
  281. function UpdateValue (newvalue) {
  282. if (newvalue < 0)
  283. newvalue = 0;
  284. else if (newvalue > snap * steps)
  285. newvalue = snap * steps;
  286. if (newvalue == desc.value)
  287. return;
  288. desc.value = newvalue;
  289. a.style.left = `${desc.value*width-5}px`;
  290. i.value = desc.value;
  291. if (desc.callback)
  292. desc.callback (desc);
  293. }
  294. UpdateValue (settings [nm]);
  295. e.addEventListener ('mousedown', function (ev) {
  296. if (ev.buttons != 1)
  297. return;
  298. desc.mouse = ev.target === a;
  299. if (desc.mouse)
  300. return;
  301. let sliderRect = a.getBoundingClientRect ();
  302. if (ev.clientX <= sliderRect.left)
  303. UpdateValue (desc.value - snap);
  304. else if (ev.clientX > sliderRect.right)
  305. UpdateValue (desc.value + snap);
  306. });
  307. e.addEventListener ('mousemove', function (ev) {
  308. if (ev.buttons != 1 || !desc.mouse)
  309. return;
  310. let mx = ev.clientX - e.getBoundingClientRect ().left;
  311. mx += (width * snap) >> 1;
  312. mx -= mx % (width * snap);
  313. UpdateValue (mx / width);
  314. });
  315. i.addEventListener ('input', function () {
  316. if (/^\d+$/.test (i.value))
  317. UpdateValue (parseInt (i.value));
  318. });
  319. desc.base = e;
  320. desc.input = i;
  321. ess [nm] = desc;
  322. return desc;
  323. }
  324. function MakeButton (text, click) {
  325. const e = document.createElement ('input');
  326. e.type = 'button';
  327. e.className = 'ytfix_button';
  328. e.value = text;
  329. e.addEventListener ('click', click);
  330. return e;
  331. }
  332. const tabs_data = [];
  333. function MakeTab (name, text, checked) {
  334. const inp = document.createElement ('input');
  335. inp.type = 'radio';
  336. inp.id = name;
  337. inp.name = 'tabs';
  338. if (checked)
  339. inp.setAttribute ('checked', '');
  340. const lbl = document.createElement ('label');
  341. lbl.setAttribute ('for', name);
  342. lbl.appendChild (document.createTextNode (text));
  343. const cont = document.createElement ('div');
  344. cont.id = name + '_cont';
  345. style.innerHTML += `.ytfix_tabs > input#${name}:checked ~ div#${name}_cont {display:block}`;
  346. tabs_data.push ({ inp: inp, lbl: lbl, cont: cont });
  347. return cont;
  348. }
  349. const tab_gen = MakeTab ('tab_gen', 'General', true);
  350. const tab_front = MakeTab ('tab_front', 'Front page', false);
  351. const tab_search = MakeTab ('tab_search', 'Search', false);
  352. const tab_video = MakeTab ('tab_video', 'Video', false);
  353. const tab_channel = MakeTab ('tab_channel', 'Channel', false);
  354. const tab_script = MakeTab ('tab_script', 'Script', false);
  355. const tabs_data_2 = [plane];
  356. tabs_data.forEach ((x) => tabs_data_2.push (x.inp, x.lbl));
  357. const tabbf = document.createElement ('div');
  358. tabbf.style = 'display:block;width:1px;height:2px;border-width:0 0 0 1px;position:absolute';
  359. tabs_data_2.push (tabbf);
  360. tabs_data.forEach ((x) => tabs_data_2.push (x.cont));
  361. const tabs = AddLine.apply (this, tabs_data_2);
  362. tabs.className += ' ytfix_tabs';
  363.  
  364. AddLine (tab_gen, MakeBoolElement ("hide_guide"), MakeDesc ('Hide "Guide" menu when page opens'));
  365. AddLine (tab_video, MakeBoolElement ("fix_removed_placeholder"), MakeDesc ('Make size of "Video removed" placeholder about the same as removed video description'));
  366. const tsm = MakeTextElement ("thumbnail_size_m");
  367. tsm.className = settings.thumbnail_size == 5 ? 'ytfix_field' : 'ytfix_hide';
  368. const tsi = MakeListElement ("thumbnail_size", ['default', '180px', '240px', '360px', '480px', 'manual']);
  369. tsi.addEventListener ('change', function () {
  370. ess.thumbnail_size_m.className = ess.thumbnail_size.selectedIndex == 5 ? 'ytfix_field' : 'ytfix_hide';
  371. });
  372. AddLine (tab_front, MakeDesc ('Set thumbnails width'), tsi, tsm);
  373. AddLine (tab_search, MakeDesc ('Set thumbnails width'), MakeListElement ("search_thumbnail", ['default', '240px', '360px']));
  374. AddLine (tab_video, MakeDesc ("Set player height in default mode"), MakeListElement ("default_player", ['default', '144px', '240px', '360px', '480px', '720px']));
  375. AddLine (tab_video, MakeDesc ("Set player height in theater mode"), MakeListElement ("theater_player", ['default', '144px', '240px', '360px', '480px', '720px']));
  376. AddLine (tab_front, MakeBoolElement ("hide_yt_suggested_blocks"), MakeDesc ('Hide suggestions blocks (recommended playlists, latest posts, etc.)'));
  377. AddLine (tab_search, MakeBoolElement ("clear_search"), MakeDesc ("Hide suggestions blocks (for you, people also watched, etc.)"));
  378. AddLine (tab_gen, MakeBoolElement ("unfix_header"), MakeDesc ("Unstick header bar from top of the screen"));
  379. AddLine (tab_video, MakeDesc ("Align resized player into it's container (normal and theater modes)"), MakeListElement ("align_player", ['center', 'left', 'right']));
  380. AddLine (tab_channel, MakeDesc ("Channel banner behaviour"), MakeListElement ('channel_top', ['default', 'hide banner with scrolling', 'hide banner entirely']));
  381. AddLine (tab_gen, MakeBoolElement ('try_load_more'), MakeDesc ('Add button to try loading more content on pages with dynamic content load'));
  382. AddLine (tab_gen, MakeBoolElement ('unbound_video_title'), MakeDesc ('Remove size limit for video titles'));
  383. AddLine (tab_gen, MakeDesc ("Change YT logo target to https://www.youtube.com/..."), MakeTextElement ("logo_target"));
  384. AddLine (tab_gen, MakeBoolElement ("remove_yt_redirect"), MakeDesc ('Remove YT tracking from links (/redirect?...)'));
  385. AddLine (tab_gen, MakeBoolElement ("no_resume_time"), MakeDesc ('Remove resume time from the video links (&t=...)'));
  386. AddLine (tab_gen, MakeDesc ('Video resume bar (red)'), MakeListElement ("resume_bar_handling", ['depending on resume time (default)', 'full width of thumbnail', 'hide']));
  387. const wwfs = document.createElement ('fieldset');
  388. const wwl = wwfs.appendChild (document.createElement ('legend'));
  389. wwl.appendChild (document.createTextNode ('Watched video thumbnails modification'));
  390. const wwt = wwfs.appendChild (document.createElement ('table')).appendChild (document.createElement ('tr'));
  391. const wwc1 = wwt.appendChild (document.createElement ('td'));
  392. const wwgs = MakeSlider ('watched_grayscale', 2, 10, 10);
  393. AddLine (wwc1, MakeDesc ('Grayscale, %'), wwgs.base, wwgs.input);
  394. const wwb = MakeSlider ('watched_blur', 50, 1, 4);
  395. AddLine (wwc1, MakeDesc ('Blur, px'), wwb.base, wwb.input);
  396. AddLine (wwc1, MakeNote (0, 'Options require user to be logged into YT account'));
  397. AddLine (wwc1, MakeNote (0, 'Sample image taken from https://unsplash.com/photos/n6TWNDfyPwk'));
  398. const wwc2 = wwt.appendChild (document.createElement ('td'));
  399. wwc2.style.textAlign = 'center';
  400. wwc2.appendChild (document.createTextNode ('Example'));
  401. wwc2.appendChild (document.createElement ('br'));
  402. wwc2.appendChild (document.createElement ('img')).src = 'https://picsum.photos/id/197/267/178';
  403. const wwc3 = wwt.appendChild (document.createElement ('td'));
  404. wwc3.style.textAlign = 'center';
  405. wwc3.appendChild (document.createTextNode ('Modified example'));
  406. wwc3.appendChild (document.createElement ('br'));
  407. const wwc3i = wwc3.appendChild (document.createElement ('img'));
  408. wwc3i.src = 'https://picsum.photos/id/197/267/178';
  409. function UpdateFilters () {
  410. wwc3i.style.filter = `grayscale(${wwgs.value}%)blur(${wwb.value}px)`;
  411. }
  412. UpdateFilters ();
  413. wwgs.callback = UpdateFilters;
  414. wwb.callback = UpdateFilters;
  415. AddLine (tab_gen, wwfs);
  416. AddLine (tab_video, MakeDesc ('Starting video quality'), MakeListElement ('video_quality', ['Auto (default)', '2160p (4K)', '1440p (HD)', '1080p (HD)', '720p', '480p', '360p', '240p', '144p']));
  417. AddLine (tab_gen, MakeDesc ('View count display modifier'), MakeListElement ('view_count_mod', ['Short everywhere', 'Exact on video page, short otherwise (default)', 'Exact everywhere']));
  418. AddLine (tab_video, MakeBoolElement ("disable_player_click_overlay"), MakeDesc ('Remove rewinding overlay'));
  419. AddLine (tab_video, MakeDesc ("Video description width (including suggested videos column)"), MakeListElement ("description_width", ['default', 'stretch', '1200px', '1280px', '1360px', '1440px', '1520px', '1600px', '1680px', '1760px', '1840px', '1920px']));
  420. AddLine (tab_video, MakeBoolElement ("simpler_fullscreen"), MakeDesc ("Simplify fullscreen (no video description, comments, etc.)"));
  421. AddLine (tab_gen, MakeBoolElement ("clear_link_pp"), MakeDesc ('Remove &pp= from links'));
  422. AddLine (tab_video, MakeBoolElement ('exact_likes'), MakeDesc ('Show exact likes count'));
  423. AddLine (tab_gen, MakeBoolElement ('disable_video_preview'), MakeDesc ('Disable video on-hover previews'));
  424. AddLine (tab_video, MakeBoolElement ('hide_engagement_panel_transcript'), MakeDesc ("Disable 'Transcript' side panel"));
  425. AddLine (tab_video, MakeBoolElement ('hide_engagement_panel_structured_description'), MakeDesc ("Disable 'Desciption' side panel"));
  426. AddLine (tab_video, MakeBoolElement ('short_to_full'), MakeDesc ('Convert "shorts" video format to normal format'));
  427. AddLine (tab_video, MakeBoolElement ('hide_metadata'), MakeDesc ('Hide YT "metadata" (links to games, movies, etc.)'));
  428. AddLine (tab_script, MakeDesc ('Alert about script version changes'), MakeListElement ("update_alert", ['never', 'only major', 'except bugfixes', 'all']));
  429. AddLine (tab_script, MakeNote (0, "Version usually consist of 3 numbers (major version, minor version and release) and it's changes usually follow these rules:"));
  430. AddLine (tab_script, MakeNote (0, '* major version change indicates some incompatabilities with pervious versions;'));
  431. AddLine (tab_script, MakeNote (0, '* minor version change indicates some new functionality;'));
  432. AddLine (tab_script, MakeNote (0, '* release change indicate bugfixes or other internal improvements.'));
  433. e1 = MakeButton ('Save settings and return to YouTube', function () {
  434. settings.hide_guide = ess.hide_guide.checked;
  435. settings.fix_removed_placeholder = ess.fix_removed_placeholder.checked;
  436. settings.thumbnail_size = ess.thumbnail_size.selectedIndex;
  437. if (settings.thumbnail_size == 5) {
  438. const v = ess.thumbnail_size_m.value;
  439. if (!/^\d+$/.test (v)) {
  440. alert ('Error: invalid value for thumbnails size');
  441. return;
  442. }
  443. settings.thumbnail_size_m = parseInt (v);
  444. }
  445. settings.search_thumbnail = ess.search_thumbnail.selectedIndex;
  446. settings.default_player = ess.default_player.selectedIndex;
  447. settings.theater_player = ess.theater_player.selectedIndex;
  448. settings.hide_yt_suggested_blocks = ess.hide_yt_suggested_blocks.checked;
  449. settings.unfix_header = ess.unfix_header.checked;
  450. settings.align_player = ess.align_player.selectedIndex;
  451. settings.channel_top = ess.channel_top.selectedIndex;
  452. settings.logo_target = ess.logo_target.value;
  453. settings.clear_search = ess.clear_search.checked;
  454. settings.try_load_more = ess.try_load_more.checked;
  455. settings.unbound_video_title = ess.unbound_video_title.checked;
  456. settings.video_quality = ess.video_quality.selectedIndex;
  457. settings.no_resume_time = ess.no_resume_time.checked;
  458. settings.remove_yt_redirect = ess.remove_yt_redirect.checked;
  459. settings.view_count_mod = ess.view_count_mod.selectedIndex;
  460. settings.resume_bar_handling = ess.resume_bar_handling.selectedIndex;
  461. settings.watched_grayscale = ess.watched_grayscale.value;
  462. settings.watched_blur = ess.watched_blur.value;
  463. settings.disable_player_click_overlay = ess.disable_player_click_overlay.checked;
  464. settings.description_width = ess.description_width.selectedIndex;
  465. settings.simpler_fullscreen = ess.simpler_fullscreen.checked;
  466. settings.clear_link_pp = ess.clear_link_pp.checked;
  467. settings.exact_likes = ess.exact_likes.checked;
  468. settings.disable_video_preview = ess.disable_video_preview.checked;
  469. settings.hide_engagement_panel_transcript = ess.hide_engagement_panel_transcript.checked;
  470. settings.hide_engagement_panel_structured_description = ess.hide_engagement_panel_structured_description.checked;
  471. settings.short_to_full = ess.short_to_full.checked;
  472. settings.update_alert = ess.update_alert.selectedIndex;
  473. settings.hide_metadata = ess.hide_metadata.checked;
  474. saver ();
  475. alert ('Settings saved');
  476. history.back ();
  477. });
  478. e2 = MakeButton ('Return to YouTube without saving', function () {
  479. history.back ();
  480. });
  481. AddLine (plane, e1, e2);
  482. e1 = MakeButton ('Export settings', function () {
  483. const d = document.createElement ('a');
  484. d.style.display = 'none';
  485. d.setAttribute ('download', 'ytfix_settings.json');
  486. d.setAttribute ('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent (JSON.stringify (settings)));
  487. document.body.appendChild (d);
  488. d.click ();
  489. document.body.removeChild (d);
  490. });
  491. e2 = MakeButton ('Import settings', function () {
  492. const f = document.createElement ('input');
  493. f.type = 'file';
  494. f.style.display = 'none';
  495. f.addEventListener ('change', function () {
  496. if (f.files.length != 1)
  497. return;
  498. const rdr = new FileReader ();
  499. rdr.addEventListener ('load', function () {
  500. try {
  501. settings = JSON.parse (rdr.result);
  502. saver ();
  503. alert ('Settings imported');
  504. document.location.reload ();
  505. }
  506. catch (ex) {
  507. alert ('Error parsing settings\n' + ex);
  508. }
  509. });
  510. rdr.addEventListener ('error', () => alert ('Error loading file\n' + rdr.error));
  511. rdr.readAsText (f.files [0]);
  512. });
  513. document.body.appendChild (f);
  514. f.click ();
  515. document.body.removeChild (f);
  516. });
  517. AddLine (plane, e1, e2);
  518. }
  519. let int = setInterval (function () {
  520. if (!document.body)
  521. return;
  522. document.body.appendChild (back);
  523. document.body.appendChild (plane);
  524. clearInterval (int);
  525. }, 1);
  526. console.log ('Settings page created');
  527. return;
  528. }
  529. // apply settings
  530. function unwrap (x) {
  531. return x.wrappedJSObject || x;
  532. }
  533. let styles = '';
  534. if (settings.hide_guide) {
  535. let url = undefined;
  536. let act = 2;
  537. setInterval (function () {
  538. if (act == 0 && document.location.toString () != url) // обнаружение смены адреса
  539. act = 1;
  540. if (act == 1) { // wait for sorp page load completion
  541. const Q = document.getElementsByTagName ('yt-page-navigation-progress');
  542. if (!Q.length || !Q [0].hasAttribute ('hidden'))
  543. return;
  544. act = 2;
  545. }
  546. if (act == 2) { // wait for button and press it if necessary
  547. const guide_button = document.getElementById ('guide-button');
  548. if (!guide_button)
  549. return;
  550. let tmp = guide_button.getElementsByTagName ('button');
  551. if (!tmp.length)
  552. return;
  553. tmp = tmp [0];
  554. if (!tmp.hasAttribute ('aria-pressed'))
  555. return;
  556. if (tmp.attributes ['aria-pressed'].value == 'true')
  557. guide_button.click ();
  558. else {
  559. url = document.location.toString ();
  560. act = 0;
  561. window.dispatchEvent (new Event ('resize'));
  562. }
  563. }
  564. }, 1000);
  565. }
  566. if (settings.fix_removed_placeholder)
  567. styles += 'paper-button.style-blue-text,tp-yt-paper-button.style-blue-text{padding:0!important}';
  568. if (settings.thumbnail_size)
  569. styles += `
  570. div#contents.style-scope.ytd-rich-grid-renderer {display:block!important}
  571. ytd-rich-grid-row.style-scope.ytd-rich-grid-renderer {display:inline!important}
  572. ytd-rich-grid-row.style-scope.ytd-rich-grid-renderer > div {display:inline!important;margin:0!important}
  573. ytd-rich-item-renderer {display:inline-block!important;width:${[0, 180, 240, 360, 480, settings.thumbnail_size_m] [settings.thumbnail_size]}px!important;contain:none!important}
  574. `
  575. if (settings.hide_yt_suggested_blocks)
  576. styles += 'div#contents.ytd-rich-grid-renderer ytd-rich-section-renderer{display:none!important}';
  577. if (settings.unfix_header)
  578. styles += `
  579. div#masthead-container.ytd-app,ytd-mini-guide-renderer.ytd-app,app-drawer#guide{position:absolute!important}
  580. ytd-feed-filter-chip-bar-renderer{position:relative!important}
  581. div#chips-wrapper{position:absolute!important;top:0!important}
  582. `;
  583. if (settings.search_thumbnail) {
  584. const sz = [0, 240, 360] [settings.search_thumbnail] + 'px!important';
  585. // min-width defaults to 240px, max-width defaults to 360px
  586. // sizes for: videos, playlists, channels, mixes
  587. styles += `ytd-video-renderer[use-prominent-thumbs] ytd-thumbnail.ytd-video-renderer,ytd-playlist-renderer[use-prominent-thumbs] ytd-playlist-thumbnail.ytd-playlist-renderer,ytd-channel-renderer[use-prominent-thumbs] #avatar-section.ytd-channel-renderer,ytd-radio-renderer[use-prominent-thumbs] ytd-thumbnail.ytd-radio-renderer,ytd-video-renderer[is-search] ytd-thumbnail.ytd-video-renderer,ytd-playlist-renderer[is-search] ytd-playlist-thumbnail.ytd-playlist-renderer,ytd-channel-renderer[is-search] #avatar-section.ytd-channel-renderer,ytd-radio-renderer[is-search] ytd-thumbnail.ytd-radio-renderer{min-width:${sz};max-width:${sz}}`;
  588. }
  589. if (settings.clear_search)
  590. styles += 'ytd-two-column-search-results-renderer ytd-shelf-renderer.style-scope.ytd-item-section-renderer,ytd-two-column-search-results-renderer ytd-horizontal-card-list-renderer.style-scope.ytd-item-section-renderer{display:none!important}';
  591. styles += [
  592. '#player-theater-container{margin-left:auto!important;margin-right:auto!important}',
  593. '#player-container-outer{margin-left:0!important}',
  594. '#player-container-outer{margin-right:0!important}#player-theater-container{margin-left:auto!important}'
  595. ] [settings.align_player];
  596. const sizes = [0, 144, 240, 360, 480, 720];
  597. const size_norm = sizes [settings.default_player];
  598. if (size_norm)
  599. styles += `
  600. ytd-watch-flexy:not([fullscreen]):not([simple-fullscreen]):not([theater]){--ytd-watch-flexy-min-player-height:${size_norm}px!important;--ytd-watch-flexy-max-player-height:${size_norm}px!important;--ytd-watch-flexy-min-player-width:calc(${size_norm}px*var(--ytd-watch-flexy-width-ratio)/var(--ytd-watch-flexy-height-ratio))!important;--ytd-watch-flexy-max-player-width:var(--ytd-watch-flexy-min-player-width)!important}
  601. ytd-watch-flexy:not([fullscreen]):not([simple-fullscreen]):not([theater]) div#player-container-outer.ytd-watch-flexy{height:var(--ytd-watch-flexy-max-player-height);min-width:calc(${size_norm}px*var(--ytd-watch-flexy-width-ratio)/var(--ytd-watch-flexy-height-ratio))!important}
  602. `;
  603. const size_theater = sizes [settings.theater_player];
  604. if (size_theater)
  605. styles += `
  606. ytd-watch-flexy:not([fullscreen]):not([simple-fullscreen])[theater] #player-theater-container{min-width:calc(${size_theater}px*var(--ytd-watch-flexy-width-ratio)/var(--ytd-watch-flexy-height-ratio))!important;max-width:calc(${size_theater}px*var(--ytd-watch-flexy-width-ratio)/var(--ytd-watch-flexy-height-ratio))!important;min-height:${size_theater}px!important;max-height:${size_theater}px!important;height:${size_theater}px!important}
  607. ytd-watch-flexy:not([fullscreen]):not([simple-fullscreen])[theater] div#movie_player{height:${size_theater}px;width:calc(${size_theater}px*var(--ytd-watch-flexy-width-ratio)/var(--ytd-watch-flexy-height-ratio))}
  608. `;
  609. if (size_norm || size_theater)
  610. setInterval (function () {
  611. const eq = document.getElementsByTagName ("ytd-watch-flexy");
  612. if (!eq.length)
  613. return;
  614. const s = eq [0].hasAttribute ('theater') ? size_theater : size_norm;
  615. if (!s)
  616. return;
  617. const p = document.getElementById ("movie_player");
  618. if (!p)
  619. return;
  620. const ep = p.wrappedJSObject || p;
  621. if (ep.setInternalSize && ep.isFullscreen && ep.getPlayerSize && !ep.isFullscreen () && ep.getPlayerSize ().height != s)
  622. ep.setInternalSize ();
  623. }, 1000);
  624. if (settings.logo_target) {
  625. let url = settings.logo_target;
  626. if (url [0] != '/')
  627. url = '/' + url;
  628. url = document.location.origin + url;
  629. setInterval (function () {
  630. const l = document.querySelectorAll ('a#logo');
  631. for (let i = l.length; --i >= 0; ) {
  632. const E = l [i];
  633. const D = (E.wrappedJSObject || E).data;
  634. if (D && D.commandMetadata && E.href != url) {
  635. E.href = url;
  636. D.commandMetadata.webCommandMetadata.url = url;
  637. }
  638. }
  639. }, 1000);
  640. }
  641. if (settings.channel_top)
  642. styles += 'app-header#header.style-scope.ytd-c4-tabbed-header-renderer{transform:none!important;position:absolute;left:0px!important;top:0px;margin-top:0px}';
  643. if (settings.channel_top > 1)
  644. styles += `
  645. div#contentContainer.style-scope.app-header-layout{padding-top:148px!important}
  646. div#contentContainer.style-scope.app-header{height:148px!important}
  647. div.banner-visible-area.style-scope.ytd-c4-tabbed-header-renderer{display:none!important}
  648. `;
  649. if (settings.try_load_more) {
  650. setInterval (function () {
  651. const l = document.querySelectorAll ('#show-more-button');
  652. let i = l.length;
  653. if (--i >= 0 && l [i].hasAttribute ('hidden')) {
  654. l [i].removeAttribute ('hidden');
  655. l [i].innerText = 'TRY LOAD MORE';
  656. }
  657. while (--i >= 0)
  658. l [i].parentNode.removeChild (l [i]);
  659. }, 1000);
  660. styles += '#show-more-button{color:var(--yt-spec-call-to-action);width:100%;text-align:center;border:1px solid;padding:1em;cursor:pointer}';
  661. }
  662. if (settings.unbound_video_title)
  663. styles += '#video-title{max-height:none!important;-webkit-line-clamp:none!important}';
  664. if (settings.video_quality) {
  665. function TryQuality (quality, qq, ep) {
  666. return qq.includes (quality) && (ep.setPlaybackQualityRange (quality, quality) || true);
  667. }
  668. let fail = '';
  669. setInterval (function () {
  670. const p = document.getElementById ("movie_player");
  671. if (!p)
  672. return;
  673. const ep = p.wrappedJSObject || p;
  674. if (!ep.getPreferredQuality || !ep.getAvailableQualityLevels || !ep.setPlaybackQualityRange || !ep.getVideoData || ep.getPreferredQuality () != 'auto')
  675. return;
  676. const vid = ep.getVideoData ().video_id;
  677. if (fail == vid) // данное видео уже обработано
  678. return;
  679. const qq = ep.getAvailableQualityLevels ();
  680. if (!qq || !qq.length)
  681. return;
  682. switch (settings.video_quality) { // intentional no breaks here
  683. case 1: if (TryQuality ('hd2160', qq, ep)) return;
  684. case 2: if (TryQuality ('hd1440', qq, ep)) return;
  685. case 3: if (TryQuality ('hd1080', qq, ep)) return;
  686. case 4: if (TryQuality ('hd720', qq, ep)) return;
  687. case 5: if (TryQuality ('large', qq, ep)) return;
  688. case 6: if (TryQuality ('medium', qq, ep)) return;
  689. case 7: if (TryQuality ('small', qq, ep)) return;
  690. case 8: if (TryQuality ('tiny', qq, ep)) return;
  691. }
  692. console.log ('Unknown video qualities in list: ', qq);
  693. fail = vid;
  694. }, 1000);
  695. }
  696. if (settings.resume_bar_handling)
  697. styles += [
  698. '',
  699. 'div.ytd-thumbnail-overlay-resume-playback-renderer{width:100%!important}',
  700. 'ytd-thumbnail-overlay-resume-playback-renderer{display:none!important}'
  701. ] [settings.resume_bar_handling];
  702. let watched_filter = '';
  703. if (settings.watched_grayscale)
  704. watched_filter += `grayscale(${settings.watched_grayscale}%)`;
  705. if (settings.watched_blur)
  706. watched_filter += `blur(${settings.watched_blur}px)`;
  707. if (watched_filter)
  708. styles += `a[href*="/watch?"]:not([href^="/watch?"]).ytd-thumbnail img {filter:${watched_filter}}`;
  709. if (settings.no_resume_time || watched_filter) {
  710. function removeTimesClearer (l) {
  711. while (l && l.tagName != 'A')
  712. l = l.parentNode;
  713. if (!l)
  714. return;
  715. if (!settings.no_resume_time) {
  716. l.href = l.href.replace (/&t=\d+s?/, '$1');
  717. return;
  718. }
  719. l.href = l.href.replace (/&t=\d+s?/, '');
  720. l = unwrap (l);
  721. if (!l.data.watchEndpoint || !l.data.watchEndpoint.startTimeSeconds)
  722. return;
  723. delete l.data.watchEndpoint;
  724. try { l.data.commandMetadata.webCommandMetadata.url = l.href; } catch (ex) { }
  725. }
  726. setInterval (function () {
  727. document.querySelectorAll ('a[href^="/watch?"] div.ytd-thumbnail-overlay-resume-playback-renderer').forEach (removeTimesClearer); // основное применение
  728. document.querySelectorAll ('a[href^="/watch?"][href*="&t="]').forEach (removeTimesClearer); // на случай прочих ссылок
  729. }, 1000);
  730. }
  731. if (settings.remove_yt_redirect) {
  732. function removeRedirectClearer (l) {
  733. l.href = decodeURIComponent (l.href.replace (/^.*\?(.*&)q=([^&]+)(&.*)?$/, '$2'));
  734. const w = l.wrappedJSObject || l;
  735. if (w.data && w.data.urlEndpoint)
  736. w.data.urlEndpoint.url = l.href;
  737. }
  738. setInterval (function () {
  739. document.querySelectorAll ('a[href^="https://www.youtube.com/redirect?"]').forEach (removeRedirectClearer);
  740. }, 1000);
  741. }
  742. if (settings.view_count_mod === 2) {
  743. function getCounterText (x) { // x is not wrapper
  744. try { return x.__data.data.viewCountText.simpleText; } catch (ex) { }
  745. try { return x.__data.data.content.videoRenderer.viewCountText.simpleText; } catch (ex) { }
  746. }
  747. function replaceCountersText (x) {
  748. x = x.wrappedJSObject || x;
  749. const par = x.parentNode.__ytfix_parent;
  750. if (!par)
  751. return;
  752. const tgt = getCounterText (par);
  753. if (tgt && x.textContent != tgt)
  754. x.textContent = tgt;
  755. }
  756. function replaceCountersCallback (mm) {
  757. for (let i = mm.length; --i >= 0; ) {
  758. const m = mm [i];
  759. if (m.type == 'characterData')
  760. replaceCountersText (m.target);
  761. }
  762. }
  763. const m = new MutationObserver (replaceCountersCallback);
  764. const opt = { subtree: true, characterData: true };
  765. function replaceCountersEach (x) {
  766. x.setAttribute ('ytfix', '');
  767. const ee = x.querySelectorAll ('#metadata-line span');
  768. if (ee.length != 2)
  769. return;
  770. const e = ee [0];
  771. (e.wrappedJSObject || e).__ytfix_parent = x;
  772. replaceCountersText (e.firstChild);
  773. m.observe (e, opt);
  774. }
  775. setInterval (function () {
  776. document.querySelectorAll ('ytd-compact-video-renderer:not([ytfix])').forEach (replaceCountersEach);
  777. document.querySelectorAll ('ytd-grid-video-renderer:not([ytfix])').forEach (replaceCountersEach);
  778. document.querySelectorAll ('ytd-rich-item-renderer:not([ytfix])').forEach (replaceCountersEach);
  779. document.querySelectorAll ('ytd-video-renderer:not([ytfix])').forEach (replaceCountersEach);
  780. }, 1000);
  781. }
  782. if (settings.disable_player_click_overlay)
  783. styles += 'div.ytp-doubletap-ui,div.ytp-doubletap-ui-legacy {display:none}';
  784. if (settings.description_width) {
  785. const w = [0, '100vw', '1200px', '1280px', '1360px', '1440px', '1520px', '1600px', '1680px', '1760px', '1840px', '1920px'] [settings.description_width];
  786. styles += `
  787. ytd-app:not([mini-guide-visible_]) ytd-page-manager {--ytf-width:calc(${w} - 20px);min-width:100%}
  788. ytd-app[mini-guide-visible_] ytd-page-manager {--ytf-width:calc(${w} - 92px);min-width:calc(100% - 92px)}
  789. ytd-watch-flexy[flexy][fullscreen] {min-width:100%!important}
  790. ytd-watch-flexy[flexy] #columns {--ytd-watch-flexy-min-player-width:calc(var(--ytf-width) - var(--ytd-watch-flexy-sidebar-width) - 3 * var(--ytd-margin-6x));min-width:var(--ytf-width)!important;max-width:var(--ytf-width)!important}
  791. ytd-watch-flexy[flexy][is-two-columns_]:not([is-four-three-to-sixteen-nine-video_]):not([is-extra-wide-video_]) div#primary.ytd-watch-flexy {max-width:var(--ytd-watch-flexy-max-player-width);min-width:var(--ytd-watch-flexy-min-player-width)}
  792. ytd-watch-flexy[flexy][is-vertical-video_] div#player-container-inner.ytd-watch-flexy {width:calc(var(--ytd-watch-flexy-min-player-height)*1.7777777778);position:relative;transform:translate(-50%,0);margin-left:50%}
  793. `;
  794. }
  795. if (settings.simpler_fullscreen) {
  796. let keys = undefined;
  797. if (document.exitFullscreen)
  798. keys = document.fullscreenEnabled && { enter: 'requestFullscreen', exit: 'exitFullscreen', check: 'fullscreenElement', event: 'fullscreenchange', type: 'standart' };
  799. else if (document.mozCancelFullScreen)
  800. keys = document.mozFullScreenEnabled && { enter: 'mozRequestFullScreen', exit: 'mozCancelFullScreen', check: 'mozFullScreenElement', event: 'mozfullscreenchange', type: 'mozilla' };
  801. else if (document.webkitExitFullscreen)
  802. keys = document.webkitFullscreenEnabled && { enter: 'webkitRequestFullscreen', exit: 'webkitExitFullscreen', check: 'webkitFullscreenElement', event: 'webkitfullscreenchange', type: 'webkit' };
  803. else if (document.msExitFullscreen)
  804. keys = document.msFullscreenEnabled && { enter: 'msRequestFullscreen', exit: 'msExitFullscreen', check: 'msFullscreenElement', event: 'MSFullscreenChange', type: 'microsoft' };
  805. if (!keys)
  806. console.log ('unable to determine fullscreen API prefix or fullscreen API disabled');
  807. else {
  808. console.log ('detected fullscreen API type:', keys.type);
  809. styles += 'button.ytp-fullerscreen-edu-button{display:none!important}';
  810. document.addEventListener (keys.event, function () {
  811. const enter = !!document [keys.check];
  812. document.getElementById ("movie_player").setFauxFullscreen (enter);
  813. for (const q of document.querySelectorAll ('ytd-watch-flexy'))
  814. q [enter ? 'setAttribute' : 'removeAttribute'] ('simple-fullscreen', '');
  815. });
  816. setInterval (function () {
  817. for (const p of document.querySelectorAll ('button.ytp-fullscreen-button:not([ytfix])')) {
  818. const q = document.createElement ('button');
  819. q.className = p.className;
  820. p.parentNode.appendChild (q);
  821. q.appendChild (p.querySelector ('svg'));
  822. q.setAttribute ('title', 'Full screen');
  823. p.style.display = 'none';
  824. p.disabled = true;
  825. p.setAttribute ('ytfix', '');
  826. q.setAttribute ('ytfix', '');
  827. q.addEventListener ('mousedown', function (event) {
  828. event.preventDefault ();
  829. });
  830. q.addEventListener ('click', function (event) {
  831. if (document [keys.check])
  832. document [keys.exit] ();
  833. else {
  834. let w = event.target;
  835. while (w && w.tagName != "YTD-PLAYER")
  836. w = w.parentNode;
  837. if (w)
  838. w [keys.enter] ();
  839. }
  840. });
  841. }
  842. }, 1000);
  843. }
  844. }
  845. if (settings.clear_link_pp) {
  846. function clearLink (l) {
  847. l.setAttribute ('href', l.getAttribute ('href').replace (/&pp=[^&]*/, ''));
  848. }
  849. setInterval (function () {
  850. document.querySelectorAll ('a[href*="/watch?"][href*="&pp="]').forEach (clearLink);
  851. }, 1000);
  852. }
  853. if (settings.exact_likes) {
  854. function conv_exact (x) {
  855. if (x.length < 4)
  856. return x;
  857. if (x.length < 7)
  858. return x.slice (0, -3) + '\u00A0' + x.slice (-3);
  859. if (x.length < 10)
  860. return x.slice (0, -6) + '\u00A0' + x.slice (-6, -3) + '\u00A0' + x.slice (-3);
  861. return x.slice (0, -9) + '\u00A0' + x.slice (-9, -6) + '\u00A0' + x.slice (-6, -3) + '\u00A0' + x.slice (-3);
  862. }
  863. setInterval (function () {
  864. const q = document.querySelectorAll ('ytd-menu-renderer.ytd-video-primary-info-renderer') [0];
  865. if (!q)
  866. return;
  867. const e = q.querySelectorAll ('a.ytd-toggle-button-renderer > yt-formatted-string');
  868. if (!e.length)
  869. return;
  870. let idx = 0;
  871. //for (const d of unwrap (q).__data.topLevelButtonData) {
  872. for (const d of unwrap (q).__data.data.topLevelButtons) {
  873. const r = d.toggleButtonRenderer;
  874. if (r !== undefined && r.targetId === 'watch-like') {
  875. const likesTxt = conv_exact (r.defaultText.accessibility.accessibilityData.label.replace (/[^\d]/g, '') || '0');
  876. if (r.defaultText.textContent !== likesTxt) {
  877. r.defaultText.simpleText = likesTxt;
  878. e [idx].textContent = likesTxt;
  879. }
  880. return;
  881. }
  882. ++idx;
  883. }
  884. }, 1000);
  885. }
  886. if (settings.disable_video_preview)
  887. styles += `
  888. ytd-thumbnail-overlay-time-status-renderer { display: inline-flex !important; }
  889. ytd-video-preview, div#hover-overlays { display: none !important; }
  890. `;
  891. if (settings.hide_engagement_panel_transcript)
  892. styles += 'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-transcript"] { display: none !important; }';
  893. if (settings.hide_engagement_panel_structured_description) {
  894. styles += 'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-structured-description"] { display: none !important; }';
  895. const refs = [];
  896. setInterval (function () {
  897. document.querySelectorAll ('ytd-video-secondary-info-renderer tp-yt-paper-button#more').forEach (function (q) {
  898. for (const w of refs)
  899. if (w === q)
  900. return;
  901. refs.push (q);
  902. q.addEventListener ('click', function (ev) {
  903. let el = ev.target;
  904. while (el && el.tagName !== 'YTD-EXPANDER')
  905. el = el.parentNode;
  906. if (el)
  907. el.removeAttribute ('collapsed');
  908. });
  909. });
  910. }, 1000);
  911. }
  912. if (settings.short_to_full)
  913. setInterval (function () {
  914. const vid = location.pathname.substr (8);
  915. if (!location.pathname.startsWith ('/shorts/') || vid.length != 11)
  916. return;
  917. const url = '/watch?v=' + vid;
  918. const ev = new Event ('popstate');
  919. const state = { savedComponentState: null, entryTime: -1 };
  920. state.endpoint = { clickTrackingParams: 'hereAwasAsomethingAunreadableAAA' };
  921. state.endpoint.commandMetadata = { webCommandMetadata: { rootVe: 1, url: url, webPageType: 'WEB_PAGE_TYPE_WATCH' } };
  922. state.endpoint.watchEndpoint = { videoId: vid, watchEndpointSupportedOnesieConfig: { html5PlaybackOnesieConfig: { commonConfig: { /* url: something internal */ } } } };
  923. ev.state = state;
  924. history.replaceState (state, '', url);
  925. window.dispatchEvent (ev);
  926. }, 1000);
  927. if (settings.view_count_mod === 0)
  928. styles += `
  929. span.view-count.ytd-video-view-count-renderer {display:none!important}
  930. span.short-view-count.ytd-video-view-count-renderer {display:block!important}
  931. `;
  932. if (settings.hide_metadata)
  933. styles += 'ytd-metadata-row-container-renderer {display:none!important}';
  934. // update notifications
  935. const script_name = gminfo && gminfo.script && gminfo.script.name;
  936. if (fix_version && load_version && saver && settings.update_alert && script_name) {
  937. const m_fix = fix_version.match (/^(\d+)\.(\d+)\.(\d+)$/);
  938. const m_load = load_version.match (/^(\d+)\.(\d+)\.(\d+)$/);
  939. if (m_fix && m_load) {
  940. if (m_fix [1] != m_load [1]) {
  941. saver ();
  942. alert (`Script "${script_name}" updated major version.\nView settings page to check if all options were transferred correctly.\n\nTo disable this alert go to 'Script' tab on settings page.`);
  943. }
  944. else if (settings.update_alert >= 2 && m_fix [2] != m_load [2]) {
  945. saver ();
  946. alert (`Script "${script_name}" updated version.\nCheck settings page for new options.\n\nTo disable this alert go to 'Script' tab on settings page.`);
  947. }
  948. else if (settings.update_alert == 3 && m_fix [3] != m_load [3]) {
  949. saver ();
  950. alert (`Script "${script_name}" released bugfix.\n\nTo disable this alert go to 'Script' tab on settings page.`);
  951. }
  952. }
  953. }
  954. // "settings" button
  955. // can't store created button: Polymer overrides it's content on soft reload leaving tags in place
  956. // but can store element that Polymer does not know how to deal with and just drops
  957. let settingsMark = { parentNode: false };
  958. setInterval (function () {
  959. if (settingsMark.parentNode)
  960. return;
  961. let toolBar = document.getElementsByTagName ('ytd-topbar-menu-button-renderer');
  962. if (!toolBar.length)
  963. return;
  964. toolBar = toolBar [0];
  965. if (!toolBar)
  966. return;
  967. toolBar = toolBar.parentNode;
  968. const sb = document.createElement ('ytd-topbar-menu-button-renderer'); // ytd-notification-topbar-button-renderer
  969. sb.className = 'style-scope ytd-masthead style-default'; // style-scope ytd-masthead notification-button-style-type-default
  970. sb.setAttribute ('use-keyboard-focused', '');
  971. sb.setAttribute ('is-icon-button', '');
  972. sb.setAttribute ('has-no-text', '');
  973. const tbo = toolBar.wrappedJSObject || toolBar;
  974. const sbo = sb.wrappedJSObject || sb;
  975. tbo.insertBefore (sbo, tbo.childNodes [0]);
  976. // div[id=notification-count][class=style-scope ytd-notification-topbar-button-renderer][innerHTML=...]
  977. const mark = document.createElement ('fix-settings-mark');
  978. mark.style = 'display:none';
  979. toolBar.insertBefore (mark, sb); // must be added to parent node of buttons in order to Polymer dropped it on soft reload
  980. settingsMark = mark;
  981. const icb = document.createElement ('yt-icon-button');
  982. icb.id = 'button';
  983. icb.className = 'style-scope ytd-topbar-menu-button-renderer style-default';
  984. const tt = document.createElement ('tp-yt-paper-tooltip');
  985. tt.className = 'style-scope ytd-topbar-menu-button-renderer';
  986. tt.setAttribute ('role', 'tooltip');
  987. tt.setAttribute ('tabindex', '-1');
  988. tt.style = 'right:auto;bottom:auto';
  989. tt.appendChild (document.createTextNode ('YT fixes ' + fix_version)); // YT wraps content into DIV element
  990. const aa = document.createElement ('a');
  991. aa.className = 'yt-simple-endpoint style-scope ytd-topbar-menu-button-renderer';
  992. aa.setAttribute ('tabindex', '-1');
  993. aa.href = '/fix-settings';
  994. aa.appendChild (icb);
  995. aa.appendChild (tt);
  996. sbo.getElementsByTagName ('div') [0].appendChild (aa.wrappedJSObject || aa); // created by YT scripts
  997. const bb = icb.getElementsByTagName ('button') [0]; // created by YT scripts
  998. bb.setAttribute ('aria-label', 'fixes settings');
  999. const ic = document.createElement ('yt-icon');
  1000. ic.className = 'style-scope ytd-topbar-menu-button-renderer';
  1001. bb.appendChild (ic);
  1002. const gpath = document.createElementNS ('http://www.w3.org/2000/svg', 'path');
  1003. gpath.className.baseVal = 'style-scope yt-icon';
  1004. gpath.setAttribute ('d', 'M1 20l6-6h2l11-11v-1l2-1 1 1-1 2h-1l-11 11v2l-6 6h-1l-2-2zM2 20v1l1 1h1l5-5v-2h-2zM13 15l2-2 8 8v1l-1 1h-1zM15 14l-1 1 7 7h1v-1zM9 11l2-2-2-2 1.5-3-3-3h-2l3 3-1.5 3-3 1.5-3-3v2l3 3 3-1.5zM9 10l-2-2 1-1 2 2z');
  1005. const svgg = document.createElementNS ('http://www.w3.org/2000/svg', 'g');
  1006. svgg.className.baseVal = 'style-scope yt-icon';
  1007. svgg.appendChild (gpath);
  1008. const svg = document.createElementNS ('http://www.w3.org/2000/svg', 'svg');
  1009. svg.className.baseVal = 'style-scope yt-icon';
  1010. svg.setAttributeNS (null, 'viewBox', '0 0 24 24');
  1011. svg.setAttributeNS (null, 'preserveAspectRatio', 'xMidYMid meet');
  1012. svg.setAttribute ('focusable', 'false');
  1013. svg.setAttribute ('style', 'pointer-events: none; display: block; width: 100%; height: 100%;');
  1014. svg.appendChild (svgg);
  1015. ic.appendChild (svg); // YT clears *ic
  1016. }, 1000);
  1017. // styles
  1018. if (styles.length) {
  1019. let styles_int = setInterval (function () {
  1020. if (!document.head)
  1021. return;
  1022. clearInterval (styles_int);
  1023. if (document.getElementById ('ytfixstyle'))
  1024. return;
  1025. const style_element = document.createElement ('style');
  1026. style_element.setAttribute ('type', 'text/css');
  1027. style_element.setAttribute ('id', 'ytfixstyle');
  1028. style_element.innerHTML = styles;
  1029. document.head.appendChild (style_element);
  1030. }, 100);
  1031. }
  1032. console.log ('Fixes loaded');
  1033. }) ();