Files
Docker/uniapp/Google Chrome/Resources/video_grab.js
T
2026-11-06 14:57:55 +08:00

133 lines
4.3 KiB
JavaScript

// 点击"下载视频"时执行:优先从页面内嵌数据 RENDER_DATA 精确提取当前视频地址,
// 其次才用嗅探列表兜底。
(function () {
try {
var pageUrl = location.href;
// 从 URL 提取视频 ID
var videoId = "";
var m = pageUrl.match(/[?&]modal_id=(\d+)/);
if (m) videoId = m[1];
if (!videoId) {
var m0 = pageUrl.match(/[?&]aweme_id=(\d+)/);
if (m0) videoId = m0[1];
}
if (!videoId) {
var m2 = pageUrl.match(/\/(?:video|note)\/(\d+)/);
if (m2) videoId = m2[1];
}
// === 方案一:从 RENDER_DATA 里精确找当前视频的播放地址 ===
var renderVideoUrl = "";
var renderAudioUrl = "";
try {
var rd = document.getElementById("RENDER_DATA");
if (rd && rd.textContent) {
var decoded = decodeURIComponent(rd.textContent);
// 在 JSON 里找 aweme_id 等于 videoId 的节点,提取其 video.play_addr 或 play_addr 列表
// 用正则粗匹配,避免完整 JSON 解析的复杂性
var idx = decoded.indexOf(videoId);
while (idx >= 0) {
// 在这个 ID 附近找 playApi / play_addr / url_list
var chunk = decoded.substring(Math.max(0, idx - 200), idx + 5000);
// 找 DASH 格式的视频轨和音频轨
var vMatches = chunk.match(/https?:\/\/[^"\\]+media-video[^"\\]*/gi);
var aMatches = chunk.match(/https?:\/\/[^"\\]+media-audio[^"\\]*/gi);
if (vMatches && vMatches.length) {
renderVideoUrl = vMatches[vMatches.length - 1];
if (aMatches && aMatches.length) {
renderAudioUrl = aMatches[aMatches.length - 1];
}
break;
}
// 找普通 mp4 直链
var mp4Matches = chunk.match(/https?:\/\/[^"\\]*\.mp4[^"\\]*/gi);
if (mp4Matches && mp4Matches.length) {
renderVideoUrl = mp4Matches[mp4Matches.length - 1];
break;
}
idx = decoded.indexOf(videoId, idx + 1);
}
}
} catch (e) { /* RENDER_DATA 解析失败就走嗅探兜底 */ }
// === 方案二:选正在播放的 <video> 元素 ===
var vids = Array.prototype.slice.call(document.querySelectorAll("video"));
var best = null;
for (var i = 0; i < vids.length; i++) {
var v = vids[i];
if (!v.paused && v.currentTime > 0) { best = v; break; }
}
if (!best) {
for (var j = 0; j < vids.length; j++) {
var v2 = vids[j];
if (v2.videoWidth > 0 || (!isNaN(v2.duration) && v2.duration > 0 && v2.currentTime > 0)) {
best = v2;
break;
}
}
}
if (!best) best = vids[0];
var current = "";
var playable = true;
if (best) {
current = best.currentSrc || best.src || "";
if (!current) {
var s = best.querySelector && best.querySelector("source");
if (s) current = s.src || s.getAttribute("src") || "";
}
if (best.networkState === 0) playable = false;
}
// === 方案三:嗅探列表兜底 ===
var all = window.__mediaUrls || [];
var videoTracks = [];
var audioTracks = [];
var otherMedia = [];
for (var k = 0; k < all.length; k++) {
var u = all[k];
if (/media-video/i.test(u)) videoTracks.push(u);
else if (/media-audio/i.test(u)) audioTracks.push(u);
else otherMedia.push(u);
}
function pickLast(arr) {
return arr.length ? arr[arr.length - 1] : "";
}
var dashVideo = pickLast(videoTracks);
var dashAudio = pickLast(audioTracks);
// 如果 RENDER_DATA 找到了,优先用它
if (renderVideoUrl) {
dashVideo = renderVideoUrl;
if (renderAudioUrl) dashAudio = renderAudioUrl;
}
var title = document.title || "";
var h1 = document.querySelector("h1");
if (h1 && h1.innerText) title = h1.innerText;
return JSON.stringify({
current: current || "",
playable: playable,
usesMse: !!window.__usesMse || /^blob:/i.test(current),
title: title,
pageUrl: pageUrl,
videoId: videoId,
sniffed: otherMedia.slice(-60),
dashVideo: dashVideo,
dashAudio: dashAudio,
m3u8Blobs: (window.__m3u8Blobs || []).slice(-1),
});
} catch (e) {
return JSON.stringify({
current: "",
title: document.title || "",
pageUrl: location.href,
error: String(e),
});
}
})();