123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
// ==UserScript==
// @name Replace Video Links with HTML5 Player (Video.js)
// @namespace http://tampermonkey.net/
// @version 1.3
// @description Replace video links with HTML5 player on XenForo.com using Video.js
// @author Your Name
// @match https://www.thecoli.com/threads/*
// @match https://thecoli.com/threads/*
// @match https://www.thecoli.com/forums/*/post-thread
// @match https://thecoli.com/forums/*/post-thread
// @match https://www.thecoli.com/conversations/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Function to load Video.js CSS and JS files if they are not already loaded
function loadVideoJs() {
const head = document.head || document.getElementsByTagName('head')[0];
const style = document.createElement('link');
style.rel = 'stylesheet';
style.href = 'https://vjs.zencdn.net/8.16.1/video-js.css';
head.appendChild(style);
// Add custom Video.js styles
const customStyle = document.createElement('style');
customStyle.textContent = `
.video-js {
max-width: 100vw;
max-height: 80vh;
object-fit: contain;
margin: 0 auto;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
`;
head.appendChild(customStyle);
const script = document.createElement('script');
script.src = 'https://vjs.zencdn.net/8.16.1/video.min.js';
head.appendChild(script);
return new Promise(resolve => {
script.onload = resolve;
});
}
var replacedLinks = {};
function replaceVideoLinks() {
var links = document.querySelectorAll('a[href*=".mp4"], a[href*=".m3u8"], a[href*=".webm"]');
links.forEach(function(link) {
if (!replacedLinks[link.href]) {
var videoUrl = link.href;
// Create a new <video> element based on file type
let videoElement;
if (videoUrl.endsWith('.m3u8')) {
// Use Video.js for .m3u8 links
loadVideoJs().then(() => {
videoElement = document.createElement('video');
videoElement.className = 'video-js';
videoElement.controls = true;
const sourceElement = document.createElement('source');
sourceElement.src = videoUrl;
sourceElement.type = 'application/x-mpegURL';
videoElement.appendChild(sourceElement);
// Add playback speed control
const speedButton = document.createElement('button');
speedButton.textContent = 'Speed';
speedButton.onclick = function() {
const speeds = ['0.5', '1', '1.5', '2'];
const currentSpeed = videoElement.playbackRate;
const nextSpeedIndex = (speeds.indexOf(currentSpeed) + 1) % speeds.length;
videoElement.playbackRate = speeds[nextSpeedIndex];
speedButton.textContent = `Speed: ${speeds[nextSpeedIndex]}x`;
};
videoElement.controlsList = 'nodownload';
videoElement.appendChild(speedButton);
// Add Picture-in-Picture (PiP) button
const pipButton = document.createElement('button');
pipButton.textContent = 'PiP';
pipButton.onclick = function() {
if (document.pictureInPictureEnabled) {
videoElement.requestPictureInPicture();
}
};
videoElement.appendChild(pipButton);
// Add closed captions
const captionButton = document.createElement('button');
captionButton.textContent = 'CC';
captionButton.onclick = function() {
videoElement.textTracks[0].mode = videoElement.textTracks[0].mode === 'disabled' ? 'showing' : 'disabled';
};
videoElement.appendChild(captionButton);
link.parentNode.replaceChild(videoElement, link);
setTimeout(() => {
const player = window.videojs(videoElement);
player.play();
}, 100); // Delay initialization by 100ms to ensure DOM readiness
replacedLinks[link.href] = true;
});
} else if (videoUrl.endsWith('.mp4') || videoUrl.endsWith('.webm')) {
// Use native HTML5 player for .mp4 and .webm links
videoElement = document.createElement('video');
videoElement.controls = true;
videoElement.style.maxWidth = '100vw';
videoElement.style.maxHeight = '80vh';
videoElement.style.objectFit = 'contain';
videoElement.style.margin = '0 auto';
videoElement.style.borderRadius = '5px';
videoElement.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.2)';
// Add playback speed control
const speedButton = document.createElement('button');
speedButton.textContent = 'Speed: 1x';
speedButton.onclick = function() {
const speeds = ['0.5', '1', '1.5', '2'];
const currentSpeed = videoElement.playbackRate;
const nextSpeedIndex = speeds.indexOf(currentSpeed) !== -1 ? (speeds.indexOf(currentSpeed) + 1) % speeds.length : 1;
videoElement.playbackRate = speeds[nextSpeedIndex];
speedButton.textContent = `Speed: ${speeds[nextSpeedIndex]}x`;
};
videoElement.appendChild(speedButton);
// Add Picture-in-Picture (PiP) button
const pipButton = document.createElement('button');
pipButton.textContent = 'PiP';
pipButton.onclick = function() {
if (document.pictureInPictureEnabled) {
videoElement.requestPictureInPicture();
}
};
videoElement.appendChild(pipButton);
// Add closed captions
const captionButton = document.createElement('button');
captionButton.textContent = 'CC';
captionButton.onclick = function() {
if (videoElement.textTracks) {
videoElement.textTracks[0].mode = videoElement.textTracks[0].mode === 'disabled' ? 'showing' : 'disabled';
}
};
videoElement.appendChild(captionButton);
// Add keyboard navigation
videoElement.tabIndex = 0;
videoElement.addEventListener('keydown', function(event) {
switch (event.key) {
case ' ':
videoElement.paused ? videoElement.play() : videoElement.pause();
break;
case 'ArrowLeft':
videoElement.currentTime -= 10;
break;
case 'ArrowRight':
videoElement.currentTime += 10;
break;
case 'ArrowUp':
videoElement.volume = Math.min(videoElement.volume + 0.1, 1);
break;
case 'ArrowDown':
videoElement.volume = Math.max(videoElement.volume - 0.1, 0);
break;
}
});
const sourceElement = document.createElement('source');
sourceElement.src = videoUrl;
if (videoUrl.endsWith('.mp4')) {
sourceElement.type = 'video/mp4';
} else if (videoUrl.endsWith('.webm')) {
sourceElement.type = 'video/webm';
}
videoElement.appendChild(sourceElement);
link.parentNode.replaceChild(videoElement, link);
replacedLinks[link.href] = true;
}
}
});
requestAnimationFrame(replaceVideoLinks);
}
replaceVideoLinks();
})();