YouTube Player Controls

fill window, float video, HD resolution, skip ads, hide annotations, show more, repeat, pause start/end

質問やレビューの投稿はこちらへ、スクリプトの通報はこちらへお寄せください。
  1. // ==UserScript==
  2. // @name YouTube Player Controls
  3. // @namespace YouTubePlayerControls
  4. // @version 1.2.9
  5. // @license MIT
  6. // @description fill window, float video, HD resolution, skip ads, hide annotations, show more, repeat, pause start/end
  7. // @author Costas
  8. // @match http://www.youtube.com/*
  9. // @match https://www.youtube.com/*
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // @noframes
  13. // ==/UserScript==
  14.  
  15. //==================================================================
  16. //Userscript specific functions
  17.  
  18. var doc = document;
  19. var win = window;
  20.  
  21. if (win.frameElement) throw new Error("Stopped JavaScript.");
  22.  
  23. function set_pref(preference, new_value) {
  24. GM_setValue(preference, new_value);
  25. }
  26.  
  27. function get_pref(preference) {
  28. return GM_getValue(preference);
  29. }
  30.  
  31. function init_pref(preference, new_value) {
  32. var value = get_pref(preference);
  33. if (value == null) {
  34. set_pref(preference, new_value);
  35. value = new_value;
  36. }
  37. return value;
  38. }
  39.  
  40. //==================================================================
  41. // Styles
  42.  
  43. var style_annotations = "\
  44. .html5-video-player .annotation,\
  45. .html5-video-player .video-annotations,\
  46. .html5-video-player .ytp-cards-button,\
  47. .html5-video-player .ytp-cards-teaser,\
  48. .html5-video-player .iv-drawer,\
  49. .html5-video-player .ima-container, \
  50. .html5-video-player .ytp-ad-overlay-slot {display:none !important;}\
  51. ";
  52.  
  53. var style_extra_ads = "\
  54. #player-ads,\
  55. #offer-module,\
  56. ytd-compact-promoted-video-renderer,\
  57. ytd-promoted-sparkles-web-renderer {display:none !important;}\
  58. ";
  59.  
  60. var style_basic = "\
  61. /* messages */\
  62. .ytpc_message {font:12px/15px arial,sans-serif; text-align:left; white-space:pre; float:left; clear:both; color:black; background:beige; margin:10px 0px 0px 300px; z-index:2147483647;}\
  63. /* options */\
  64. #ytpc_options_popup {direction:ltr; position:absolute; top:5px; right:5px; width:235px; box-shadow:0px 0px 6px 2px gray; font-size:11px; color:black; background:white; padding:5px; border-radius:5px; z-index:10; user-select:none; -moz-user-select:none;}\
  65. html[dark] #ytpc_options_popup {color:#e0e0e0; background:#1b1b1b;}\
  66. #ytpc_options_popup input {margin:3px 2px -2px 5px !important;}\
  67. .ytpc_options_group.space {margin-bottom:3px; border:1px solid lightgray; border-radius:3px;}\
  68. html[dark] .ytpc_options_group.space {border:1px solid #606060 !important;}\
  69. .ytpc_options_group.space *[hide] {color:steelblue; margin-left:7px;}\
  70. .ytpc_options_group[hide] *[hide] {opacity:0.5;}\
  71. .ytpc_options_group.column > span:first-child {display:inline-block; min-width:120px;}\
  72. .ytpc_options_text {font-weight:500; font-size:12px; margin-left:5px; margin-top:7px;}\
  73. .ytpc_options_close {font-size:14px; color:#ff8888; position:absolute; top:0px; right:4px; cursor:pointer;}\
  74. .ytpc_options_close:hover {font-weight:bold; color:red;}\
  75. .ytpc_options_title {font-weight:500; font-size:14px; padding:3px 5px !important;}\
  76. /* button */\
  77. #ytpc_title_container {position:relative; z-index:1; float:right;}\
  78. body[dir='rtl'] #ytpc_title_container {float:left !important;}\
  79. #ytpc_ytcontrol_container {position:relative; float:right; padding:5px;}\
  80. #ytpc_ytcontrol_button {overflow:hidden; position:relative; float:right; width:24px; height:23px; margin:-4px -3px 0px 6px; cursor:pointer; opacity:0.7; background-size:100%; user-select:none; -moz-user-select:none; background-image:url('data:image/png;base64,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'); }\
  81. #ytpc_ytcontrol_button:hover {opacity:1;}\
  82. /* masthead */\
  83. #content[ytpc_cinema]:not([float]) #masthead-container[ytpc_hide] {display:none !important;}\
  84. #content[ytpc_cinema][ytpc_top]:not([float]) #page-manager {margin-top:0px !important;}\
  85. ";
  86.  
  87.  
  88. //==============================================================
  89. //basic
  90.  
  91. function newNode(kind, id, classname, refnode, position) {
  92.  
  93. var node = doc.createElement(kind);
  94.  
  95. if (node == null) return null;
  96.  
  97. if (id != null) node.id = id;
  98.  
  99. if (classname != null) node.className = classname;
  100.  
  101. if (refnode != null) {
  102. switch (position) {
  103. //insert after refnode
  104. case 'after':
  105. if (refnode.nextSibling != null)
  106. refnode.parentNode.insertBefore(node, refnode.nextSibling);
  107. else
  108. refnode.parentNode.appendChild(node);
  109. break;
  110.  
  111. //insert before refnode
  112. case 'before':
  113. refnode.parentNode.insertBefore(node, refnode);
  114. break;
  115.  
  116. //insert as first child of refnode
  117. case 'first':
  118. var child = refnode.childNodes[0];
  119. if (child != null)
  120. refnode.insertBefore(node, child);
  121. else
  122. refnode.appendChild(node);
  123. break;
  124.  
  125. //insert as last child of refnode
  126. case 'last':
  127. default:
  128. refnode.appendChild(node);
  129. break;
  130. }
  131. }
  132.  
  133. return node;
  134. }
  135.  
  136.  
  137. function message(str) {
  138. var node = newNode("div", null, "ytpc_message", doc.body);
  139. node.textContent = str + "\n";
  140. }
  141.  
  142.  
  143. function insertStyle(str, id) {
  144. var styleNode = null;
  145.  
  146. if (id != null) {
  147. styleNode = doc.getElementById(id);
  148. }
  149.  
  150. if (styleNode == null) {
  151. styleNode = newNode("style", id, null, doc.head);
  152. styleNode.setAttribute("type", "text/css");
  153. }
  154.  
  155. if (styleNode.textContent != str)
  156. styleNode.textContent = str;
  157. }
  158.  
  159.  
  160. function injectScript(str, src) {
  161. var script = doc.createElement("script");
  162. if (str) script.textContent = str;
  163. if (src) script.src = src;
  164. doc.body.appendChild(script);
  165. if (!src) doc.body.removeChild(script);
  166. }
  167.  
  168.  
  169. function simulClick(el) {
  170. var clickEvent = doc.createEvent('MouseEvents');
  171. clickEvent.initEvent('click', true, true);
  172. clickEvent.artificialevent = true;
  173. el.dispatchEvent(clickEvent);
  174. }
  175.  
  176.  
  177. function xpath(outer_dom, inner_dom, query) {
  178. //XPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7
  179. return outer_dom.evaluate(query, inner_dom, null, 7, null);
  180. }
  181.  
  182.  
  183. function docsearch(query) {
  184. return xpath(doc, doc, query);
  185. }
  186.  
  187.  
  188. function innersearch(inner, query) {
  189. return xpath(doc, inner, query);
  190. }
  191.  
  192.  
  193.  
  194. //==================================================================
  195. //YT Player
  196.  
  197. function ytplayer_script() {
  198. injectScript("function f() {\
  199. var a = document.getElementById('c4-player') || document.getElementById('movie_player');\
  200. var b = document.getElementById('ytpc_ytplayer_state');\
  201. if (a != null && b != null)\
  202. if (b.getAttribute('loop') == 'true') {\
  203. if (window.location.href.indexOf('list=') == -1) {\
  204. if (a.getPlayerState() == 0) {\
  205. a.playVideo();\
  206. }\
  207. }\
  208. else {\
  209. var d = a.getDuration();\
  210. if ((d - a.getCurrentTime() <= 1) && d > 0) {\
  211. a.playVideoAt(a.getPlaylistIndex());\
  212. }\
  213. }\
  214. }\
  215. else {\
  216. if (b.getAttribute('pause_end') == 'true' && b.getAttribute('pause_end_mark') != 'true') {\
  217. var d = a.getDuration();\
  218. if ((d - a.getCurrentTime() <= 1) && d > 0) {\
  219. a.pauseVideo();\
  220. b.setAttribute('pause_end_mark', 'true');\
  221. }\
  222. }\
  223. }\
  224. }\
  225. window.setInterval(f, 1000);\
  226. ");
  227. }
  228.  
  229.  
  230. function ytplayer_state(attr, value) {
  231. var node = doc.getElementById('ytpc_ytplayer_state');
  232. if (!node) {
  233. node = newNode("div", 'ytpc_ytplayer_state', null, doc.body);
  234. node.style.display = "none";
  235. }
  236.  
  237. if (!node) return null;
  238.  
  239. if (attr && value) node.setAttribute(attr, value);
  240. else if (attr) return node.getAttribute(attr);
  241. }
  242.  
  243.  
  244. function set_loop() { ytplayer_state('loop', 'true'); }
  245. function set_noloop() { ytplayer_state('loop', 'false'); }
  246. function set_pause_end() { ytplayer_state('pause_end', 'true'); }
  247. function set_nopause_end() { ytplayer_state('pause_end', 'false'); }
  248. function reset_pause_end_mark() { ytplayer_state('pause_end_mark', 'false'); }
  249. function success_pause() { return ytplayer_state('pause_status') == 'success'; }
  250. function success_quality() { return ytplayer_state('quality_status') == 'success'; }
  251.  
  252.  
  253. function adjust_pause_end() {
  254. var islive = docsearch("//yt-view-count-renderer/*[contains(.,'watching now')]").snapshotLength > 0;
  255. //message("Is live: " + islive);
  256. //alert("Is live: " + islive);
  257. get_pref("ytPauseEnd") && !islive ? set_pause_end() : set_nopause_end();
  258. reset_pause_end_mark();
  259. }
  260.  
  261.  
  262. function adjust_loop() {
  263. get_pref("ytLoop") ? set_loop() : set_noloop();
  264. }
  265.  
  266.  
  267. function ytplayer_pause() {
  268. ytplayer_state('pause_status', 'fail');
  269.  
  270. injectScript("var a = document.getElementById('c4-player') || document.getElementById('movie_player');\
  271. if (a != null)\
  272. if (a.pauseVideo != null){\
  273. a.pauseVideo();\
  274. var n = document.getElementById('ytpc_ytplayer_state');\
  275. if (n) n.setAttribute('pause_status', 'success');\
  276. }\
  277. ");
  278. }
  279.  
  280.  
  281. function ytplayer_quality(def) {
  282. ytplayer_state('quality_status', 'fail');
  283.  
  284. injectScript("var a = document.getElementById('c4-player') || document.getElementById('movie_player');\
  285. if (a != null)\
  286. if (a.setPlaybackQualityRange != null){\
  287. a.setPlaybackQualityRange('" + def + "');\
  288. var n = document.getElementById('ytpc_ytplayer_state');\
  289. if (n) n.setAttribute('quality_status', 'success');\
  290. }\
  291. ");
  292. }
  293.  
  294.  
  295. //==============================================================
  296. //preferences
  297.  
  298. init_pref("ytPause", false);
  299. init_pref("ytPauseEnd", false);
  300. init_pref("ytDef", "default");
  301. init_pref("ytLoop", false);
  302. init_pref("ytCine", false);
  303. init_pref("ytStretch", true);
  304. init_pref("ytHide", true);
  305. init_pref("ytAnnot", false);
  306. init_pref("ytAds", false);
  307. init_pref("ytAdsExtra", false);
  308. init_pref("ytFloat", false);
  309. init_pref("ytFSmall", true);
  310. init_pref("ytLoad", false);
  311.  
  312.  
  313. function close_ytplayer_options(e) {
  314. var popup = doc.getElementById("ytpc_options_popup");
  315. if (!popup) return;
  316.  
  317. if (!e) {
  318. popup.parentNode.removeChild(popup);
  319. return;
  320. }
  321.  
  322. if (e.artificialevent) return;
  323.  
  324. var p = e.target;
  325. for (var i = 0; i < 5; i++) {
  326. if (p) {
  327. if (p.id)
  328. if (p.id.search(/ytpc/) == 0) {
  329. e.stopPropagation();
  330. return;
  331. }
  332. if (p.className)
  333. if (p.className.search(/ytpc/) == 0) {
  334. e.stopPropagation();
  335. return;
  336. }
  337. }
  338. p = p.parentNode;
  339. }
  340.  
  341. popup.parentNode.removeChild(popup);
  342. }
  343.  
  344.  
  345. function new_checkbox(prefname, str, node_kind, parent, value, func, hide1, hide2) {
  346. var div = newNode(node_kind, null, "ytpc_generic", parent);
  347. var input = newNode("input", null, "ytpc_generic", div);
  348. input.type = "checkbox";
  349. if (!value) {
  350. input.checked = get_pref(prefname);
  351. if (hide1 && !input.checked) parent.setAttribute("hide", "true");
  352. input.onclick = function (e) {
  353. var val = get_pref(prefname);
  354. if (hide2 && parent.getAttribute("hide")) val = !val; //no change if hidden
  355. set_pref(prefname, !val);
  356. e.target.checked = !val;
  357. if (hide1)
  358. if (!val)
  359. parent.removeAttribute("hide");
  360. else
  361. parent.setAttribute("hide", "true");
  362. if (func) func();
  363. };
  364. }
  365. else {
  366. input.value = value;
  367. input.checked = (get_pref(prefname) == input.value);
  368. input.onclick = function (e) {
  369. var val = get_pref(prefname);
  370. set_pref(prefname, e.target.value);
  371. e.target.checked = true;
  372. var other = innersearch(parent.parentNode, ".//input[@value='" + val + "']").snapshotItem(0);
  373. if (other && (other != e.target)) other.checked = false;
  374. if (func) func();
  375. };
  376. }
  377. var span = newNode("span", null, "ytpc_generic", div);
  378. span.textContent = str;
  379. if (hide2) div.setAttribute("hide", "true");
  380. }
  381.  
  382.  
  383. function ytplayer_options() {
  384. var popup = doc.getElementById("ytpc_options_popup");
  385. if (popup) return;
  386.  
  387. var parent = doc.getElementById("ytpc_ytcontrol_container");
  388. if (!parent) return;
  389.  
  390. popup = newNode("span", "ytpc_options_popup", null, parent);
  391. //parent.parentNode.parentNode.style.overflow = "visible";
  392.  
  393. var title_node = newNode("div", null, "ytpc_options_title", popup);
  394. title_node.textContent = "YouTube Player Controls";
  395.  
  396. var closemark = newNode("span", null, "ytpc_options_close", popup);
  397. closemark.textContent = "\u2716";
  398. closemark.title = "close";
  399. closemark.onclick = function (e) { e.stopPropagation(); close_ytplayer_options(); }
  400.  
  401. var groupCine = newNode("div", null, "ytpc_options_group space", popup);
  402. new_checkbox("ytCine", "Fill Window", "span", groupCine, null, function () { resetTheaterMode(); win.location.reload(); }, true, false);
  403. new_checkbox("ytStretch", "Stretch", "span", groupCine, null, function () { cinema(0); }, false, true);
  404. new_checkbox("ytHide", "Hide Search", "span", groupCine, null, function () { cinema(0); }, false, true);
  405.  
  406. var groupFloat = newNode("div", null, "ytpc_options_group column", popup);
  407. new_checkbox("ytFloat", "Float Video", "span", groupFloat, null, function () { win.location.reload(); }, true, false);
  408. new_checkbox("ytFSmall", "Float Minimize", "span", groupFloat, null, null, false, true);
  409.  
  410. var groupAnnot = newNode("div", null, "ytpc_options_group column", popup);
  411. new_checkbox("ytAnnot", "Hide Annotations", "span", groupAnnot, null, annotation);
  412. new_checkbox("ytAdsExtra", "Hide Related Ads", "span", groupAnnot, null, extra_ads);
  413.  
  414. var groupClick = newNode("div", null, "ytpc_options_group column", popup);
  415. new_checkbox("ytLoad", "Click Show More", "span", groupClick, null, function() { auto_load(10); });
  416. new_checkbox("ytAds", "Click Skip Ads", "span", groupClick);
  417.  
  418. var groupPause = newNode("div", null, "ytpc_options_group column", popup);
  419. new_checkbox("ytPause", "Pause Start", "span", groupPause);
  420. new_checkbox("ytPauseEnd", "Pause End", "span", groupPause, null, adjust_pause_end);
  421.  
  422. var groupRepeat = newNode("div", null, "ytpc_options_group column", popup);
  423. new_checkbox("ytLoop", "Repeat (Loop)", "span", groupRepeat, null, adjust_loop);
  424.  
  425. var div = newNode("div", null, "ytpc_options_text", popup);
  426. //default, small, medium, large, hd720, hd1080, hd1440, highres;
  427. div.textContent = "Resolution";
  428. var groupDef1 = newNode("div", null, "ytpc_options_group", popup);
  429. var groupDef2 = newNode("div", null, "ytpc_options_group", popup);
  430. new_checkbox("ytDef", "Default", "span", groupDef1, "default");
  431. new_checkbox("ytDef", "LQ 240", "span", groupDef1, "small");
  432. new_checkbox("ytDef", "MQ 360", "span", groupDef1, "medium");
  433. new_checkbox("ytDef", "HQ 480", "span", groupDef1, "large");
  434. new_checkbox("ytDef", "HD 720", "span", groupDef2, "hd720");
  435. new_checkbox("ytDef", "HD 1080", "span", groupDef2, "hd1080");
  436. new_checkbox("ytDef", "HD 1440", "span", groupDef2, "hd1440");
  437. new_checkbox("ytDef", "MAX", "span", groupDef2, "highres");
  438. }
  439.  
  440.  
  441. function build_yt_control() {
  442.  
  443. var node = doc.getElementById("ytpc_ytcontrol_container");
  444.  
  445. if (!node) {
  446. var parent = doc.getElementById("ytpc_title_container");
  447. if (!parent) {
  448. var pp = doc.getElementsByClassName("watch-active-metadata");
  449. if (pp.length == 0) return;
  450. parent = newNode("span", "ytpc_title_container", null, pp[0], 'first');
  451. }
  452. if (!parent) return;
  453.  
  454. node = newNode("span", "ytpc_ytcontrol_container", null, parent);
  455. if (!node) return;
  456.  
  457. //control button
  458. var control = newNode("span", "ytpc_ytcontrol_button", null, node);
  459. //control.textContent = "Ctrl";
  460. control.title = "YouTube Player Controls";
  461. control.onclick = ytplayer_options;
  462. }
  463. }
  464.  
  465.  
  466. //==================================================================
  467. //Theater mode
  468.  
  469. function setTheaterMode() {
  470. if (docsearch("//ytd-page-manager/ytd-watch-flexy[@theater]").snapshotLength > 0) return; //already in theater mode
  471. var thnode = docsearch("//*[@class='ytp-chrome-controls']//*[contains(@class,'ytp-size-button')]").snapshotItem(0);
  472. if (thnode) simulClick(thnode);
  473. }
  474.  
  475. function resetTheaterMode() {
  476. if (docsearch("//ytd-page-manager/ytd-watch-flexy[@theater]").snapshotLength == 0) return; //already in default view
  477. var thnode = docsearch("//*[@class='ytp-chrome-controls']//*[contains(@class,'ytp-size-button')]").snapshotItem(0);
  478. if (thnode) simulClick(thnode);
  479. }
  480.  
  481. function showmast(movetop) {
  482. var mastoffset = doc.getElementById("masthead-container");
  483. if (mastoffset) {
  484. mastoffset.removeAttribute("ytpc_hide");
  485. if (!movetop)
  486. mastoffset.parentNode.removeAttribute("ytpc_top");
  487. }
  488. }
  489.  
  490. function hidemast(movetop) {
  491. var mastoffset = doc.getElementById("masthead-container");
  492. if (mastoffset) {
  493. mastoffset.setAttribute("ytpc_hide", "");
  494. if (movetop)
  495. mastoffset.parentNode.setAttribute("ytpc_top", "");
  496. }
  497. }
  498.  
  499. function cinema(start_count) {
  500. //not video page
  501. if (win.location.href.indexOf("watch?") == -1) {
  502. showmast(false);
  503. insertStyle("", "ytpc_style_cinemode");
  504. return;
  505. }
  506.  
  507. //video page
  508. if (!get_pref("ytCine")) return;
  509.  
  510. var page = docsearch("//ytd-page-manager/ytd-watch-flexy").snapshotItem(0);
  511. if (!page) return;
  512.  
  513. setTheaterMode();
  514.  
  515. var intheater = page.getAttribute("theater") != null;
  516. var fullscreen = page.getAttribute("fullscreen") != null;
  517.  
  518. if (intheater)
  519. page.parentNode.parentNode.setAttribute("ytpc_cinema", "");
  520. else
  521. page.parentNode.parentNode.removeAttribute("ytpc_cinema");
  522.  
  523. var hide = get_pref("ytHide") || fullscreen;
  524.  
  525. var H = doc.documentElement.clientHeight || doc.body.clientHeight;
  526. var W = doc.documentElement.clientWidth || doc.body.clientWidth;
  527.  
  528. var ratio = 16 / 9; //max width/height aspect ratio of visible area
  529. var pratio = ratio; //video aspect ratio
  530. var pl = docsearch("//ytd-watch-flexy//*[contains(@class,'html5-main-video')]").snapshotItem(0);
  531. if (pl) {
  532. var pwidth = Number(pl.style.width.replace(/[^\d\.\-]/g, '')); //video width
  533. var pheight = Number(pl.style.height.replace(/[^\d\.\-]/g, '')); //video height
  534. pratio = pwidth / pheight; //video aspect ratio
  535. if (pratio < ratio) ratio = pratio; //adopt video aspect ratio for narrower videos
  536. }
  537.  
  538. var height = H - (hide ? 0 : 56); //visible height, adjust for search bar
  539. var width = height * ratio; //visible width
  540. if (width > W) {
  541. width = W;
  542. height = width / ratio;
  543. }
  544.  
  545. if (hide && !fullscreen) { //hide or show search bar
  546. if (H >= height + 56) {
  547. showmast(false);
  548. }
  549. else {
  550. hidemast(true);
  551. if (doc.body.scrollTop || doc.documentElement.scrollTop)
  552. showmast(true);
  553. }
  554. }
  555. else
  556. showmast(false);
  557.  
  558. //check at most 6 times
  559. if (start_count > 5) return;
  560.  
  561. var left = Math.round((W - width) / 2);
  562. var top = Math.round((height - (W / pratio)) / 2);
  563. height = Math.round(height);
  564. width = Math.round(width);
  565.  
  566. var stretch = get_pref("ytStretch"); //horizontal stretch of videos
  567.  
  568. if (pratio > ratio && top >= 0) stretch = true; //enforce stretch for wide videos if visible area permits
  569.  
  570. if (stretch && (ratio > 1)) //do not stretch narrow videos
  571. insertStyle("\
  572. ytd-watch-flexy[theater]:not([float]) #player-theater-container {height:" + height + "px !important; max-height:" + height + "px !important; min-height:" + height + "px !important;}\
  573. ytd-watch-flexy[theater]:not([float]) .html5-main-video {width:100% !important; height:auto !important; left:0px !important; top:" + top + "px !important; margin-left:0px !important;}\
  574. ", "ytpc_style_cinemode");
  575. else
  576. insertStyle("\
  577. ytd-watch-flexy[theater]:not([float]) #player-theater-container {height:" + height + "px !important; max-height:" + height + "px !important; min-height:" + height + "px !important;}\
  578. ytd-watch-flexy[theater]:not([float]) .html5-main-video {width:" + width + "px !important; height:" + height + "px !important; left:" + left + "px !important; margin-left:0px !important;}\
  579. ", "ytpc_style_cinemode");
  580. }
  581.  
  582.  
  583. //==================================================================
  584. // Float
  585.  
  586. var floatheight = 0;
  587. var floatbot = 0;
  588.  
  589. function reset_float(page) {
  590. if (page.getAttribute("float") != null) win.setTimeout(function () { win.dispatchEvent(new Event('resize')) }, 100);
  591. page.removeAttribute("float");
  592. page.parentNode.parentNode.removeAttribute("float");
  593. insertStyle("", "ytpc_style_float");
  594. floatheight = 0;
  595. floatbot = 0;
  596. }
  597.  
  598. function float(start_count) {
  599. if (!get_pref("ytFloat")) return;
  600. var small = get_pref("ytFSmall");
  601. var cine = get_pref("ytCine");
  602.  
  603.  
  604. var page = docsearch("//ytd-page-manager/ytd-watch-flexy").snapshotItem(0);
  605. if (!page) return;
  606.  
  607. //in a new video page make sure that it starts without float
  608. if (start_count == 0) {
  609. reset_float(page);
  610. return;
  611. }
  612.  
  613. var intheater = page.getAttribute("theater") != null;
  614. var fullscreen = page.getAttribute("fullscreen") != null;
  615. var vid = intheater || fullscreen ? docsearch("//*[@id='player-theater-container']").snapshotItem(0)
  616. : docsearch("//*[@id='primary-inner']/*[@id='player']").snapshotItem(0);
  617. if (!vid) return;
  618.  
  619. var val = vid.getBoundingClientRect();
  620. var vwidth = val.right - val.left;
  621. var vheight = val.bottom - val.top;
  622. var vleft = val.left;
  623. var vright = val.right;
  624.  
  625. //player dimensions for fill window float
  626. var W = doc.body.clientWidth || doc.documentElement.clientWidth;
  627. var height = 240;
  628. var width = 427;
  629. var left = Math.round((W - width) / 2);
  630.  
  631. var infloat = page.getAttribute("float") != null;
  632. var inpltop = (docsearch("//*[@ytpc_top]").snapshotLength > 0);
  633.  
  634. //store initial values
  635. if (!infloat) {
  636. floatheight = vheight;
  637. floatbot = inpltop ? vheight - 56 : vheight;
  638. }
  639.  
  640. var scrollT = doc.body.scrollTop || doc.documentElement.scrollTop;
  641. var thres = -1;
  642.  
  643. if (intheater || fullscreen) {
  644. if (floatheight > 0)
  645. thres = inpltop || fullscreen ? floatheight - 296 : floatheight - 240;
  646. }
  647. else
  648. if (small) {
  649. if (floatheight > 0)
  650. thres = floatheight - 220;
  651. }
  652. else {
  653. if (vheight > 0)
  654. thres = 1;
  655. }
  656.  
  657. if (scrollT >= thres && thres > 0) {
  658. page.setAttribute("float", "");
  659. page.parentNode.parentNode.setAttribute("float", "");
  660.  
  661. if (intheater || fullscreen)
  662. insertStyle("\
  663. ytd-watch-flexy[float] #player-theater-container {position: fixed !important; top:56px !important; z-index:1000 !important;\
  664. height: " + height + "px !important; max-height:" + height + "px !important; min-height:" + height + "px !important;}\
  665. ytd-watch-flexy[float] .html5-main-video {width: " + width + "px !important; height: " + height + "px !important; left: " + left + "px !important; top:0px !important; margin-left:0px !important;}\
  666. ytd-watch-flexy[float] #columns {margin-top: " + floatbot + "px !important;}\
  667. ", "ytpc_style_float");
  668. else {
  669. var rtl = (doc.body.getAttribute('dir') == 'rtl');
  670. var lroff = rtl ? "right: " + (W - vright) + "px !important;" : "left: " + vleft + "px !important;";
  671. if (small) {
  672. insertStyle("\
  673. ytd-watch-flexy[float] #player-container {position: fixed !important; top:56px !important; " + lroff + " width: " + width + "px !important; height: " + height + "px !important; z-index:1000 !important;}\
  674. ytd-watch-flexy[float] .html5-main-video {width: " + width + "px !important; height: " + height + "px !important;}\
  675. ", "ytpc_style_float");
  676. }
  677. else {
  678. insertStyle("\
  679. ytd-watch-flexy[float] #player-container {position: fixed !important; top:80px !important; " + lroff + " width: " + vwidth + "px !important; height: " + vheight + "px !important; z-index:1000 !important;}\
  680. ", "ytpc_style_float");
  681. }
  682. }
  683. }
  684. else {
  685. reset_float(page);
  686. }
  687. }
  688.  
  689.  
  690. //=================================================================
  691. //ads & annotations
  692.  
  693. var adds_on = false;
  694. var fs_on = false;
  695.  
  696. function skip_ads(start_count) {
  697.  
  698. //adjust cinema after adds or fullscreen change by forcing resize event
  699. if (get_pref("ytCine")) {
  700. if (start_count == 0) adds_on = false;
  701. var adds = doc.getElementsByClassName("video-ads ytp-ad-module");
  702. if (adds.length > 0) {
  703. if (adds[0].style.display != "none") {
  704. adds_on = true;
  705. }
  706. }
  707. else {
  708. if (adds_on) { //adds turned off
  709. //win.dispatchEvent(new Event('resize'));
  710. win.setTimeout(function () { win.dispatchEvent(new Event('resize')) }, 100);
  711. }
  712. adds_on = false;
  713. }
  714.  
  715. var fs = (docsearch("//ytd-page-manager/ytd-watch-flexy[@fullscreen]").snapshotLength > 0);
  716. if (fs != fs_on) {
  717. //win.dispatchEvent(new Event('resize'));
  718. win.setTimeout(function () { win.dispatchEvent(new Event('resize')) }, 100);
  719. //alert("fullscreen");
  720. }
  721. fs_on = fs;
  722. }
  723.  
  724. //main skip ads
  725. if (!get_pref("ytAds")) return;
  726.  
  727. var button = doc.getElementsByClassName("ytp-ad-skip-button ytp-button");
  728. if (button.length > 0)
  729. if (button[0].parentNode)
  730. if (button[0].parentNode.style.display != "none") {
  731. //message("will click");
  732. simulClick(button[0]);
  733. }
  734. }
  735.  
  736. function extra_ads() {
  737. insertStyle(get_pref("ytAdsExtra") ? style_extra_ads : "", "ytpc_style_extra_ads");
  738. }
  739.  
  740. function annotation() {
  741. insertStyle(get_pref("ytAnnot") ? style_annotations : "", "ytpc_style_annotations");
  742. }
  743.  
  744.  
  745. //==================================================================
  746. //Load More
  747.  
  748. function auto_load(start_count) {
  749. if (win.location.href.indexOf("watch?") == -1) return;
  750.  
  751. var button = docsearch("//div[(@id='meta') or (@id='above-the-fold')]//tp-yt-paper-button[(@id='more') or (@id='expand')]").snapshotItem(0);
  752. if (!button) return;
  753.  
  754. if (start_count < 2) {
  755. button.removeAttribute("buttonclicked");
  756. return;
  757. }
  758.  
  759. if (!get_pref("ytLoad")) return;
  760. if (button.getAttribute("buttonclicked") == "true") return;
  761.  
  762. button.setAttribute("buttonclicked","true");
  763. simulClick(button);
  764. }
  765.  
  766.  
  767.  
  768. //==================================================================
  769. // Main
  770.  
  771. var old_addr = win.location.href;
  772. var nochanges_count = -1;
  773. var start_count = -1;
  774. //for yt_start
  775. var pause_count = 0;
  776. var def_count = 0;
  777. var pref_ytPause = false;
  778. var pref_ytDef = 'default';
  779. var has_focus = false;
  780.  
  781. ytplayer_script();
  782. insertStyle(style_basic, "ytpc_style_basic");
  783.  
  784. win.addEventListener("focus", function () { reset_nochanges(); }, false);
  785. win.addEventListener("blur", function () { reset_nochanges(); }, false);
  786. win.addEventListener("resize", function () { reset_nochanges(); cinema(0); float(1); }, false);
  787. win.addEventListener("scroll", function () { reset_nochanges(); cinema(0); float(1); }, false);
  788. win.addEventListener("click", function (e) { reset_nochanges(); close_ytplayer_options(e); }, false);
  789.  
  790. function reset_nochanges() {
  791. nochanges_count = -1;
  792. }
  793.  
  794. function yt_start() {
  795. if (start_count == 0) {
  796. pause_count = 0;
  797. def_count = 0;
  798. pref_ytPause = get_pref('ytPause');
  799. pref_ytDef = get_pref('ytDef');
  800. }
  801.  
  802. if (pref_ytPause && pause_count <= 2) {
  803. ytplayer_pause();
  804. if (success_pause()) pause_count++;
  805. }
  806.  
  807. if (pref_ytDef != 'default' && def_count <= 2) {
  808. ytplayer_quality(pref_ytDef);
  809. if (success_quality()) def_count++;
  810. }
  811. }
  812.  
  813.  
  814. //main routine
  815. function check_changes() {
  816. if (old_addr == win.location.href) {
  817. if (nochanges_count < 20) nochanges_count++;
  818. if (start_count < 20) start_count++;
  819. }
  820. else {
  821. old_addr = win.location.href;
  822. nochanges_count = 0;
  823. start_count = 0;
  824. }
  825.  
  826. if (!has_focus) {
  827. has_focus = doc.hasFocus();
  828. if (has_focus) {
  829. nochanges_count = 0;
  830. start_count = 0;
  831. }
  832. }
  833.  
  834. //no video page
  835. if (win.location.href.indexOf("watch?") == -1) {
  836. if (start_count < 20) {
  837. set_noloop();
  838. set_nopause_end();
  839. cinema(20); //for showmast
  840. }
  841. return;
  842. }
  843.  
  844. //video page
  845. skip_ads(start_count);
  846.  
  847. if (start_count < 20) {
  848. if (start_count == 0) {
  849. close_ytplayer_options();
  850. adjust_loop();
  851. adjust_pause_end();
  852. }
  853. build_yt_control();
  854. yt_start();
  855. annotation();
  856. extra_ads();
  857. auto_load(start_count);
  858. }
  859.  
  860. if (nochanges_count < 20) {
  861. cinema(start_count);
  862. float(start_count);
  863. }
  864. }
  865.  
  866. win.setInterval(check_changes, 1000);
  867. check_changes();