拦截m3u8请求并移除其中插入的广告ts片段
// ==UserScript==
// @name Remove Ad from M3U8
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 拦截m3u8请求并移除其中插入的广告ts片段
// @author
// @match *://*/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
let processingM3U8 = false;
async function fixAdM3u8Ai(m3u8_url, headers = null){
let ts = new Date().getTime();
let option = headers ? {headers: headers} : {};
function b(s1, s2) {
let i = 0;
while (i < s1.length) {
if (s1[i] !== s2[i]) {
break
}
i++
}
return i;
}
function reverseString(str) {
return str.split('').reverse().join('');
}
//log('播放的地址:' + m3u8_url);
const m3u8_response = await fetch(m3u8_url, option);
let m3u8 =await m3u8_response.text();
//log('m3u8处理前:' + m3u8);
m3u8 = m3u8.trim().split('\n').map(it => it.startsWith('#') ? it : urljoin(m3u8_url, it)).join('\n');
//log('m3u8处理后:============:' + m3u8);
// 获取嵌套m3u8地址
m3u8 = m3u8.replace(/\n\n/gi, '\n'); //删除多余的换行符
let last_url = m3u8.split('\n').slice(-1)[0];
if (last_url.length < 5) {
last_url = m3u8.split('\n').slice(-2)[0];
}
if (last_url.includes('.m3u8') && last_url !== m3u8_url) {
m3u8_url = urljoin(m3u8_url, last_url);
// console.log('嵌套的m3u8_url:' + m3u8_url);
const m3u8_nest_response = await fetch(m3u8_url, option);
m3u8 = await m3u8_nest_response.text();
}
//log('----处理有广告的地址----');
let s = m3u8.trim().split('\n').filter(it => it.trim()).join('\n');
let ss = s.split('\n');
//找出第一条播放地址
//let firststr = ss.find(x => !x.startsWith('#'));
let firststr = '';
let maxl = 0;//最大相同字符
let kk = 0;
let kkk = 2;
let secondstr = '';
for (let i = 0; i < ss.length; i++) {
let s = ss[i];
if (!s.startsWith("#")) {
if (kk == 0) firststr = s;
if (kk == 1) maxl = b(firststr, s);
if (kk > 1) {
if (maxl > b(firststr, s)) {
if (secondstr.length < 5) secondstr = s;
kkk = kkk + 2;
} else {
maxl = b(firststr, s);
kkk++;
}
}
kk++;
if (kk >= 20) break;
}
}
if (kkk > 30) firststr = secondstr;
let firststrlen = firststr != null ? firststr.length : null
//log('字符串长度:' + firststrlen);
let ml = Math.round(ss.length / 2).toString().length; //取数据的长度的位数
//log('数据条数的长度:' + ml);
//找出最后一条播放地址
let maxc = 0;
let laststr = ss.toReversed().find((x) => {
if (!x.startsWith('#')) {
let k = b(reverseString(firststr), reverseString(x));
maxl = b(firststr, x);
maxc++;
if (firststrlen - maxl <= ml + k || maxc > 10) {
return true;
}
}
return false;
});
// console.log('最后一条切片:' + laststr);
//log('最小相同字符长度:' + maxl);
let ad_urls = [];
for (let i = 0; i < ss.length; i++) {
let s = ss[i];
if (!s.startsWith('#')) {
if (b(firststr, s) < maxl) {
ad_urls.push(s); // 广告地址加入列表
ss.splice(i - 1, 2);
i = i - 2;
} else {
ss[i] = urljoin(m3u8_url, s);
}
} else {
ss[i] = s.replace(/URI=\"(.*)\"/, 'URI="' + urljoin(m3u8_url, '$1') + '"');
}
}
// console.log('处理的m3u8地址:' + m3u8_url);
// console.log('----广告地址----');
console.log(ad_urls);
m3u8 = ss.join('\n');
//log('处理完成');
//console.log('处理耗时:' + (new Date().getTime() - ts).toString());
return m3u8;
}
function resolve (from, to){
const resolvedUrl = new URL(to, new URL(from, 'resolve://'));
if (resolvedUrl.protocol === 'resolve:') {
const { pathname, search, hash } = resolvedUrl;
return pathname + search + hash;
}
return resolvedUrl.href;
};
function urljoin (fromPath, nowPath){
fromPath = fromPath || '';
nowPath = nowPath || '';
return resolve(fromPath, nowPath);
};
function playm3u8(urlm3,playerElement) {
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(urlm3);
hls.attachMedia(playerElement);
//video.play();
hls.on(Hls.Events.MANIFEST_PARSED, function () {
playerElement.play();
});
hls.on(Hls.Events.ERROR, (event, data) => {
//console.log("1222");
console.log(event, data);
// 监听出错事件
});
}
}
function replacePlayerSource(playerElement, newSource) {
// 假设播放器使用 <video> 或类似标签
if (playerElement) {
playm3u8(newSource,playerElement)
} else {
console.warn('Player element not found.');
}
}
const originalXhrOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
if (url.endsWith('.m3u8')) {
this.addEventListener('load', async function() {
if (!processingM3U8 && (this.responseType === '' || this.responseType === 'text')) {
processingM3U8 = true;
const m3u8_content = await fixAdM3u8Ai(url);
processingM3U8 = false;
// 创建 Blob 并生成 URL
const blob = new Blob([m3u8_content], { type: 'text/plain' });
const newM3u8Url = URL.createObjectURL(blob);
// 替换播放器源
const player = document.querySelector('video'); // 根据实际情况调整选择器
replacePlayerSource(player,newM3u8Url);
}
});
}
originalXhrOpen.apply(this, arguments);
};
})();