Autoplay videos when in view, pause when not
// ==UserScript==
// @name Autoplay Visible Videos on reddtastic.com
// @namespace http://tampermonkey.net/
// @version 2025-06-26
// @description Autoplay videos when in view, pause when not
// @author You
// @match *://reddtastic.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=reddtastic.com
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const style = document.createElement('style');
style.textContent = `
body {
scroll-behavior: smooth;
font-family: 'Trebuchet MS', sans-serif;
background-color: #131313;
max-width: 100%;
}
.nav-bar__logo {
display: none;
}
#posts {
align-items: center;
flex-direction: column;
align-self: center;
max-width: 600px;
}
.post {
width: 600px;
max-width: 100vw;
display: flex;
margin-bottom: 20px;
border: solid;
border-radius: 15px;
border-color: #333333;
flex-direction: column;
align-items: center;
gap: 0.5rem;
padding: 1rem;
background-color: #1e1e20;
color: #e1e7e9;
font-family: 'Trebuchet MS', sans-serif;
font-weight: bold;
}
.post__column {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
max-width: 100vw;
}
.post img {
border-radius: 8px;
}
.post video {
border-radius: 8px;
width: 100%;
max-height: 80vh;
object-fit: cover;
}
.post a {
color: #e1e7e9;
text-decoration: none;
align-self: self-start;
}
.post a:hover {
text-decoration: underline;
}
.post__title {
font-size: 18px;
}
.post__meta {
align-self: self-start;
font-weight: normal;
}
`;
document.head.appendChild(style);
let currentlyPlaying = null;
const observedVideos = new Set(); // Track which videos we're already observing
const intersectionObserver = new IntersectionObserver((entries) => {
// Sort by visibility ratio (most visible first)
const visibleVideos = entries
.filter(entry => entry.isIntersecting)
.sort((a, b) => b.intersectionRatio - a.intersectionRatio);
// Pause all observed videos except the most visible one
entries.forEach(entry => {
if (entry.target !== visibleVideos[0]?.target) {
entry.target.pause();
}
});
// Play the most visible one
if (visibleVideos.length > 0) {
const topVideo = visibleVideos[0].target;
if (currentlyPlaying && currentlyPlaying !== topVideo) {
currentlyPlaying.pause();
}
topVideo.play().catch(e => console.log('Autoplay blocked:', e));
currentlyPlaying = topVideo;
}
}, {
threshold: [0.75]
});
function observeVideo(video) {
if (!observedVideos.has(video)) {
intersectionObserver.observe(video);
observedVideos.add(video);
}
}
function observeAllVideos() {
document.querySelectorAll('video').forEach(observeVideo);
}
// MutationObserver to watch for dynamically added videos
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
// Check if the added node is a video
if (node.tagName === 'VIDEO') {
observeVideo(node);
}
// Check if the added node contains videos
else if (node.querySelectorAll) {
node.querySelectorAll('video').forEach(observeVideo);
}
}
});
// Clean up removed videos
mutation.removedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.tagName === 'VIDEO') {
intersectionObserver.unobserve(node);
observedVideos.delete(node);
if (currentlyPlaying === node) {
currentlyPlaying = null;
}
}
else if (node.querySelectorAll) {
node.querySelectorAll('video').forEach((video) => {
intersectionObserver.unobserve(video);
observedVideos.delete(video);
if (currentlyPlaying === video) {
currentlyPlaying = null;
}
});
}
}
});
});
});
// Start observing for DOM changes
mutationObserver.observe(document.body, {
childList: true,
subtree: true
});
// Initial observation setup
const waitForVideos = () => {
observeAllVideos();
// Keep checking periodically for any videos we might have missed
setTimeout(waitForVideos, 2000);
};
// Start when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', waitForVideos);
} else {
waitForVideos();
}
})();