Skip to content

Instantly share code, notes, and snippets.

@uahim
Last active February 1, 2026 18:59
Show Gist options
  • Select an option

  • Save uahim/155e538af7f1681f3708c30feeadf3b1 to your computer and use it in GitHub Desktop.

Select an option

Save uahim/155e538af7f1681f3708c30feeadf3b1 to your computer and use it in GitHub Desktop.
!
// ==UserScript==
// @name Pluto TV .m3u8 Grabber FORK
// @namespace http://tampermonkey.net/
// @version 1.8.5
// @description Captures m3u8 URLs via XHR and retrieves the last intercepted URL
// @author GhostyTongue + mihau
// @match *://*.pluto.tv/*
// @grant none
// @license MIT
// @downloadURL https://update.greasyfork.org/scripts/506067/Pluto%20TV%20m3u8%20Grabber.user.js
// @updateURL https://update.greasyfork.org/scripts/506067/Pluto%20TV%20m3u8%20Grabber.meta.js
// ==/UserScript==
(function() {
'use strict';
let lastInterceptedUrl = null;
let logs = [];
let isInterceptionEnabled = false;
let enableInterceptionTimeout = null;
function showAlert(message) {
const alertBox = document.createElement('div');
alertBox.style.position = 'fixed';
alertBox.style.top = '20px';
alertBox.style.left = '50%';
alertBox.style.transform = 'translateX(-50%)';
alertBox.style.backgroundColor = '#333';
alertBox.style.color = '#fff';
alertBox.style.padding = '20px';
alertBox.style.borderRadius = '5px';
alertBox.style.zIndex = '9999';
alertBox.style.maxWidth = '80%';
alertBox.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
alertBox.style.fontSize = '16px';
alertBox.style.textAlign = 'center';
alertBox.style.opacity = '0';
alertBox.style.transition = 'opacity 0.5s';
const messageElem = document.createElement('p');
messageElem.innerHTML = message;
alertBox.appendChild(messageElem);
const closeButton = document.createElement('button');
closeButton.innerText = 'Close';
closeButton.style.marginTop = '10px';
closeButton.style.padding = '10px 20px';
closeButton.style.backgroundColor = '#FF4500';
closeButton.style.color = '#FFFFFF';
closeButton.style.border = 'none';
closeButton.style.borderRadius = '5px';
closeButton.style.cursor = 'pointer';
closeButton.addEventListener('click', () => {
alertBox.style.opacity = '0';
setTimeout(() => {
document.body.removeChild(alertBox);
}, 500);
});
alertBox.appendChild(closeButton);
document.body.appendChild(alertBox);
setTimeout(() => {
alertBox.style.opacity = '1';
}, 10);
}
function logMessage(message) {
logs.push(message);
console.log(message);
}
function copyToClipboard(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
logMessage(`Copied to clipboard: ${text}`);
}
function handleUrlChange() {
logMessage('URL changed - resetting interception state');
lastInterceptedUrl = null;
isInterceptionEnabled = false;
updateButtonColor();
if (enableInterceptionTimeout) {
clearTimeout(enableInterceptionTimeout);
enableInterceptionTimeout = null;
}
isInterceptionEnabled = true;
}
enableInterceptionTimeout = setTimeout(() => {
isInterceptionEnabled = true;
logMessage('Initial interception delay completed');
}, 2000);
window.addEventListener('popstate', handleUrlChange);
window.addEventListener('hashchange', handleUrlChange);
const originalXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
this._url = url;
const urlLower = url.toLowerCase();
if (urlLower.includes('master.m3u8') || urlLower.includes('playlist.m3u8')) {
this.addEventListener('load', () => {
if (isInterceptionEnabled) {
lastInterceptedUrl = url;
logMessage(`Intercepted URL via XHR: ${url}`);
updateButtonColor();
}
});
}
originalXHROpen.apply(this, arguments);
};
function updateButtonColor() {
const getButton = document.getElementById('m3u8GrabberButton');
if (getButton) {
getButton.style.backgroundColor = lastInterceptedUrl ? '#28a745' : '#ff4444';
}
}
function createGetButton() {
const getButton = document.createElement("button");
getButton.id = "m3u8GrabberButton";
getButton.innerText = "Get Playlist URL";
getButton.style.position = "fixed";
getButton.style.top = "10px";
getButton.style.right = "10px";
getButton.style.padding = "10px 20px";
getButton.style.backgroundColor = "#ff4444";
getButton.style.color = "#FFFFFF";
getButton.style.border = "none";
getButton.style.borderRadius = "5px";
getButton.style.cursor = "pointer";
getButton.style.zIndex = "9999";
getButton.style.transition = "background-color 0.5s ease";
getButton.addEventListener("click", () => {
if (!lastInterceptedUrl) {
showAlert("No m3u8 URL detected yet. Please start playing a channel.");
return;
}
var trash = new RegExp("\\?.*", "gi")
var clean = lastInterceptedUrl.replace(trash, "");
var display = clean + "?jwt=" + getParameterByName("jwt",lastInterceptedUrl);
copyToClipboard(display);
showAlert(`URL copied to clipboard!<br><small>${display}</small>`);
});
document.body.appendChild(getButton);
}
function getParameterByName(name, url) {
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
window.addEventListener('load', createGetButton);
})();
@Gh0styTongue
Copy link

Bruh

@uahim
Copy link
Author

uahim commented Feb 1, 2026

sup?

let me quote the MIT license you put your code under:

Permission is hereby granted (...) copy, modify, merge, publish, distribute (etc)

@Gh0styTongue
Copy link

sup?

let me quote the MIT license you put your code under:

Permission is hereby granted (...) copy, modify, merge, publish, distribute (etc)

Bruh so true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment