// ==UserScript==
// @name M3U8 嗅探 + MediaGo 投喂器
// @namespace https://blog.zhecydn.asia/
// @version 2.3
// @description 一键投喂M3U8视频资源到 MediaGo(支持 docker 与本地版),具备自动防重名命名、4K/1080P 🔥 标注及文件夹自动整理功能
// @author zhecydn
// @match *://*/*
// @allframes true
// @run-at document-start
// @license MIT
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
let MEDIAGO_URL = GM_getValue('mediago_url', '');
let theme = GM_getValue('theme', 'dark');
let mode = GM_getValue('mode', 'api');
let target = GM_getValue('target', 'nas');
let folderType = GM_getValue('folder_type', 'domain');
let counter = GM_getValue('counter', {});
let detectedUrls = new Set();
let panel = null;
// --- 1. 跨页面通信 ---
if (window.self !== window.top) {
window.notifyTop = url => window.top.postMessage({ type: 'VIDEO_MSG_V22', url: url }, '*');
} else {
window.addEventListener('message', e => {
if (e.data && e.data.type === 'VIDEO_MSG_V22') addUrl(e.data.url);
});
}
// --- 2. 辅助逻辑 ---
const getResTag = (u) => {
u = u.toLowerCase();
if (u.includes('8k') || u.includes('4320')) return '[👑 8K] ';
if (u.includes('4k') || u.includes('2160')) return '[💎 4K] ';
if (u.includes('2k') || u.includes('1440')) return '[🚀 2K] ';
if (u.includes('1080') || u.includes('1920') || u.includes('3000k')) return '[🔥 1080P] ';
if (u.includes('720') || u.includes('1280')) return '[🌟 720P] ';
if (u.includes('480') || u.includes('848') || u.includes('800k')) return '[🍃 480P] ';
return '';
};
const getFolder = () => folderType === 'domain' ? location.hostname.split('.')[0] : '';
const getSmartName = (base) => {
if (!counter[base]) counter[base] = 0;
counter[base]++;
GM_setValue('counter', counter);
const now = new Date();
const ts = `${now.getHours()}${now.getMinutes()}${now.getSeconds()}`;
return `${base}_${counter[base]}_${ts}`;
};
// --- 3. 核心嗅探 ---
function addUrl(url) {
if (typeof url !== 'string' || !/\.m3u8(\?|$)/i.test(url) || detectedUrls.has(url)) return;
if (url.startsWith('blob:')) return;
if (window.self !== window.top) { window.notifyTop(url); return; }
detectedUrls.add(url);
if (!panel) createPanel();
const li = document.createElement('li');
li.className = 'm3u8-item';
li.innerHTML = `
${getResTag(url)}${url.split('?')[0].substring(0, 60)}...
`;
// 核心增强:点击整行(除了投喂按钮)即可切换勾选状态
li.onclick = (e) => {
if (e.target.tagName !== 'BUTTON') {
const cb = li.querySelector('.checkbox');
cb.checked = !cb.checked;
li.classList.toggle('selected', cb.checked);
}
};
document.getElementById('m3u8-list').prepend(li);
const btn = li.querySelector('.single-send');
btn.onclick = (e) => { e.stopPropagation(); sendTask(url, btn); };
}
const origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(m, u) {
try { addUrl(new URL(u, location.href).href); } catch(e) {}
return origOpen.apply(this, arguments);
};
const origFetch = window.fetch;
window.fetch = function(res) {
let u = typeof res === 'string' ? res : (res && res.url);
if (u) { try { addUrl(new URL(u, location.href).href); } catch(e) {} }
return origFetch.apply(this, arguments);
};
setInterval(() => {
document.querySelectorAll('video, source, a').forEach(el => {
const src = el.src || el.getAttribute('src') || el.href;
if (src && src.includes('.m3u8')) {
try { addUrl(new URL(src, location.href).href); } catch(e) {}
}
});
}, 3000);
// --- 4. 投喂逻辑 ---
function sendTask(url, btn, customName = null) {
const baseTitle = document.title || '视频任务';
let finalName = "";
if (customName === null) {
const n = prompt('确认任务名称:', baseTitle);
if (n === null) return;
finalName = getSmartName(n.trim() || baseTitle);
} else {
finalName = getSmartName(customName);
}
const folder = getFolder();
const encodedName = encodeURIComponent(finalName);
const encodedUrl = encodeURIComponent(url);
const folderParam = folder ? `&folder=${encodeURIComponent(folder)}` : '';
if (target === 'local') {
const jump = `mediago://index.html/?n=true&name=${encodedName}&url=${encodedUrl}&headers=Referer%3A*${folderParam}&type=m3u8&silent=true`;
window.open(jump, '_blank');
} else {
if (!MEDIAGO_URL) return alert('请先⚙️设置 mediago docker 地址');
if (mode === 'api') {
GM_xmlhttpRequest({
method: 'POST',
url: `${MEDIAGO_URL}/api/download-now`,
headers: { 'Content-Type': 'application/json' },
data: JSON.stringify({ name: finalName, url: url, type: 'm3u8', folder: folder }),
onload: () => console.log('API发送成功')
});
} else {
const jump = `${MEDIAGO_URL}/?n=true&name=${encodedName}&url=${encodedUrl}&headers=Referer%3A*${folderParam}&type=m3u8&silent=true`;
window.open(jump, '_blank');
}
}
if (btn) { btn.innerText = "✅ 已投喂"; btn.style.opacity = "0.5"; }
}
// --- 5. UI 界面 ---
function createPanel() {
if (window.self !== window.top || document.getElementById('mediago-panel')) return;
panel = document.createElement('div');
panel.id = 'mediago-panel';
panel.className = theme;
panel.innerHTML = `