V LIVE Video Rotation

Adds buttons and keyboard shortcuts to rotate and flip the video.

目前为 2019-04-20 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name V LIVE Video Rotation
  3. // @description Adds buttons and keyboard shortcuts to rotate and flip the video.
  4. // @version 1.2
  5. // @author aqmx
  6. // @namespace aqmx
  7. // @license MIT
  8. // @match *://www.vlive.tv/video/*
  9. // @grant GM_addStyle
  10. // ==/UserScript==
  11.  
  12.  
  13. GM_addStyle(`
  14. .vlive_info {
  15. display: inline-block;
  16. box-sizing: border-box;
  17. width: 100%;
  18. }
  19. .vlive_info .tit {
  20. display: block;
  21. margin-bottom: 9px;
  22. }
  23. .vlive_info .status_area {
  24. display: inline-flex;
  25. align-items: center;
  26. }
  27. .vlive_info .ico_blue {
  28. margin-right: unset;
  29. }
  30. .vlive_info .txt, .vlive_info .like, .vlive_info .date {
  31. margin-top: 0;
  32. }
  33. .vlive_info .icon_play, .vlive_info .icon_like {
  34. margin-top: 1px;
  35. }
  36.  
  37. #video-rotation-controls {
  38. position: relative;
  39. display: inline-block;
  40. margin-left: 15px;
  41. z-index: 1;
  42. }
  43. #video-rotation-controls button {
  44. font-size: 20px;
  45. background: #f5f5f5;
  46. border: 1px solid #d9d9d9;
  47. color: #414141;
  48. margin-top: -4px;
  49. line-height: 22px;
  50. height: 24px;
  51. width: 24px;
  52. outline: 0;
  53. }
  54. `);
  55.  
  56.  
  57. let directions = {
  58. left: 'r270',
  59. up: '0',
  60. right: 'r90',
  61. down: 'r180',
  62. flipH: 'flipH',
  63. flipV: 'flipV',
  64. }
  65. let rotations = {
  66. r90: 'rotate(90deg)',
  67. r180: 'rotate(180deg)',
  68. r270: 'rotate(270deg)',
  69. };
  70. let scaling = {
  71. flipH: 'scaleX(-1)',
  72. flipV: 'scaleY(-1)',
  73. portrait: 'scale(1.759)',
  74. landscape: 'scale(0.559)',
  75. };
  76.  
  77. let shortcutsEnabled = true;
  78. let shortcuts = {
  79. 'Numpad8': directions.up,
  80. 'Numpad2': directions.down,
  81. 'Numpad4': directions.left,
  82. 'Numpad6': directions.right,
  83. 'Numpad9': directions.flipH,
  84. 'Numpad3': directions.flipV,
  85. }
  86. let stylesheet = null;
  87.  
  88.  
  89. (() => {
  90. stylesheet = document.createElement('style');
  91. document.head.appendChild(stylesheet);
  92.  
  93. let getAllSubsets = arr => arr.reduce((subsets, value) => subsets.concat(subsets.map(set => [...set, value])), [[]]).slice(1);
  94. let scalings = getAllSubsets(Object.keys(scaling));
  95.  
  96. for (let rule in rotations) {
  97. stylesheet.sheet.insertRule(`video.${rule} { transform: ${rotations[rule]}; }`, stylesheet.sheet.cssRules.length);
  98. for (let scalingSet of scalings) {
  99. stylesheet.sheet.insertRule(`video.${rule}.${scalingSet.reduce((s, v) => s+'.'+v)} { transform: ${rotations[rule] + scalingSet.reduce((s, v) => s+' '+scaling[v], '')}; }`, stylesheet.sheet.cssRules.length);
  100. }
  101. }
  102.  
  103. let flips = getAllSubsets([directions.flipH, directions.flipV]);
  104. for (let flipSet of flips) {
  105. stylesheet.sheet.insertRule(`video.${flipSet.reduce((s, v) => s+'.'+v)} { transform:${flipSet.reduce((s, v) => s+' '+scaling[v], '')}; }`, stylesheet.sheet.cssRules.length);
  106. }
  107. })();
  108.  
  109. function addButton() {
  110. let btnArea = document.querySelector('#content .vlive_section .vlive_info .btn_area');
  111. if (!btnArea) {
  112. setTimeout(addButton, 500);
  113. return;
  114. }
  115.  
  116. let div = document.createElement('div');
  117. div.id = 'video-rotation-controls';
  118. div.innerHTML = `
  119. <button data-direction="${directions.left}">🠘</button>
  120. <button data-direction="${directions.up}">🠙</button>
  121. <button data-direction="${directions.right}">🠚</button>
  122. <button data-direction="${directions.down}">🠛</button>
  123. <button data-direction="${directions.flipV}">🡙</button>
  124. <button data-direction="${directions.flipH}">🡘</button>`;
  125. btnArea.parentNode.insertBefore(div, btnArea);
  126.  
  127. document.querySelector('#video-rotation-controls').addEventListener('click', function(e) {
  128. if (e.target.tagName.toLowerCase() != 'button') return;
  129. let video = document.querySelector('.u_rmcplayer_video video') || document.querySelector('.vwplayer_vlivelive .videoBox video');
  130. if (!video) return;
  131. let flip = [directions.flipH, directions.flipV].includes(e.target.dataset.direction);
  132.  
  133. if (flip) {
  134. video.classList.toggle(e.target.dataset.direction);
  135. }
  136. else {
  137. video.classList.remove(directions.left, directions.right, directions.down, 'portrait', 'landscape');
  138. if (e.target.dataset.direction != directions.up) {
  139. video.classList.add(e.target.dataset.direction);
  140. if ([directions.left, directions.right].includes(e.target.dataset.direction)) {
  141. video.classList.add(video.videoHeight > video.videoWidth ? 'portrait' : 'landscape');
  142. }
  143. }
  144. }
  145. });
  146. }
  147.  
  148. addButton();
  149.  
  150.  
  151. // Shortcuts
  152. window.addEventListener('keydown', function(e) {
  153. if (shortcuts[e.code] && shortcutsEnabled) {
  154. let button = document.querySelector('#video-rotation-controls button[data-direction="'+shortcuts[e.code]+'"]');
  155. if (button) button.click();
  156. }
  157. else if (e.code == 'Pause') {
  158. shortcutsEnabled = !shortcutsEnabled;
  159. }
  160. });