A2P

Anime2Potplayer,用Potplayer打开浏览器播放的动漫,这样就可以使用SVP4补帧啦!

当前为 2025-05-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name A2P
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.5
  5. // @description Anime2Potplayer,用Potplayer打开浏览器播放的动漫,这样就可以使用SVP4补帧啦!
  6. // @author MakotoArai(https://github.com/MakotoArai-CN)
  7. // @supportURL https://blog.ciy.cool
  8. // @license GPL-v3
  9. // @icon https://cravatar.cn/avatar/1e84fce3269537e4aa7473602516bf6d?s=256
  10. // @match *anich.emmmm.eu.org/*
  11. // @match *.mutedm.com/*
  12. // @match *.iyinghua.com/*
  13. // @match *.5dm.link/*
  14. // @match *.dmd77.com/*
  15. // @grant GM_setValue
  16. // @grant GM_getValue
  17. // ==/UserScript==
  18.  
  19. 'use strict';
  20.  
  21. window.onload = function () {
  22. console.info("%cA2P%c%s", "color:red;font-size:40px;font-weight:bold;", "color:black;font-size:16px;font-weight:normal", "\n" + GM_info.script.version);
  23.  
  24. // 定时器用于动态嗅探视频链接
  25. const videoTimer = setInterval(findVideoUrl, 1000);
  26. // 域名包含 anich.emmmm.eu.org 则启用下面的逻辑
  27. if (window.location.href.includes("anich.emmmm.eu.org")) {
  28. // 定时器检测url是否改变,如果改变则重新调用findVideoUrl
  29. setInterval(function () {
  30. if (GM_getValue("url") !== window.location.href) {
  31. // 存入url方便对比
  32. GM_setValue("url", window.location.href);
  33. findVideoUrl();
  34. }
  35. }, 1500);
  36. }
  37.  
  38. function findVideoUrl() {
  39. const videoElement = document.querySelector("video");
  40. if (videoElement && videoElement.src) {
  41. if (videoElement.src.includes("blob:")) return;
  42. clearInterval(videoTimer);
  43. preparePotplayerInteraction(videoElement, GM_getValue("check") ?? false);
  44. }
  45. }
  46.  
  47. function preparePotplayerInteraction(videoElement, check = true) {
  48. const videoUrl = videoElement.src;
  49. console.log(`检测到视频链接: ${videoUrl}`);
  50.  
  51. creatBtn(videoElement);
  52. if (check) {
  53. window.location.href = `potplayer://${videoUrl}`;
  54. // 检测是否播放,如果正在播放则暂停网页的播放
  55. var pause_Flag = 0;
  56. const checkTimer = setInterval(() => {
  57. const isPlaying = !videoElement.paused && !videoElement.ended && videoElement.readyState > 2;
  58. // console.log(isPlaying ? "正在播放" : "已暂停或结束");
  59. if (isPlaying) videoElement.pause();
  60. if (!isPlaying || pause_Flag > 100) clearInterval(checkTimer);
  61. pause_Flag++;
  62. }, 1500);
  63. };
  64.  
  65. }
  66.  
  67. function creatBtn(videoElement) {
  68. // 插入自定义CDN
  69. document.head.insertAdjacentHTML("beforeend", `
  70. <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
  71. `);
  72. // 右键菜单
  73. var menu = document.createElement("div");
  74. // 插入自定义css
  75. document.head.insertAdjacentHTML("beforeend", `
  76. <style>
  77. a {text-decoration: none;}
  78. div.usercm{background-repeat:no-repeat;background-position:center center;background-size:cover;background-color:#fff;font-size:13px!important;width:160px;-moz-box-shadow:1px 1px 3px rgba
  79. (0,0,0,.3);box-shadow:0px 0px 15px #333;position:absolute;display:none;z-index:10000;opacity:0.9; border-radius: 8px;}
  80. div.usercm ul{list-style-type:none;list-style-position:outside;margin:0px;padding:0px;display:block}
  81. div.usercm ul li{margin:0px;padding:0px;line-height:35px;}
  82. div.usercm ul li a{color:#666;padding:0 15px;display:block}
  83. /* div.usercm ul li a:hover{color:#fff;background:rgba(170,222,18,0.88)} */
  84. div.usercm ul li a:hover{color:#fff;background:rgba(15, 120, 233, 0.88)} /* 蓝色 */
  85. div.usercm ul li a i{margin-right:10px}
  86. a.disabled{color:#c8c8c8!important;cursor:not-allowed}
  87. a.disabled:hover{background-color:rgba(255,11,11,0)!important}
  88. div.usercm{background:#fff !important;}
  89. </style>
  90. )`);
  91.  
  92. /* 右键菜单 */
  93. menu.innerHTML = `
  94. <div class="usercm" style="left: 199px; top: 5px; display: none;">
  95. <ul>
  96. <li><a href="/"><i class="fa fa-home fa-fw"></i><span>首页</span></a></li>
  97. <li><a href="javascript:void(0);" onclick="'' == (window.getSelection ? window.getSelection() : document.selection.createRange().text) ? console.log() : document.execCommand('Copy')"><i class="fa fa-copy fa-fw"></i><span>复制</span></a></li>
  98. <li><a href="javascript:history.go(1);"><i class="fa fa-arrow-right fa-fw"></i><span>前进</span></a></li>
  99. <li><a href="javascript:history.go(-1);"><i class="fa fa-arrow-left fa-fw"></i><span>后退</span></a></li>
  100. <li style="border-bottom:1px solid gray"><a href="javascript:window.location.reload();"><i class="fa fa-refresh fa-fw"></i><span>重载网页</span></a></li>
  101. <li><a href="javascript:void(0);" class="potplayer"><i class="fa fa-arrow-right fa-fw"></i><span>Potplayer(X)</span></a></li>
  102. <li><a href="javascript:void(0);" class="aa2p"><i class="fa fa-arrow-right fa-fw"></i><span>自动跳转</span></a></li>
  103. <li><a href="https://blog.ciy.cool"><i class="fa fa-pencil-square-o fa-fw"></i><span>关于我</span></a></li>
  104. </ul>
  105. </div>
  106. `;
  107. document.body.appendChild(menu);
  108. // 自定义鼠标右键
  109. // 自定义鼠标右键菜单行为
  110. (function () {
  111. let mouseX = 0;
  112. let mouseY = 0;
  113. let windowWidth = 0;
  114. let windowHeight = 0;
  115.  
  116. // 获取元素
  117. const menu = document.querySelector('.usercm');
  118.  
  119. // 鼠标移动事件
  120. window.addEventListener('mousemove', function (e) {
  121. windowWidth = window.innerWidth;
  122. windowHeight = window.innerHeight;
  123. mouseX = e.clientX;
  124. mouseY = e.clientY;
  125.  
  126. // 设置菜单位置
  127. let left = e.pageX;
  128. let top = e.pageY;
  129.  
  130. if (mouseX + menu.offsetWidth >= windowWidth) {
  131. left = left - menu.offsetWidth - 5;
  132. }
  133. if (mouseY + menu.offsetHeight >= windowHeight) {
  134. top = top - menu.offsetHeight - 5;
  135. }
  136.  
  137. // 绑定右键点击事件
  138. document.documentElement.addEventListener('contextmenu', function (event) {
  139. if (event.button === 2) { // 右键点击
  140. event.preventDefault();
  141. menu.style.left = `${left}px`;
  142. menu.style.top = `${top}px`;
  143. menu.style.display = 'block';
  144. }
  145. });
  146.  
  147. // 点击隐藏菜单
  148. document.documentElement.addEventListener('click', function () {
  149. menu.style.display = 'none';
  150. });
  151. });
  152.  
  153. // 禁用默认右键菜单
  154. window.oncontextmenu = function (e) {
  155. e.preventDefault();
  156. return false;
  157. };
  158.  
  159. // 判断是否是移动端
  160. const userAgent = navigator.userAgent;
  161. const mobileKeywords = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod'];
  162. let isMobile = false;
  163.  
  164. for (let keyword of mobileKeywords) {
  165. if (userAgent.indexOf(keyword) > -1) {
  166. isMobile = true;
  167. break;
  168. }
  169. }
  170.  
  171. // 非移动端才启用自定义右键菜单
  172. if (!isMobile) {
  173. // 上面已经实现了 mouseMoveShow 和 disabledContextMenu
  174. // console.log('已启用自定义右键菜单');
  175. }
  176. })();
  177. const potplayer = document.querySelector(".potplayer");
  178. const aa2p = document.querySelector(".aa2p");
  179. const videoUrl = videoElement.src;
  180. potplayer.addEventListener("click", function () {
  181. window.location.href = `potplayer://${videoUrl}`;
  182. // 暂停播放
  183. videoElement.pause();
  184. })
  185.  
  186. // Alt + X 按键跳转
  187. document.onkeydown = function (e) {
  188. const keyNum = window.event ? e.keyCode : e.which;
  189. if (e.altKey && Number.isInteger(keyNum)) {
  190. switch (keyNum) {
  191. case 88:
  192. window.location.href = `potplayer://${videoUrl}`;
  193. videoElement.pause();
  194. break;
  195. }
  196. }
  197. };
  198.  
  199. aa2p.innerHTML = `<i class="fa fa-arrow-right fa-fw"></i><span>${GM_getValue("check") ? "关闭自动跳转" : "开启自动跳转"}</span>`;
  200. aa2p.addEventListener("click", function () {
  201. const check = GM_getValue("check") ?? false;
  202. if (check) {
  203. GM_setValue("check", false);
  204. aa2p.innerHTML = `<i class="fa fa-arrow-right fa-fw"></i><span>开启自动跳转</span>`;
  205. } else {
  206. GM_setValue("check", true);
  207. aa2p.innerHTML = `<i class="fa fa-arrow-right fa-fw"></i><span>关闭自动跳转</span>`;
  208. }
  209. })
  210. }
  211. }