Kemono/Coomer-VideoPlayer

在Kemono或Coomer的post链接下添加video.js视频播放器。

  1. // ==UserScript==
  2. // @name Kemono/Coomer-VideoPlayer
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.3.5
  5. // @description 在Kemono或Coomer的post链接下添加video.js视频播放器。
  6. // @description:en-US Add video.js player under Kemono or Coomer's post links.
  7. // @author dsx137
  8. // @match https://coomer.party/*
  9. // @match https://kemono.party/*
  10. // @match https://coomer.su/*
  11. // @match https://kemono.su/*
  12. // @match https://nekohouse.su/*
  13. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  14. // @grant GM_addStyle
  15. // @grant GM_getResourceText
  16. // @resource video.js_css https://cdnjs.cloudflare.com/ajax/libs/video.js/8.16.1/video-js.css
  17. // @require https://cdnjs.cloudflare.com/ajax/libs/video.js/8.16.1/video.min.js
  18. // @license AGPLv3
  19. // ==/UserScript==
  20.  
  21. const suffixes = ['.mp4', '.m4v']
  22.  
  23. function isSupportedVideoLink(link) {
  24. try {
  25. const path = new URL(link).pathname.toLowerCase();
  26. return suffixes.some(suffix => path.endsWith(suffix));
  27. } catch (error) {
  28. return false;
  29. }
  30. }
  31.  
  32. function createPlayer(linkSrc) {
  33. let player = document.createElement("video");
  34.  
  35. player.className = "video-js vjs-theme-city";
  36. player.style.width = "100%";
  37. player.style.backgroundColor = "black"
  38. player.setAttribute("data-setup", '{"controls":true, "autoplay":false, "preload":"auto", "height":"800px"}');
  39. let src = document.createElement("source");
  40. src.type = "video/mp4";
  41. src.src = linkSrc;
  42. player.appendChild(src);
  43.  
  44. let volumePanelHoverCounter = 0;
  45.  
  46. player.addEventListener('keydown', event => {
  47. let step = 5;
  48. let volumePanel = player.parentElement.querySelector('.vjs-volume-panel');
  49.  
  50. let volumePanelAdjust = () => {
  51. volumePanelHoverCounter++;
  52. setTimeout(() => {
  53. volumePanelHoverCounter--;
  54. if (volumePanelHoverCounter <= 0) {
  55. if (!volumePanel.matches(':hover')) {
  56. volumePanel.classList.remove('vjs-hover');
  57. }
  58. }
  59. }, 1000);
  60. }
  61.  
  62. switch (event.code) {
  63. case 'ArrowLeft':
  64. player.currentTime = Math.max(0, player.currentTime - step);
  65. break;
  66. case 'ArrowRight':
  67. player.currentTime = Math.min(player.duration, player.currentTime + step);
  68. break;
  69. case 'Space':
  70. player.paused ? player.play() : player.pause();
  71. break;
  72. case 'ArrowUp':
  73. if (player.muted) {
  74. player.volume = 0
  75. player.muted = false;
  76. }
  77. player.volume = Math.min(1, player.volume + 0.1);
  78. volumePanel.classList.add('vjs-hover');
  79. volumePanelAdjust();
  80. break;
  81. case 'ArrowDown':
  82. if (player.muted) {
  83. break;
  84. }
  85. player.volume = Math.max(0, player.volume - 0.1);
  86. volumePanel.classList.add('vjs-hover');
  87. volumePanelAdjust();
  88. break;
  89. case 'KeyM':
  90. player.muted = !player.muted;
  91. break;
  92. case 'KeyF':
  93. if (document.fullscreenElement) {
  94. document.exitFullscreen();
  95. } else {
  96. player.requestFullscreen();
  97. }
  98. break;
  99. default:
  100. return;
  101. }
  102.  
  103. event.stopPropagation();
  104. event.preventDefault();
  105. });
  106.  
  107. return player
  108. }
  109.  
  110. function attachPlayer() {
  111. let links = Array.from(document.querySelectorAll(".post__attachment-link, .scrape__attachment-link")).filter(it => !it.classList.contains('has-player'));
  112. if (links.length == 0) return false;
  113. for (let i in links) {
  114. let linkSrc = links[i].href;
  115. if (!isSupportedVideoLink(linkSrc)) break;
  116. links[i].classList.add('has-player');
  117. let player = createPlayer(linkSrc)
  118. links[i].parentElement.appendChild(player);
  119. videojs(player)
  120.  
  121. let observer = new MutationObserver(mutations => {
  122. mutations.forEach(mutation => {
  123. if (!mutation.target) {
  124. return;
  125. }
  126.  
  127. let newLinkSrc = mutation.target.getAttribute("href");
  128. if (!isSupportedVideoLink(newLinkSrc)) {
  129. videojs(player).dispose();
  130. links[i].classList.remove('has-player');
  131. return;
  132. }
  133.  
  134. player.src = newLinkSrc;
  135. videojs(player)
  136. });
  137. });
  138. observer.observe(links[i], { attributes: true, attributeFilter: ['href'] });
  139. }
  140. return true;
  141. }
  142.  
  143. (function () {
  144. 'use strict';
  145. GM_addStyle(GM_getResourceText("video.js_css"));
  146.  
  147. attachPlayer();
  148.  
  149. let observer = new MutationObserver(mutations => {
  150. attachPlayer();
  151. });
  152. observer.observe(document.body, { childList: true, subtree: true })
  153. })();