Resize YT To Window Size

Moves the YouTube video to the top of the website and fill the window with the video player.

当前为 2018-08-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Resize YT To Window Size
  3. // @description Moves the YouTube video to the top of the website and fill the window with the video player.
  4. // @author Chris H (Zren / Shade)
  5. // @icon https://s.ytimg.com/yts/img/favicon_32-vflOogEID.png
  6. // @homepageURL https://github.com/Zren/ResizeYoutubePlayerToWindowSize/
  7. // @namespace http://xshade.ca
  8. // @version 119
  9. // @include http*://*.youtube.com/*
  10. // @include http*://youtube.com/*
  11. // @include http*://*.youtu.be/*
  12. // @include http*://youtu.be/*
  13. // @grant none
  14. // ==/UserScript==
  15.  
  16. // Github: https://github.com/Zren/ResizeYoutubePlayerToWindowSize
  17. // GreasyFork: https://greasyfork.org/scripts/811-resize-yt-to-window-size
  18. // OpenUserJS.org: https://openuserjs.org/scripts/zren/Resize_YT_To_Window_Size
  19. // Userscripts.org: http://userscripts-mirror.org/scripts/show/153699
  20.  
  21. (function (window) {
  22. "use strict";
  23.  
  24. //--- Settings
  25. var playerHeight = '100vh';
  26.  
  27. //--- Imported Globals
  28. // yt
  29. // ytcenter
  30. // html5Patched (Youtube+)
  31. // ytplayer
  32. var uw = window;
  33.  
  34. //--- Already Loaded?
  35. // GreaseMonkey loads this script twice for some reason.
  36. if (uw.ytwp) return;
  37.  
  38. //--- Is iframe?
  39. function inIframe () {
  40. try {
  41. return window.self !== window.top;
  42. } catch (e) {
  43. return true;
  44. }
  45. }
  46. if (inIframe()) return;
  47.  
  48. //--- Utils
  49. function isStringType(obj) { return typeof obj === 'string'; }
  50. function isArrayType(obj) { return obj instanceof Array; }
  51. function isObjectType(obj) { return typeof obj === 'object'; }
  52. function isUndefined(obj) { return typeof obj === 'undefined'; }
  53. function buildVenderPropertyDict(propertyNames, value) {
  54. var d = {};
  55. for (var i in propertyNames)
  56. d[propertyNames[i]] = value;
  57. return d;
  58. }
  59. function observe(selector, config, callback) {
  60. var observer = new MutationObserver(function(mutations) {
  61. mutations.forEach(function(mutation){
  62. callback(mutation);
  63. });
  64. });
  65. var target = document.querySelector(selector);
  66. if (!target) {
  67. return null;
  68. }
  69. observer.observe(target, config);
  70. return observer;
  71. }
  72.  
  73. //--- Stylesheet
  74. var JSStyleSheet = function(id) {
  75. this.id = id;
  76. this.stylesheet = '';
  77. };
  78.  
  79. JSStyleSheet.prototype.buildRule = function(selector, styles) {
  80. var s = "";
  81. for (var key in styles) {
  82. s += "\t" + key + ": " + styles[key] + ";\n";
  83. }
  84. return selector + " {\n" + s + "}\n";
  85. };
  86.  
  87. JSStyleSheet.prototype.appendRule = function(selector, k, v) {
  88. if (isArrayType(selector))
  89. selector = selector.join(',\n');
  90. var newStyle;
  91. if (!isUndefined(k) && !isUndefined(v) && isStringType(k)) { // v can be any type (as we stringify it).
  92. var d = {};
  93. d[k] = v;
  94. newStyle = this.buildRule(selector, d);
  95. } else if (!isUndefined(k) && isUndefined(v) && isObjectType(k)) {
  96. newStyle = this.buildRule(selector, k);
  97. } else {
  98. // Invalid Arguments
  99. console.log('Illegal arguments', arguments);
  100. return;
  101. }
  102.  
  103. this.stylesheet += newStyle;
  104. };
  105.  
  106. JSStyleSheet.injectIntoHeader = function(injectedStyleId, stylesheet) {
  107. var styleElement = document.getElementById(injectedStyleId);
  108. if (!styleElement) {
  109. styleElement = document.createElement('style');
  110. styleElement.type = 'text/css';
  111. styleElement.id = injectedStyleId;
  112. document.getElementsByTagName('head')[0].appendChild(styleElement);
  113. }
  114. styleElement.appendChild(document.createTextNode(stylesheet));
  115. };
  116.  
  117. JSStyleSheet.prototype.injectIntoHeader = function(injectedStyleId, stylesheet) {
  118. JSStyleSheet.injectIntoHeader(this.id, this.stylesheet);
  119. };
  120.  
  121. //--- History
  122. var HistoryEvent = function() {}
  123. HistoryEvent.listeners = []
  124.  
  125. HistoryEvent.dispatch = function(state, title, url) {
  126. var stack = this.listeners
  127. for (var i = 0, l = stack.length; i < l; i++) {
  128. stack[i].call(this, state, title, url)
  129. }
  130. }
  131. HistoryEvent.onPushState = function(state, title, url) {
  132. HistoryEvent.dispatch(state, title, url)
  133. return HistoryEvent.origPushState.apply(window.history, arguments)
  134. }
  135. HistoryEvent.onReplaceState = function(state, title, url) {
  136. HistoryEvent.dispatch(state, title, url)
  137. return HistoryEvent.origReplaceState.apply(window.history, arguments)
  138. }
  139. HistoryEvent.inject = function() {
  140. if (!HistoryEvent.injected) {
  141. HistoryEvent.origPushState = window.history.pushState
  142. HistoryEvent.origReplaceState = window.history.replaceState
  143.  
  144. window.history.pushState = HistoryEvent.onPushState
  145. window.history.replaceState = HistoryEvent.onReplaceState
  146. HistoryEvent.injected = true
  147. }
  148. }
  149.  
  150. HistoryEvent.timerId = 0
  151. HistoryEvent.onTick = function() {
  152. var currentPage = window.location.pathname + window.location.search
  153. if (HistoryEvent.lastPage != currentPage) {
  154. HistoryEvent.dispatch({}, document.title, window.location.href)
  155. HistoryEvent.lastPage = currentPage
  156. }
  157. }
  158. HistoryEvent.startTimer = function() {
  159. HistoryEvent.lastPage = window.location.pathname + window.location.search
  160. HistoryEvent.timerId = setInterval(HistoryEvent.onTick, 500)
  161. }
  162. HistoryEvent.stopTimer = function() {
  163. clearInterval(HistoryEvent.timerId)
  164. }
  165. window.ytwpHistoryEvent = HistoryEvent
  166.  
  167.  
  168. //--- Constants
  169. var scriptShortName = 'ytwp'; // YT Window Player
  170. var scriptStyleId = scriptShortName + '-style'; // ytwp-style
  171. var scriptBodyClassId = scriptShortName + '-window-player'; // .ytwp-window-player
  172. var viewingVideoClassId = scriptShortName + '-viewing-video'; // .ytwp-viewing-video
  173. var topOfPageClassId = scriptShortName + '-scrolltop'; // .ytwp-scrolltop
  174. var scriptBodyClassSelector = 'body.' + scriptBodyClassId; // body.ytwp-window-player
  175.  
  176. var videoContainerId = 'player';
  177. var videoContainerPlacemarkerId = scriptShortName + '-placemarker'; // ytwp-placemarker
  178.  
  179. var transitionProperties = ["transition", "-ms-transition", "-moz-transition", "-webkit-transition", "-o-transition"];
  180. var transformProperties = ["transform", "-ms-transform", "-moz-transform", "-webkit-transform", "-o-transform"];
  181.  
  182. //--- YTWP
  183. var ytwp = uw.ytwp = {
  184. scriptShortName: scriptShortName, // YT Window Player
  185. log_: function(logger, args) { logger.apply(console, ['[' + this.scriptShortName + '] '].concat(Array.prototype.slice.call(args))); return 1; },
  186. log: function() { return this.log_(console.log, arguments); },
  187. error: function() { return this.log_(console.error, arguments); },
  188.  
  189. initialized: false,
  190. pageReady: false,
  191. isWatchPage: false,
  192. };
  193.  
  194. ytwp.isWatchUrl = function (url) {
  195. if (!url)
  196. url = uw.location.href;
  197. return url.match(/https?:\/\/(www\.)?youtube.com\/watch\?/)
  198. || url.match(/https?:\/\/(www\.)?youtube.com\/(c|channel)\/[^\/]+\/live/);
  199. };
  200.  
  201. ytwp.enterTheaterMode = function() {
  202. // ytwp.log('enterTheaterMode')
  203. var watchElement = document.querySelector('ytd-watch:not([hidden])') || document.querySelector('ytd-watch-flexy:not([hidden])')
  204. if (watchElement) {
  205. if (!watchElement.hasAttribute('theater')) {
  206. var sizeButton = watchElement.querySelector('button.ytp-size-button')
  207. if (sizeButton) {
  208. sizeButton.click()
  209. }
  210. }
  211. watchElement.canFitTheater_ = true // When it's too small, it disables the theater mode.
  212. } else if (watchElement = document.querySelector('#page.watch')) {
  213. if (!watchElement.classList.contains('watch-stage-mode')) {
  214. var sizeButton = watchElement.querySelector('button.ytp-size-button')
  215. if (sizeButton) {
  216. sizeButton.click()
  217. }
  218. }
  219. }
  220. }
  221. ytwp.enterTheaterMode();
  222. uw.addEventListener('resize', ytwp.enterTheaterMode);
  223.  
  224. ytwp.init = function() {
  225. ytwp.log('init');
  226. if (!ytwp.initialized) {
  227. ytwp.isWatchPage = ytwp.isWatchUrl();
  228. if (ytwp.isWatchPage) {
  229. ytwp.removeSearchAutofocus();
  230. if (!document.getElementById(scriptStyleId)) {
  231. ytwp.event.initStyle();
  232. }
  233. ytwp.initScroller();
  234. ytwp.initialized = true;
  235. ytwp.pageReady = false;
  236. }
  237. }
  238. ytwp.event.onWatchInit();
  239. if (ytwp.isWatchPage) {
  240. ytwp.html5PlayerFix();
  241. }
  242. }
  243.  
  244. ytwp.initScroller = function() {
  245. // Register listener & Call it now.
  246. uw.addEventListener('scroll', ytwp.onScroll, false);
  247. uw.addEventListener('resize', ytwp.onScroll, false);
  248. ytwp.onScroll();
  249. }
  250.  
  251. ytwp.onScroll = function() {
  252. var viewportHeight = document.documentElement.clientHeight;
  253.  
  254. // topOfPageClassId
  255. if (ytwp.isWatchPage && uw.scrollY == 0) {
  256. document.body.classList.add(topOfPageClassId);
  257. //var player = document.getElementById('movie_player');
  258. //if (player)
  259. // player.focus();
  260. } else {
  261. document.body.classList.remove(topOfPageClassId);
  262. }
  263.  
  264. // viewingVideoClassId
  265. if (ytwp.isWatchPage && uw.scrollY <= viewportHeight) {
  266. document.body.classList.add(viewingVideoClassId);
  267. } else {
  268. document.body.classList.remove(viewingVideoClassId);
  269. }
  270. }
  271.  
  272. ytwp.event = {
  273. initStyle: function() {
  274. ytwp.log('initStyle');
  275. ytwp.style = new JSStyleSheet(scriptStyleId);
  276. ytwp.event.buildStylesheet();
  277. // Duplicate stylesheet targeting data-spf-name if enabled.
  278. if (uw.spf) {
  279. var temp = scriptBodyClassSelector;
  280. scriptBodyClassSelector = 'body[data-spf-name="watch"]';
  281. ytwp.event.buildStylesheet();
  282. ytwp.style.appendRule('body[data-spf-name="watch"]:not(.ytwp-window-player) #masthead-positioner', {
  283. 'position': 'absolute',
  284. 'top': playerHeight + ' !important'
  285. });
  286. }
  287. ytwp.style.injectIntoHeader();
  288. },
  289. buildStylesheet: function() {
  290. ytwp.log('buildStylesheet');
  291. //--- Browser Scrollbar
  292. ytwp.style.appendRule(scriptBodyClassSelector + '::-webkit-scrollbar', {
  293. 'width': '0',
  294. 'height': '0',
  295. });
  296.  
  297. //--- Video Player
  298. var d;
  299. d = buildVenderPropertyDict(transitionProperties, 'left 0s linear, padding-left 0s linear');
  300. d['padding'] = '0 !important';
  301. d['margin'] = '0 !important';
  302. ytwp.style.appendRule([
  303. scriptBodyClassSelector + ' #player',
  304. scriptBodyClassSelector + '.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible #player',
  305. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #player',
  306. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #player-legacy',
  307. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #watch7-main-container',
  308. ], d);
  309. //
  310. d = buildVenderPropertyDict(transitionProperties, 'width 0s linear, left 0s linear');
  311.  
  312. // Bugfix for Firefox
  313. // Parts of the header (search box) are hidden under the player.
  314. // Firefox doesn't seem to be using the fixed header+guide yet.
  315. d['float'] = 'initial';
  316.  
  317. // Skinny mode
  318. d['left'] = 0;
  319. d['margin-left'] = 0;
  320.  
  321. ytwp.style.appendRule(scriptBodyClassSelector + ' #player-api', d);
  322.  
  323. // Theatre mode
  324. ytwp.style.appendRule(scriptBodyClassSelector + ' .watch-stage-mode #player .player-api', {
  325. 'left': 'initial !important',
  326. 'margin-left': 'initial !important',
  327. });
  328.  
  329. // Hide the cinema/wide mode button since it's useless.
  330. //ytwp.style.appendRule(scriptBodyClassSelector + ' #movie_player .ytp-size-button', 'display', 'none');
  331.  
  332. // !important is mainly for simplicity, but is needed to override the !important styling when the Guide is open due to:
  333. // .sidebar-collapsed #watch7-video, .sidebar-collapsed #watch7-main, .sidebar-collapsed .watch7-playlist { width: 945px!important; }
  334. // Also, Youtube Center resizes #player at element level.
  335. // Don't resize if Youtube+'s html.floater is detected.
  336. // Dont' resize if Youtube+ (Iridium/Material)'s html.iri-always-visible is detected.
  337. ytwp.style.appendRule(
  338. [
  339. scriptBodyClassSelector + ' #player',
  340. scriptBodyClassSelector + ' #player-api',
  341. 'html:not(.floater):not(.iri-always-visible) ' + scriptBodyClassSelector + ' #movie_player',
  342. scriptBodyClassSelector + ' #player-mole-container',
  343. 'html:not(.floater):not(.iri-always-visible) ' + scriptBodyClassSelector + ' .html5-video-container',
  344. 'html:not(.floater):not(.iri-always-visible) ' + scriptBodyClassSelector + ' .html5-main-video',
  345. scriptBodyClassSelector + ' ytd-watch-flexy[theater] #player-theater-container.ytd-watch-flexy',
  346. ],
  347. {
  348. 'width': '100% !important',
  349. 'min-width': '100% !important',
  350. 'max-width': '100% !important',
  351. 'height': playerHeight + ' !important',
  352. 'min-height': playerHeight + ' !important',
  353. 'max-height': playerHeight + ' !important',
  354. }
  355. );
  356.  
  357. ytwp.style.appendRule(
  358. [
  359. scriptBodyClassSelector + ' #player',
  360. scriptBodyClassSelector + ' .html5-main-video',
  361. ],
  362. {
  363. 'top': '0 !important',
  364. 'right': '0 !important',
  365. 'bottom': '0 !important',
  366. 'left': '0 !important',
  367. }
  368. );
  369. // Resize #player-unavailable, #player-api
  370. // Using min/max width/height will keep
  371. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-width', 'width', '100% !important');
  372. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height', 'height', '100% !important');
  373.  
  374. // Fix video overlays
  375. ytwp.style.appendRule([
  376. scriptBodyClassSelector + ' .html5-video-player .ad-container-single-media-element-annotations', // Ad
  377. scriptBodyClassSelector + ' .html5-video-player .ytp-upnext', // Autoplay Next Video
  378. ], 'top', '0');
  379.  
  380.  
  381. //--- Move Video Player
  382. ytwp.style.appendRule(scriptBodyClassSelector + ' #player', {
  383. 'position': 'absolute',
  384. // Already top:0; left: 0;
  385. });
  386. ytwp.style.appendRule(scriptBodyClassSelector, { // body
  387. 'margin-top': playerHeight,
  388. });
  389.  
  390. // Fix the top right avatar button
  391. ytwp.style.appendRule(scriptBodyClassSelector + ' button.ytp-button.ytp-cards-button', 'top', '0');
  392.  
  393.  
  394. //--- Sidebar
  395. // Remove the transition delay as you can see it moving on page load.
  396. d = buildVenderPropertyDict(transitionProperties, 'margin-top 0s linear, padding-top 0s linear');
  397. d['margin-top'] = '0 !important';
  398. d['top'] = '0 !important';
  399. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch7-sidebar', d);
  400.  
  401. ytwp.style.appendRule(scriptBodyClassSelector + '.cardified-page #watch7-sidebar-contents', 'padding-top', '0');
  402.  
  403. //--- Absolutely position the fixed header.
  404. // Masthead
  405. d = buildVenderPropertyDict(transitionProperties, 'top 0s linear !important');
  406. ytwp.style.appendRule(scriptBodyClassSelector + '.hide-header-transition #masthead-positioner', d);
  407. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #masthead-positioner', {
  408. 'position': 'absolute',
  409. 'top': playerHeight + ' !important'
  410. });
  411. // Lower masthead below Youtube+'s html.floater
  412. ytwp.style.appendRule('html.floater ' + scriptBodyClassSelector + '.' + viewingVideoClassId + ' #masthead-positioner', {
  413. 'z-index': '5',
  414. });
  415. // Autocomplete popup
  416. ytwp.style.appendRule(scriptBodyClassSelector + ' .sbdd_a', {
  417. 'top': '56px',
  418. });
  419. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' .sbdd_a', {
  420. 'top': 'calc(' + playerHeight + ' + 56px) !important',
  421. 'position': 'absolute !important',
  422. });
  423.  
  424. // Guide
  425. // When watching the video, we need to line it up with the masthead.
  426. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #appbar-guide-menu', {
  427. 'display': 'initial',
  428. 'position': 'absolute',
  429. 'top': '100% !important' // Masthead height
  430. });
  431. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #page.watch #guide', {
  432. 'display': 'initial',
  433. 'margin': '0',
  434. 'position': 'initial'
  435. });
  436.  
  437.  
  438. //---
  439. // MiniPlayer-Bar
  440. ytwp.style.appendRule(scriptBodyClassSelector + ' #miniplayer-bar #player', {
  441. 'position': 'static',
  442. });
  443. ytwp.style.appendRule(
  444. [
  445. scriptBodyClassSelector + ' #miniplayer-bar #player',
  446. scriptBodyClassSelector + ' #miniplayer-bar #player-api',
  447. 'html:not(.floater):not(.iri-always-visible) ' + scriptBodyClassSelector + ' #miniplayer-bar #movie_player',
  448. scriptBodyClassSelector + ' #player-mole-container',
  449. 'html:not(.floater):not(.iri-always-visible) ' + scriptBodyClassSelector + ' #miniplayer-bar .html5-video-container',
  450. 'html:not(.floater):not(.iri-always-visible) ' + scriptBodyClassSelector + ' #miniplayer-bar .html5-main-video',
  451. ],
  452. {
  453. 'width': '252px !important',
  454. 'min-width': '252px !important',
  455. 'max-width': '252px !important',
  456. 'height': '142px !important',
  457. 'min-height': '142px !important',
  458. 'max-height': '142px !important',
  459. }
  460. );
  461.  
  462. //---
  463. // Hide Scrollbars
  464. ytwp.style.appendRule(scriptBodyClassSelector + '.' + topOfPageClassId, 'overflow-x', 'hidden');
  465.  
  466.  
  467. //--- Fix Other Possible Style Issues
  468. ytwp.style.appendRule(scriptBodyClassSelector + ' #placeholder-player', 'display', 'none');
  469. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch-sidebar-spacer', 'display', 'none');
  470. ytwp.style.appendRule(scriptBodyClassSelector + ' .skip-nav', 'display', 'none');
  471.  
  472. //--- Whitespace Leftover From Moving The Video
  473. ytwp.style.appendRule(scriptBodyClassSelector + ' #page.watch', 'padding-top', '0');
  474. ytwp.style.appendRule(scriptBodyClassSelector + ' .player-branded-banner', 'height', '0');
  475.  
  476. //--- Youtube+ Compatiblity
  477. ytwp.style.appendRule(scriptBodyClassSelector + ' #body-container', 'position', 'static');
  478. ytwp.style.appendRule('.part_static_size:not(.content-snap-width-skinny-mode) ' + scriptBodyClassSelector + ' .watch-non-stage-mode #player-playlist', 'width', '1066px');
  479.  
  480. //--- Playlist Bar
  481. ytwp.style.appendRule([
  482. scriptBodyClassSelector + ' #placeholder-playlist',
  483. scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist',
  484. ], {
  485. 'height': '540px !important',
  486. 'max-height': '540px !important',
  487. });
  488.  
  489. d = buildVenderPropertyDict(transitionProperties, 'transform 0s linear');
  490. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch-appbar-playlist', d);
  491. d = buildVenderPropertyDict(transformProperties, 'translateY(0px)');
  492. d['margin-left'] = '0';
  493. d['top'] = 'calc(' + playerHeight + ' + 60px)';
  494. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist', d);
  495. ytwp.style.appendRule(scriptBodyClassSelector + ' .playlist-videos-list', {
  496. 'max-height': '470px !important',
  497. 'height': 'initial !important',
  498. });
  499.  
  500. // Old layout `&disable_polymer=true`
  501. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist', {
  502. 'left': 'calc((100vw - 1066px)/2 + 640px + 10px)',
  503. 'width': '416px',
  504. });
  505. ytwp.style.stylesheet += '@media screen and (min-height: 630px) and (min-width: 1294px) {\n';
  506. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist', {
  507. 'left': 'calc((100vw - 1280px)/2 + 854px + 10px)',
  508. });
  509. ytwp.style.stylesheet += '}\n @media screen and (min-width: 1720px) and (min-height:980px) {\n';
  510. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist', {
  511. 'left': 'calc((100vw - 1706px)/2 + 1280px + 10px)',
  512. });
  513. ytwp.style.stylesheet += '}\n';
  514.  
  515. //---
  516. // Material UI
  517. ytwp.style.appendRule(scriptBodyClassSelector + '.ytwp-scrolltop #extra-buttons', 'display', 'none !important');
  518. // ytwp.style.appendRule('body > #player:not(.ytd-watch)', 'display', 'none');
  519. // ytwp.style.appendRule('body.ytwp-viewing-video #content:not(app-header-layout) ytd-page-manager', 'margin-top', '0 !important');
  520. // ytwp.style.appendRule('.ytd-watch-0 #content-separator.ytd-watch', 'margin-top', '0');
  521. ytwp.style.appendRule('ytd-app', 'position', 'static !important');
  522. ytwp.style.appendRule('ytd-watch #top', 'margin-top', '71px !important'); // 56px (topnav height) + 15px (margin)
  523. ytwp.style.appendRule('ytd-watch #container', 'margin-top', '0 !important');
  524. ytwp.style.appendRule('ytd-watch #content-separator', 'margin-top', '0 !important');
  525. ytwp.style.appendRule(scriptBodyClassSelector + '.ytwp-viewing-video ytd-app #masthead-container.ytd-app', {
  526. 'position': 'absolute',
  527. 'top': playerHeight,
  528. 'z-index': 0,
  529. });
  530. ytwp.style.appendRule(scriptBodyClassSelector + '.ytwp-viewing-video ytd-watch #masthead-positioner', {
  531. 'top': playerHeight + ' !important',
  532. });
  533. ytwp.style.appendRule(scriptBodyClassSelector + ' .ytp-cued-thumbnail-overlay', 'z-index', '10');
  534.  
  535. //---
  536. // Flexy UI
  537. ytwp.style.appendRule(scriptBodyClassSelector + ' ytd-watch-flexy[theater] #player-theater-container.ytd-watch-flexy', {
  538. 'position': 'absolute',
  539. 'top': '0',
  540. });
  541. ytwp.style.appendRule(scriptBodyClassSelector + ' ytd-watch-flexy', 'padding-top', '71px'); // 56px (topnav height) + 15px (margin)
  542. },
  543. onWatchInit: function() {
  544. ytwp.log('onWatchInit');
  545. if (!ytwp.initialized) return;
  546. if (ytwp.pageReady) return;
  547.  
  548. ytwp.event.addBodyClass();
  549. ytwp.pageReady = true;
  550. },
  551. onDispose: function() {
  552. ytwp.log('onDispose');
  553. ytwp.initialized = false;
  554. ytwp.pageReady = false;
  555. ytwp.isWatchPage = false;
  556. },
  557. addBodyClass: function() {
  558. // Insert CSS Into the body so people can style around the effects of this script.
  559. document.body.classList.add(scriptBodyClassId);
  560. ytwp.log('Applied ' + scriptBodyClassSelector);
  561. },
  562. };
  563.  
  564. ytwp.html5PlayerFix = function() {
  565. ytwp.log('html5PlayerFix');
  566. return;
  567.  
  568. try {
  569. if (!uw.ytcenter // Youtube Center
  570. && !uw.html5Patched // Youtube+
  571. && (!ytwp.html5.app)
  572. && (uw.ytplayer && uw.ytplayer.config)
  573. && (uw.yt && uw.yt.player && uw.yt.player.Application && uw.yt.player.Application.create)
  574. ) {
  575. ytwp.html5.app = ytwp.html5.getPlayerInstance();
  576. }
  577.  
  578. ytwp.html5.update();
  579. ytwp.html5.autohideControls();
  580. } catch (e) {
  581. ytwp.error(e);
  582. }
  583. }
  584.  
  585. ytwp.fixMasthead = function() {
  586. ytwp.log('fixMasthead');
  587. var el = document.querySelector('#masthead-positioner-height-offset');
  588. if (el) {
  589. ytwp.fixMastheadElement(el);
  590. }
  591. }
  592. ytwp.fixMastheadElement = function(el) {
  593. ytwp.log('fixMastheadElement', el);
  594. if (el.style.height) { // != ""
  595. setTimeout(function(){
  596. el.style.height = ""
  597. document.querySelector('#appbar-guide-menu').style.marginTop = "";
  598. }, 0);
  599. }
  600. }
  601.  
  602. JSStyleSheet.injectIntoHeader(scriptStyleId + '-focusfix', 'input#search[autofocus] { display: none; }');
  603. ytwp.removeSearchAutofocus = function() {
  604. var e = document.querySelector('input#search');
  605. // ytwp.log('removeSearchAutofocus', e)
  606. if (e) {
  607. e.removeAttribute('autofocus')
  608. }
  609. }
  610.  
  611. ytwp.registerMastheadFix = function() {
  612. ytwp.log('registerMastheadFix');
  613. // Fix the offset when closing the Share widget (element.style.height = ~275px).
  614.  
  615. observe('#masthead-positioner-height-offset', {
  616. attributes: true,
  617. }, function(mutation) {
  618. console.log(mutation.type, mutation)
  619. if (mutation.attributeName === 'style') {
  620. var el = mutation.target;
  621. if (el.style.height) { // != ""
  622. setTimeout(function(){
  623. el.style.height = ""
  624. document.querySelector('#appbar-guide-menu').style.marginTop = "";
  625. }, 0);
  626. }
  627.  
  628. }
  629. });
  630. }
  631.  
  632. //--- Material UI
  633. ytwp.materialPageTransition = function() {
  634. ytwp.log('materialPageTransition')
  635. ytwp.init();
  636.  
  637. if (ytwp.isWatchUrl()) {
  638. ytwp.removeSearchAutofocus();
  639. ytwp.event.addBodyClass();
  640. // if (!ytwp.html5.app) {
  641. if (!ytwp.initialized) {
  642. ytwp.log('materialPageTransition !ytwp.html5.app', ytwp.html5.app)
  643. setTimeout(ytwp.materialPageTransition, 100);
  644. }
  645. var playerApi = document.querySelector('#player-api')
  646. if (playerApi) {
  647. playerApi.click()
  648. }
  649. } else {
  650. ytwp.event.onDispose();
  651. document.body.classList.remove(scriptBodyClassId);
  652. }
  653. ytwp.onScroll();
  654. ytwp.fixMasthead();
  655. ytwp.attemptToUpdatePlayer();
  656. };
  657.  
  658. //--- Listeners
  659. ytwp.registerListeners = function() {
  660. ytwp.registerMaterialListeners();
  661. ytwp.registerMastheadFix();
  662. };
  663.  
  664. ytwp.registerMaterialListeners = function() {
  665. // For Material UI
  666. HistoryEvent.listeners.push(ytwp.materialPageTransition);
  667. HistoryEvent.startTimer();
  668. // HistoryEvent.inject();
  669. // HistoryEvent.listeners.push(console.log.bind(console));
  670. };
  671.  
  672. ytwp.main = function() {
  673. ytwp.registerListeners();
  674. ytwp.init();
  675. ytwp.fixMasthead();
  676. };
  677.  
  678. ytwp.main();
  679.  
  680. // ytwp.updatePlayerTimerId = 0;
  681. ytwp.updatePlayerAttempts = -1;
  682. ytwp.updatePlayerMaxAttempts = 150; // 60fps = 2.5sec
  683. ytwp.attemptToUpdatePlayer = function() {
  684. console.log('ytwp.attemptToUpdatePlayer')
  685. if (0 <= ytwp.updatePlayerAttempts && ytwp.updatePlayerAttempts < ytwp.updatePlayerMaxAttempts) {
  686. ytwp.updatePlayerAttempts = 0;
  687. } else {
  688. ytwp.updatePlayerAttempts = 0;
  689. ytwp.attemptToUpdatePlayerTick();
  690. }
  691. // setTimeout(ytwp.updatePlayer, 10000); /// Just in case it's not caught
  692. }
  693. ytwp.attemptToUpdatePlayerTick = function() {
  694. console.log('ytwp.attemptToUpdatePlayerTick', ytwp.updatePlayerAttempts)
  695. if (ytwp.updatePlayerAttempts < ytwp.updatePlayerMaxAttempts) {
  696. ytwp.updatePlayerAttempts += 1;
  697. ytwp.updatePlayer();
  698. // ytwp.updatePlayerTimerId = setTimeout(ytwp.attemptToUpdatePlayerTick, 200);
  699. requestAnimationFrame(ytwp.attemptToUpdatePlayerTick);
  700. }
  701. }
  702.  
  703. ytwp.updatePlayer = function() {
  704. ytwp.removeSearchAutofocus();
  705. ytwp.enterTheaterMode();
  706. }
  707.  
  708. ytwp.materialPageTransition()
  709. setInterval(ytwp.updatePlayer, 2500);
  710.  
  711. })(window);