HTML5 Player Simulation

English:"Add HTML5 player support in restricted browsers.";中文:“在受限制的浏览器中,添加html5播放器支持”

  1. // ==UserScript==
  2. // @name HTML5 Player Simulation
  3. // @namespace xiaoxingxingboer31@outlook.com
  4. // @version a.1
  5. // @description English:"Add HTML5 player support in restricted browsers.";中文:“在受限制的浏览器中,添加html5播放器支持”
  6. // @author xiaoxingxingboer31
  7. // @match *://*
  8. // @grant none
  9. // @run-at document-start
  10. // @license Proprietary
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. // 引入解码器库
  17. const decoderScripts = [
  18. 'https://cdn.jsdelivr.net/npm/broadway-player@0.5.0/Player/Decoder.js', // H.264
  19. 'https://cdn.jsdelivr.net/npm/libde265.js@1.0.4/libde265.js', // HEVC/H.265
  20. 'https://cdn.jsdelivr.net/npm/dav1d.js@0.8.1/dav1d.js', // AV1
  21. ];
  22.  
  23. decoderScripts.forEach(src => {
  24. const script = document.createElement('script');
  25. script.src = src;
  26. document.head.appendChild(script);
  27. });
  28.  
  29. // 创建自定义的<video>元素
  30. class CustomVideoPlayer extends HTMLElement {
  31. constructor() {
  32. super();
  33. this.attachShadow({ mode: 'open' });
  34.  
  35. // 创建Canvas用于绘制视频帧
  36. this.canvas = document.createElement('canvas');
  37. this.canvas.style.width = '100%';
  38. this.canvas.style.height = '100%';
  39. this.canvas.style.backgroundColor = '#000';
  40. this.shadowRoot.appendChild(this.canvas);
  41.  
  42. // 创建基础控件
  43. const controls = document.createElement('div');
  44. controls.style.position = 'absolute';
  45. controls.style.bottom = '0';
  46. controls.style.left = '0';
  47. controls.style.right = '0';
  48. controls.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  49. controls.style.padding = '10px';
  50. controls.style.display = 'flex';
  51. controls.style.alignItems = 'center';
  52.  
  53. // 播放/暂停按钮
  54. this.playPauseBtn = document.createElement('button');
  55. this.playPauseBtn.innerText = '播放';
  56. this.playPauseBtn.style.padding = '5px 10px';
  57. this.playPauseBtn.style.marginRight = '10px';
  58. this.playPauseBtn.addEventListener('click', () => this.togglePlay());
  59. controls.appendChild(this.playPauseBtn);
  60.  
  61. // 音量控制
  62. this.volumeControl = document.createElement('input');
  63. this.volumeControl.type = 'range';
  64. this.volumeControl.min = '0';
  65. this.volumeControl.max = '1';
  66. this.volumeControl.step = '0.1';
  67. this.volumeControl.value = '1';
  68. this.volumeControl.style.flexGrow = '1';
  69. this.volumeControl.addEventListener('input', () => this.volume = this.volumeControl.value);
  70. controls.appendChild(this.volumeControl);
  71.  
  72. // 进度条
  73. this.progressBar = document.createElement('progress');
  74. this.progressBar.value = '0';
  75. this.progressBar.max = '100';
  76. this.progressBar.style.width = '100%';
  77. this.progressBar.style.marginTop = '10px';
  78. this.progressBar.addEventListener('click', (e) => this.seek(e));
  79. controls.appendChild(this.progressBar);
  80.  
  81. this.shadowRoot.appendChild(controls);
  82.  
  83. // 初始化解码器
  84. this.decoders = {
  85. 'avc': new Decoder({ rgb: true, webgl: true }), // H.264
  86. 'hevc': new libde265.Decoder(), // HEVC/H.265
  87. 'av1': new dav1d.Decoder(), // AV1
  88. };
  89.  
  90. // 绑定解码器回调
  91. this.decoders.avc.onPictureDecoded = this.decoders.hevc.onPictureDecoded = this.decoders.av1.onPictureDecoded = (buffer, width, height) => {
  92. const ctx = this.canvas.getContext('2d');
  93. const imageData = new ImageData(new Uint8ClampedArray(buffer), width, height);
  94. ctx.putImageData(imageData, 0, 0);
  95. };
  96.  
  97. // 初始化状态
  98. this._src = '';
  99. this._currentTime = 0;
  100. this._paused = true;
  101. this._volume = 1;
  102. this._muted = false;
  103. this._playbackRate = 1;
  104. this._crossOrigin = null;
  105. this._autoplay = false;
  106. this._loop = false;
  107. this._preload = 'auto';
  108. this._poster = '';
  109. this._width = 0;
  110. this._height = 0;
  111. this._controls = false;
  112. this._controlsList = '';
  113. this._disablePictureInPicture = false;
  114. this._playsInline = false;
  115. this._videoElement = null;
  116. this._audioContext = null;
  117. this._sourceNode = null;
  118. this._gainNode = null;
  119.  
  120. // 绑定事件
  121. this._timeupdateEvent = new Event('timeupdate');
  122. this._playEvent = new Event('play');
  123. this._pauseEvent = new Event('pause');
  124. this._endedEvent = new Event('ended');
  125. this._errorEvent = new Event('error');
  126. this._progressEvent = new Event('progress');
  127. this._seekedEvent = new Event('seeked');
  128. }
  129.  
  130. // 实现src属性
  131. get src() {
  132. return this._src;
  133. }
  134. set src(value) {
  135. this._src = value;
  136. this.load();
  137. }
  138.  
  139. // 实现currentTime属性
  140. get currentTime() {
  141. return this._currentTime;
  142. }
  143. set currentTime(value) {
  144. this._currentTime = value;
  145. this.dispatchEvent(this._timeupdateEvent);
  146. }
  147.  
  148. // 实现paused属性
  149. get paused() {
  150. return this._paused;
  151. }
  152.  
  153. // 实现volume属性
  154. get volume() {
  155. return this._volume;
  156. }
  157. set volume(value) {
  158. this._volume = value;
  159. if (this._gainNode) {
  160. this._gainNode.gain.value = value;
  161. }
  162. this.volumeControl.value = value;
  163. }
  164.  
  165. // 实现muted属性
  166. get muted() {
  167. return this._muted;
  168. }
  169. set muted(value) {
  170. this._muted = value;
  171. if (this._gainNode) {
  172. this._gainNode.gain.value = value ? 0 : this._volume;
  173. }
  174. }
  175.  
  176. // 实现playbackRate属性
  177. get playbackRate() {
  178. return this._playbackRate;
  179. }
  180. set playbackRate(value) {
  181. this._playbackRate = value;
  182. if (this._videoElement) {
  183. this._videoElement.playbackRate = value;
  184. }
  185. }
  186.  
  187. // 实现crossOrigin属性
  188. get crossOrigin() {
  189. return this._crossOrigin;
  190. }
  191. set crossOrigin(value) {
  192. this._crossOrigin = value;
  193. if (this._videoElement) {
  194. this._videoElement.crossOrigin = value;
  195. }
  196. }
  197.  
  198. // 实现autoplay属性
  199. get autoplay() {
  200. return this._autoplay;
  201. }
  202. set autoplay(value) {
  203. this._autoplay = value;
  204. if (this._videoElement) {
  205. this._videoElement.autoplay = value;
  206. }
  207. }
  208.  
  209. // 实现loop属性
  210. get loop() {
  211. return this._loop;
  212. }
  213. set loop(value) {
  214. this._loop = value;
  215. if (this._videoElement) {
  216. this._videoElement.loop = value;
  217. }
  218. }
  219.  
  220. // 实现preload属性
  221. get preload() {
  222. return this._preload;
  223. }
  224. set preload(value) {
  225. this._preload = value;
  226. if (this._videoElement) {
  227. this._videoElement.preload = value;
  228. }
  229. }
  230.  
  231. // 实现poster属性
  232. get poster() {
  233. return this._poster;
  234. }
  235. set poster(value) {
  236. this._poster = value;
  237. if (this._videoElement) {
  238. this._videoElement.poster = value;
  239. }
  240. }
  241.  
  242. // 实现width属性
  243. get width() {
  244. return this._width;
  245. }
  246. set width(value) {
  247. this._width = value;
  248. this.canvas.width = value;
  249. }
  250.  
  251. // 实现height属性
  252. get height() {
  253. return this._height;
  254. }
  255. set height(value) {
  256. this._height = value;
  257. this.canvas.height = value;
  258. }
  259.  
  260. // 实现controls属性
  261. get controls() {
  262. return this._controls;
  263. }
  264. set controls(value) {
  265. this._controls = value;
  266. controls.style.display = value ? 'flex' : 'none';
  267. }
  268.  
  269. // 切换播放/暂停
  270. togglePlay() {
  271. if (this._paused) {
  272. this.play();
  273. } else {
  274. this.pause();
  275. }
  276. }
  277.  
  278. // 实现play方法
  279. play() {
  280. if (this._videoElement) {
  281. this._videoElement.play();
  282. this._paused = false;
  283. this.playPauseBtn.innerText = '暂停';
  284. this.dispatchEvent(this._playEvent);
  285. this._drawVideoFrame();
  286. }
  287. }
  288.  
  289. // 实现pause方法
  290. pause() {
  291. if (this._videoElement) {
  292. this._videoElement.pause();
  293. this._paused = true;
  294. this.playPauseBtn.innerText = '播放';
  295. this.dispatchEvent(this._pauseEvent);
  296. }
  297. }
  298.  
  299. // 加载视频并解码
  300. load() {
  301. if (this._src) {
  302. fetch(this._src)
  303. .then(response => response.arrayBuffer())
  304. .then(buffer => {
  305. const data = new Uint8Array(buffer);
  306. const codec = this._detectCodec(data);
  307.  
  308. if (this.decoders[codec]) {
  309. this.decoders[codec].decode(data); // 解码视频帧
  310. } else {
  311. console.error('不支持的视频编码格式:', codec);
  312. }
  313. })
  314. .catch(error => {
  315. console.error('视频加载失败:', error);
  316. });
  317. }
  318. }
  319.  
  320. // 检测视频编码格式
  321. _detectCodec(data) {
  322. if (data[4] === 0x68 && data[5] === 0x76 && data[6] === 0x63 && data[7] === 0x31) {
  323. return 'avc'; // H.264
  324. } else if (data[4] === 0x68 && data[5] === 0x65 && data[6] === 0x76 && data[7] === 0x63) {
  325. return 'hevc'; // HEVC/H.265
  326. } else if (data[4] === 0x61 && data[5] === 0x76 && data[6] === 0x30 && data[7] === 0x31) {
  327. return 'av1'; // AV1
  328. } else {
  329. return 'unknown';
  330. }
  331. }
  332.  
  333. // 绘制视频帧
  334. _drawVideoFrame() {
  335. if (!this._paused && this._videoElement) {
  336. const ctx = this.canvas.getContext('2d');
  337. ctx.drawImage(this._videoElement, 0, 0, this.canvas.width, this.canvas.height);
  338. this.currentTime = this._videoElement.currentTime;
  339. this.progressBar.value = (this.currentTime / this._videoElement.duration) * 100;
  340. requestAnimationFrame(() => this._drawVideoFrame());
  341. }
  342. }
  343.  
  344. // 进度条跳转
  345. seek(e) {
  346. const rect = this.progressBar.getBoundingClientRect();
  347. const percent = (e.clientX - rect.left) / rect.width;
  348. this.currentTime = percent * this._videoElement.duration;
  349. }
  350.  
  351. // 释放资源
  352. _releaseResources() {
  353. if (this._audioContext) {
  354. this._audioContext.close();
  355. this._audioContext = null;
  356. }
  357. if (this._videoElement) {
  358. URL.revokeObjectURL(this._videoElement.src);
  359. this._videoElement = null;
  360. }
  361. }
  362. }
  363.  
  364. // 注册(不可用)自定义元素
  365. customElements.define('custom-video', CustomVideoPlayer);
  366.  
  367. // 替换<video>标签
  368. function replaceVideoElements() {
  369. const videoElements = document.querySelectorAll('video');
  370. videoElements.forEach(video => {
  371. const customVideo = document.createElement('custom-video');
  372. customVideo.src = video.src;
  373. // 设置其他属性...
  374. video.parentNode.replaceChild(customVideo, video);
  375. });
  376. }
  377.  
  378. // 监控DOM变化并替换<video>标签
  379. const observer = new MutationObserver((mutations) => {
  380. mutations.forEach((mutation) => {
  381. mutation.addedNodes.forEach((node) => {
  382. if (node.tagName === 'VIDEO') {
  383. const customVideo = document.createElement('custom-video');
  384. customVideo.src = node.src;
  385. // 设置其他属性...
  386. node.parentNode.replaceChild(customVideo, node);
  387. }
  388. });
  389. });
  390. });
  391.  
  392. // 在脚本加载完成后替换<video>标签
  393. window.addEventListener('load', () => {
  394. replaceVideoElements();
  395. observer.observe(document.body, { childList: true, subtree: true });
  396. });
  397. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址