Greasy Fork is available in English.

YouTube Rotate 90°

把Youtube影片旋轉0°、90°、180°、270°,讓你輕鬆觀看影片!

  1. // ==UserScript==
  2. // @name YouTube Rotate 90°
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.7.2
  5. // @description 把Youtube影片旋轉0°、90°、180°、270°,讓你輕鬆觀看影片!
  6. // @author zaqwsx2205
  7. // @match https://*.youtube.com/*
  8. // @match https://*.youtube.com/watch?v=*
  9. // @match https://www.youtube.com/embed/*
  10. // @match https://www.youtube-nocookie.com/embed/*
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. const policy = window.trustedTypes?.createPolicy('transform90-policy', {
  18. createHTML: input => input
  19. });
  20.  
  21. var click = 0;
  22. var safeURL = location.href;
  23. var svgicon =
  24. `<svg x="0px" y="0px" viewBox="0 0 248.78 250" width="50%" height="100%" >
  25. <g id="eca2fcdd-2bc2-42f1-9d5f-b6fc079f9fe7">
  26. <g id="ac89a795-d0d7-41f7-8442-954a17c4d3bc">
  27. <path d="M76.31,66.6,0,142.92l76.31,76.31,76.31-76.31ZM33.24,142.92l43-43,43.07,43-43,43Z" style="fill: #fff"/>
  28. <path d="M217.76,69.19a105.73,105.73,0,0,0-74.9-31V0l-50,50,50,49.9V61.72A82.37,82.37,0,1,1,109.5,219.34L92,236.88A105.87,105.87,0,0,0,217.76,69.19Z" style="fill: #fff"/>
  29. </g>
  30. </g>
  31. </svg>`;
  32.  
  33.  
  34.  
  35. buildHTML();
  36. observeURL();
  37.  
  38. function buildHTML() {
  39. document.querySelectorAll('.video-div, .video-div1, .video-div2, .transform90').forEach(function (element) {
  40. element.remove();
  41. });
  42.  
  43. let container = document.body;
  44. let div = document.createElement('div');
  45. div.classList.add('video-div2');
  46. container.appendChild(div);
  47. div.innerHTML = policy.createHTML(`<style>
  48. .ytp-autohide .transform90-top {
  49. opacity: 0 !important;
  50. -moz-transition: opacity .1s cubic-bezier(0.4,0.0,1,1) !important;
  51. -webkit-transition: opacity .1s cubic-bezier(0.4,0.0,1,1) !important;
  52. transition: opacity .1s cubic-bezier(0.4,0.0,1,1) !important;
  53. }
  54. .ytp-transform90-icon {
  55. margin: auto !important;
  56. width: 36px !important;
  57. height: 36px !important;
  58. position: relative !important;
  59. }
  60. .ytp-big-mode .ytp-transform90-icon {
  61. width: 54px !important;
  62. height: 54px !important;
  63. }
  64. .ytp-transform90-title {
  65. font-weight: 500 !important;
  66. text-align: center !important;
  67. font-size: 14px !important;
  68. }
  69. .ytp-big-mode .ytp-transform90-title {
  70. font-size: 20px !important;
  71. }
  72. .ytp-miniplayer-scrim .transform90-miniplayer{
  73. position: absolute;
  74. width: 60px;
  75. height: 40px;
  76. padding: 8px;
  77. z-index: 67;
  78. top: 0;
  79. left: 40px;
  80. }
  81. </style>`);
  82.  
  83. if (document.querySelectorAll('.ytp-embed').length > 0) {
  84. let chromeTopButtons = document.querySelector('.ytp-chrome-top-buttons');
  85. let button = document.createElement('button');
  86. button.classList.add('ytp-button', 'transform90', 'transform90-top');
  87. button.setAttribute('data-tooltip-opaque', 'true');
  88. button.setAttribute('aria-label', '');
  89. button.style.width = 'auto';
  90. button.style.height = 'auto';
  91.  
  92. let iconDiv = document.createElement('div');
  93. iconDiv.classList.add('ytp-transform90-icon');
  94. iconDiv.style.transform = 'scaleX(-1)';
  95. iconDiv.innerHTML = policy.createHTML(`${svgicon}`);
  96.  
  97. let titleDiv = document.createElement('div');
  98. titleDiv.classList.add('ytp-transform90-title');
  99. titleDiv.textContent = 'Video Rotate 90°';
  100.  
  101. button.appendChild(iconDiv);
  102. button.appendChild(titleDiv);
  103.  
  104. chromeTopButtons.prepend(button);
  105. } else if (document.querySelector('.ytd-miniplayer #player-container #ytd-player')) {
  106. let scrim = document.querySelector('.ytp-miniplayer-scrim');
  107. let button = document.createElement('button');
  108. button.classList.add('transform90', 'ytp-play-button', 'ytp-button', 'transform90-miniplayer');
  109. button.title = 'Video Rotate 90°';
  110. button.setAttribute('aria-label', 'Video Rotate 90°');
  111. button.style.display = 'inline-flex';
  112. button.style.justifyContent = 'center';
  113. button.style.transform = 'scaleX(-1)';
  114. button.innerHTML = policy.createHTML(`${svgicon}`);
  115.  
  116. scrim.prepend(button);
  117. } else {
  118. let chromeControls = document.querySelector('.ytp-chrome-controls .ytp-right-controls');
  119. if (chromeControls != null) {
  120. let button = document.createElement('button');
  121. button.classList.add('transform90', 'ytp-button');
  122. button.title = 'Video Rotate 90°';
  123. button.setAttribute('aria-label', 'Video Rotate 90°');
  124. button.style.display = 'inline-flex';
  125. button.style.justifyContent = 'center';
  126. button.style.transform = 'scaleX(-1)';
  127. button.innerHTML = policy.createHTML(`${svgicon}`);
  128.  
  129. chromeControls.prepend(button);
  130. }
  131. }
  132.  
  133. document.querySelectorAll('.transform90').forEach(function (button) {
  134. button.addEventListener('click', function () {
  135. click++;
  136. transform(click);
  137. });
  138. });
  139. }
  140.  
  141.  
  142. function transform(x) {
  143. document.querySelectorAll('.video-div, .video-div1').forEach(function (el) {
  144. el.remove();
  145. });
  146.  
  147. switch (x) {
  148. case 1:
  149. addVideoRotateStyles(90);
  150. transform90();
  151. break;
  152. case 2:
  153. addVideoRotateStyles(180);
  154. break;
  155. case 3:
  156. addVideoRotateStyles(270);
  157. transform90();
  158. break;
  159. case 4:
  160. click = 0;
  161. break;
  162. }
  163. }
  164.  
  165. function addVideoRotateStyles(degrees) {
  166. var container = document.querySelector('.html5-video-container');
  167. var videoDiv = document.createElement('div');
  168. var videoDiv1 = document.createElement('div');
  169.  
  170. videoDiv.className = 'video-div';
  171. videoDiv1.className = 'video-div1';
  172.  
  173. container.appendChild(videoDiv);
  174. container.appendChild(videoDiv1);
  175.  
  176. var styleContent = `
  177. .html5-video-container {
  178. display: flex !important;
  179. justify-content: center !important;
  180. align-items: center !important;
  181. height: 100% !important;
  182. }
  183. .video-stream {
  184. position: relative !important;
  185. transform: rotate(${degrees}deg) !important;
  186. height: auto !important;
  187. left: 0 !important;
  188. top: 0 !important;
  189. }
  190. `;
  191.  
  192. videoDiv.innerHTML = policy.createHTML(`<style>${styleContent}</style>`);
  193. }
  194.  
  195. function transform90() {
  196. setTimeout(function () {
  197. var container = document.querySelector('.html5-video-container');
  198. var width = container.offsetWidth;
  199. var height = container.offsetHeight;
  200. var wh = width - height;
  201. var videoDiv1 = document.querySelector('.video-div1');
  202.  
  203. if (videoDiv1) {
  204. videoDiv1.innerHTML = policy.createHTML('<style>.video-stream{width:calc(100% - ' + wh + 'px)!important;}</style>');
  205. }
  206. }, 20);
  207. }
  208.  
  209. function observeURL() {
  210. var composeBox = document.querySelector('#player-container video');
  211. var composeObserver = new MutationObserver(function (mutationsList) {
  212. if (safeURL !== location.href) {
  213. safeURL = location.href;
  214. click = 0;
  215. buildHTML();
  216. }
  217. });
  218.  
  219. if (!composeBox) {
  220. window.setTimeout(observeURL, 500);
  221. return;
  222. }
  223.  
  224. var config = { characterData: true, childList: true, attributes: true };
  225. composeObserver.observe(composeBox, config);
  226. }
  227.  
  228. window.addEventListener('resize', function () {
  229. transform(click);
  230. });
  231.  
  232. document.addEventListener("fullscreenchange", function (event) {
  233. transform(click);
  234. });
  235.  
  236. /*document.querySelectorAll(".ytp-size-button").forEach(function (element) {
  237. element.addEventListener('click', function () {
  238. transform(click);
  239. });
  240. });*/
  241.  
  242. })();