🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

토론 » 개발

华医网已实现可加速,但是无法跳过时长检测,开源社区求助

§
게시: 2025-11-25

```

(function() {
'use strict';
const TARGET_RATE = 4; // 目标倍速
let video = null;
let player = null;
let originalPlayerTime = null; // 实际方法:j2s_getCurrentTime
let fakeCurrentTime = 0;
let fakeTimeStartTime = 0;
let isPlaying = false;
let lastProgressTime = 0;
let originalGetMaxPlayTime = null; // 保存原getMaxPlayTime

// 1. 定位播放器(适配j2s_getCurrentTime)
const findPlayer = () => {
video = document.querySelector('video.pv-video') ||
document.querySelector('video[src^="blob:"]') ||
document.querySelector('video');
if (video) {
const possibleProps = ['player', '_player', 'pvPlayer', 'videoPlayer'];
for (const prop of possibleProps) {
if (video[prop] && typeof video[prop] === 'object' &&
typeof video[prop].j2s_getCurrentTime === 'function') {
return video[prop];
}
}
}
if (window.player && typeof window.player.j2s_getCurrentTime === 'function') {
return window.player;
}
return null;
};

// 2. Hook j2s_getCurrentTime(伪造1倍速时间)
const hookPlayerTime = () => {
if (!player) {
console.error('❌ 未找到播放器实例,无法 Hook');
return;
}
originalPlayerTime = player.j2s_getCurrentTime.bind(player);
const initTime = originalPlayerTime();
fakeCurrentTime = isNaN(initTime) ? 0 : Math.max(0, initTime);
fakeTimeStartTime = performance.now();

player.j2s_getCurrentTime = function() {
const realTime = originalPlayerTime();
if (isNaN(realTime) || realTime < 0) return fakeCurrentTime;

if (isPlaying) {
const elapsed = (performance.now() - fakeTimeStartTime) / 1000;
fakeCurrentTime = initTime + elapsed * 1; // 伪造1倍速
fakeCurrentTime = Math.min(fakeCurrentTime, realTime + 0.1); // 防检测
return fakeCurrentTime;
}

fakeCurrentTime = realTime;
return fakeCurrentTime;
};
console.log('✅ Hook j2s_getCurrentTime 成功');
};

// 3. 篡改getMaxPlayTime(解决快进检测)+ 兜底逻辑
const hookJumpToTime = () => {
if (window.getMaxPlayTime) {
originalGetMaxPlayTime = window.getMaxPlayTime; // 保存原函数
window.getMaxPlayTime = function() {
const currentTime = player.j2s_getCurrentTime();
const realMaxTime = originalGetMaxPlayTime(); // 真实已播放最大时长
// 确保jumpToTime ≥ 真实值-3(避免触发平台阈值)
return Math.max(currentTime, realMaxTime - 3);
};
console.log('✅ 篡改getMaxPlayTime:解决快进检测');
}
};

// 4. 处理关键节点提示(showTime):拦截定时器+自动关提示
const handleKeyNodes = () => {
// 方案1:拦截创建关键节点检测的定时器
const originalSetInterval = window.setInterval;
window.setInterval = function(callback, delay) {
const callbackStr = callback.toString();
// 匹配平台关键节点检测特征(showTime.includes、sect_tips)
if (callbackStr.includes('showTime') && callbackStr.includes('sect_tips')) {
console.log('✅ 拦截关键节点检测定时器');
return -1; // 不创建该定时器
}
return originalSetInterval(callback, delay);
};

// 方案2:自动关闭已显示的提示(兜底)
setInterval(() => {
if (window.$) {
$('#div_processbar_tip, .sect_tips').hide();
}
}, 100); // 每100ms检查一次
console.log('✅ 处理关键节点提示:自动关闭+拦截定时器');
};

// 5. 扩展埋点工具拦截(覆盖所有可能的埋点)
const interceptAllSensors = () => {
// 覆盖常见埋点工具:sensors、百度统计、谷歌分析、友盟等
const sensorsList = [
window.sensors,
window._hmt,
window.gtag,
window.umeng,
window.analytics
];

sensorsList.forEach(sensor => {
if (!sensor) return;

// 拦截sensors.track
if (sensor.track) {
const originalTrack = sensor.track;
sensor.track = function(eventName, properties) {
if (eventName.includes('video') || eventName.includes('duration') || eventName.includes('watch')) {
const currentProgress = Math.round(video.currentTime);
properties.duration = currentProgress;
properties.watch_time = currentProgress;
properties.effective_time = currentProgress; // 新增可能字段
console.log(`🔄 篡改埋点[${eventName}]:${currentProgress}秒`);
}
return originalTrack.call(this, eventName, properties);
};
}

// 拦截百度统计_hmt.push
if (sensor.push && Array.isArray(sensor)) {
const originalPush = sensor.push;
sensor.push = function(args) {
if (args[0] === '_trackEvent' && (args[2] === 'video_watch' || args[2] === 'duration')) {
args[3] = Math.round(video.currentTime); // 篡改时长参数
console.log(`🔄 篡改百度统计:${args[3]}秒`);
}
return originalPush.call(this, args);
};
}
});
console.log('✅ 扩展埋点拦截:覆盖所有常见工具');
};

// 6. 强化上报请求拦截(补充所有可能的字段名)
const interceptAllReports = () => {
if (!video) return;

const origXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new origXHR();
const origOpen = xhr.open;
const origSend = xhr.send;

xhr.open = function(method, url) {
// 扩展上报URL关键词:增加api、data、upload等
this.isReport = /report|log|duration|watch|complete|api|data|upload/.test(url);
return origOpen.apply(this, arguments);
};

xhr.send = function(data) {
if (this.isReport && data) {
try {
let payload = JSON.parse(data);
const currentProgress = Math.round(video.currentTime);
// 补充所有可能的时长字段名(平台常用)
const durationKeys = [
'watchDuration', 'duration', 'time', 'spent', 'realDuration',
'viewDuration', 'effectiveTime', 'watch_time', 'play_time', 'learning_time'
];
durationKeys.forEach(key => {
if (payload.hasOwnProperty(key)) {
payload[key] = currentProgress;
}
});
// 确保jumpToTime和进度同步
if (payload.jumpToTime) payload.jumpToTime = currentProgress;
// 补充进度比例字段(平台可能用比例判断)
if (payload.progressRatio || payload.finishRatio) {
const totalDuration = player.j2s_getDuration();
payload.progressRatio = Math.min(1.0, currentProgress / totalDuration);
payload.finishRatio = payload.progressRatio;
}
console.log(`🔄 强化篡改上报:时长=${currentProgress},进度比例=${payload.progressRatio || '1.0'}`);
data = JSON.stringify(payload);
} catch (e) {
// 处理FormData格式(平台可能用表单上报)
if (data instanceof FormData) {
const currentProgress = Math.round(video.currentTime);
const durationKeys = ['watchDuration', 'duration', 'time', 'jumpToTime'];
durationKeys.forEach(key => {
if (data.has(key)) {
data.set(key, currentProgress);
}
});
console.log(`🔄 篡改FormData上报:时长=${currentProgress}`);
} else {
console.log('⚠️ 上报请求体非JSON/FormData,跳过篡改');
}
}
}
return origSend.apply(this, [data]);
};
return xhr;
};

// 拦截Fetch请求(补充)
const originalFetch = window.fetch;
window.fetch = async function(url, options) {
if (/report|log|duration|watch|complete|api|data|upload/.test(url) && options?.method === 'POST' && options?.body) {
let body = options.body;
try {
let payload = {};
if (options.headers?.get('Content-Type')?.includes('json') || typeof body === 'string') {
const bodyStr = typeof body === 'string' ? body : await body.text();
payload = JSON.parse(bodyStr);
} else if (body instanceof FormData) {
for (const [key, value] of body.entries()) {
payload[key] = value;
}
}

const currentProgress = Math.round(video.currentTime);
const durationKeys = [
'watchDuration', 'duration', 'time', 'spent', 'realDuration',
'viewDuration', 'effectiveTime', 'watch_time', 'jumpToTime'
];
durationKeys.forEach(key => {
if (payload[key]) payload[key] = currentProgress;
});

if (options.headers?.get('Content-Type')?.includes('json') || typeof body === 'string') {
options.body = JSON.stringify(payload);
} else if (body instanceof FormData) {
for (const [key, value] of Object.entries(payload)) {
body.set(key, value);
}
options.body = body;
}
console.log(`🔄 篡改Fetch上报:时长=${currentProgress}`);
} catch (e) {
console.log('⚠️ Fetch上报篡改失败');
}
}
return originalFetch.apply(this, arguments);
};

console.log('✅ 强化上报拦截:覆盖JSON/FormData + 所有字段');
};

// 7. 确保页面停留时长与视频进度匹配(防后端校验)
const syncPageStayTime = () => {
// 篡改document.visibilityState(页面可见性)
Object.defineProperty(document, 'visibilityState', {
get: () => 'visible',
configurable: true
});
Object.defineProperty(document, 'hidden', {
get: () => false,
configurable: true
});

// 拦截页面失焦事件
const originalAddEventListener = document.addEventListener;
document.addEventListener = function(event) {
if (event === 'visibilitychange' || event === 'blur' || event === 'pagehide') {
console.log(`✅ 拦截页面${event}事件`);
return;
}
return originalAddEventListener.apply(this, arguments);
};

// 模拟页面活跃(定时触发交互事件)
setInterval(() => {
const event = new MouseEvent('mousemove', {
clientX: Math.random() * window.innerWidth,
clientY: Math.random() * window.innerHeight,
bubbles: true
});
document.dispatchEvent(event);
}, 5000); // 每5秒模拟一次鼠标移动

console.log('✅ 同步页面停留时长:页面永久活跃');
};

// 8. 视频播放完成后主动触发完整上报(兜底)
const triggerCompleteReport = () => {
video.addEventListener('ended', () => {
console.log('🎬 视频播放完毕,主动触发完整上报');
const currentProgress = Math.round(video.currentTime);
const totalDuration = player.j2s_getDuration();

// 1. 主动调用平台可能的上报函数
if (window.reportVideoComplete) {
window.reportVideoComplete({
watchDuration: currentProgress,
duration: totalDuration,
progressRatio: 1.0,
jumpToTime: currentProgress,
status: 'complete'
});
}

// 2. 模拟一次POST上报请求(如果知道平台上报URL)
if (window.player && window.player.reportUrl) {
fetch(window.player.reportUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
videoId: window.player.videoId,
watchDuration: currentProgress,
totalDuration: totalDuration,
progressRatio: 1.0,
jumpToTime: currentProgress,
finish: true
})
}).then(res => console.log('✅ 主动上报完成:', res.status));
}
});
};

// 9. 原倍速推进逻辑(保留)
const startForceRate = () => {
if (!video) {
video = player.HTML5 || (player.flash ? document.querySelector('video') : null);
if (!video) {
console.error('❌ 未找到视频元素,无法推进进度');
return;
}
}

video.addEventListener('play', () => {
isPlaying = true;
fakeTimeStartTime = performance.now();
lastProgressTime = performance.now();
console.log('▶️ 视频播放,启动4倍速推进');
});

video.addEventListener('pause', () => {
isPlaying = false;
console.log('⏸️ 视频暂停,暂停倍速推进');
});

const forceProgress = () => {
if (!isPlaying || video.paused) return;

const now = performance.now();
const timeSinceLastProgress = (now - lastProgressTime) / 1000;
lastProgressTime = now;

const targetRate = TARGET_RATE;
const realTime = originalPlayerTime ? originalPlayerTime() : video.currentTime;
if (isNaN(realTime)) return;

const step = timeSinceLastProgress * targetRate;
const targetTime = realTime + step;

if (targetTime >= video.duration) {
video.currentTime = video.duration;
isPlaying = false;
console.log('⏹️ 视频播放完毕');
return;
}

video.currentTime = targetTime;
requestAnimationFrame(forceProgress);
};

if (!video.paused) {
isPlaying = true;
fakeTimeStartTime = performance.now();
lastProgressTime = performance.now();
forceProgress();
}
requestAnimationFrame(forceProgress);
console.log(`🚀 已启动 ${TARGET_RATE}x 倍速进度推进(精准控制)`);
};

// 10. 主流程(整合所有优化)
const init = () => {
player = findPlayer();
if (!player) {
console.error('❌ 未找到支持 Hook 的播放器实例');
video = document.querySelector('video');
if (video) startForceRate();
return;
}

console.log('✅ 找到播放器实例:', player);
hookPlayerTime(); // 1. Hook时间方法
hookJumpToTime(); // 2. 篡改快进检测
handleKeyNodes(); // 3. 处理关键节点提示
startForceRate(); // 4. 启动4倍速
interceptAllSensors(); // 5. 扩展埋点拦截
interceptAllReports(); // 6. 强化上报拦截
syncPageStayTime(); // 7. 同步页面停留时长
triggerCompleteReport(); // 8. 播放完成主动上报
console.log('🎉 所有优化已加载:4倍速 + 全检测点覆盖');
};

// 启动脚本
if (document.readyState === 'complete' || document.readyState === 'interactive') {
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
```

답글 게시

답글을 게시하려면 로그인하세요.