"ui";
var config = storages.create("douyin_home_skip");
var isPause = true;
var running = true;
// 重置全局状态
function resetState() {
isPause = true;
}
function qdui() {
ui.layout(
<ScrollView>
<vertical>
<appbar>
<horizontal>
<toolbar id="bt" title="抖音长视频跳过" gravity="center" textColor="red" layout_weight="1" />
<Switch id="autoService" text="无障碍" checked="{{auto.service != null}}" padding="20 20 8 8" textSize="15sp" layout_gravity="right" />
</horizontal>
</appbar>
<text text="主页完整观看设置:" textSize="16sp" textColor="red" margin="10"/>
<horizontal padding="5">
<checkbox id="homeOverTime" text="超过 秒 上滑(长视频跳过)" checked="true" />
<input id="homeOverTimeVal" hint="120" w="80" gravity="center" inputType="number" />
<text text="秒" />
</horizontal>
<text text="温馨提示:" gravity="left" textColor="red" margin="10 5"/>
<text text="1. 仅底部30%区域识别时长" gravity="left" margin="2"/>
<text text="2. 长视频(>设定值)立即跳过" gravity="left" margin="2"/>
<text text="3. 短视频完整播放" gravity="left" margin="2"/>
<text text="4. 图文内容无进度条直接上滑" gravity="left" margin="2"/>
<text text="5. 悬浮窗:上暂停/开始,下停止" gravity="left" margin="2"/>
<button id="start" text="开始运行" style="Widget.AppCompat.Button.Colored" margin="15"/>
</vertical>
</ScrollView>
);
ui.autoService.on("check", function (checked) {
if (checked && auto.service == null) {
app.startActivity({ action: "android.settings.ACCESSIBILITY_SETTINGS" });
}
if (!checked && auto.service != null) {
auto.service.disableSelf();
}
});
readconfig();
ui.homeOverTimeVal.setVisibility(ui.homeOverTime.checked ? 0 : 8);
ui.homeOverTime.on("check", function (checked) {
ui.homeOverTimeVal.setVisibility(checked ? 0 : 8);
});
ui.emitter.on("resume", function () {
ui.autoService.checked = auto.service != null;
});
ui.start.on("click", function () {
if (auto.service == null) {
toast("请先开启无障碍服务!");
return;
}
if (!ui.homeOverTime.checked) {
toast("请勾选「超过秒上滑」模式");
return;
}
saveconfig();
resetState();
createFloatWindow();
runDouyinSkip();
});
}
// ==============================================
// 只修改这里:修复暂停按钮没反应,其他全保留原版
// ==============================================
var window;
function createFloatWindow() {
try {
window = floaty.window(
<vertical bg="#88000000" padding="5">
<button id="pauseBtn" text="▶" width="50" height="40" color="#FFFFFF" />
<button id="stopBtn" text="■" width="50" height="40" color="#FFFFFF" />
</vertical>
);
window.setPosition(10, device.height / 3);
setInterval(function(){}, 1000);
// 修复:暂停点击立即生效、重置状态
window.pauseBtn.click(function(){
isPause = !isPause;
if(isPause){
window.pauseBtn.setText("▶");
toast("已暂停");
}else{
window.pauseBtn.setText("⏸");
toast("已恢复运行");
}
});
window.stopBtn.click(function(){
toast("脚本已停止");
running = false;
resetState();
exit();
});
} catch (e) {
toast("请开启悬浮窗权限");
}
}
function parseTotalDuration(text) {
if (!text) return null;
var match = text.match(/\/\s(\d+):(\d+)/);
if (match) {
var m = parseInt(match[1]);
var s = parseInt(match[2]);
if (!isNaN(m) && !isNaN(s) && s < 60) {
return m 60 + s;
}
}
match = text.match(/^(\d+):(\d+)$/);
if (match) {
var m = parseInt(match[1]);
var s = parseInt(match[2]);
if (!isNaN(m) && !isNaN(s) && s < 60) {
return m * 60 + s;
}
}
return null;
}
function hasSeekBar() {
try {
return className("android.widget.SeekBar").findOne(500) != null;
} catch(e) {
return false;
}
}
// 只在屏幕底部 30% 区域查找
function scanForDuration(durationMs) {
let screenH = device.height;
let areaTop = screenH * 0.7;
let areaBottom = screenH;
var start = new Date().getTime();
while (new Date().getTime() - start < durationMs) {
if(isPause) return null;
var items = className("android.widget.TextView").find();
for (var i = 0; i < items.length; i++) {
let item = items[i];
let bounds = item.bounds();
if(bounds.centerY() >= areaTop && bounds.centerY() <= areaBottom){
var t = item.text();
if (t) {
var sec = parseTotalDuration(t);
if (sec && sec > 0) {
log("底部区域识别时长:" + sec + "秒");
return sec;
}
}
}
}
sleep(40);
}
return null;
}
function getVideoDuration() {
try {
if (isPause) return null;
if (!hasSeekBar()) return null;
var bar = className("android.widget.SeekBar").findOne(1500);
if (!bar) return null;
var b = bar.bounds();
var cx = b.centerX();
var cy = b.centerY();
var lx = b.left + 40;
var res = null;
// 扫描时长:2300ms
var th = threads.start(() => {
res = scanForDuration(2300);
});
// 按压时长:2100ms
if(!isPause){
gesture(2100, [cx, cy], [lx, cy]);
}
th.join();
return res;
} catch (e) {
log("获取时长异常:" + e);
return null;
}
}
function getVideoDurationWithRetry() {
log("开始识别视频时长...");
var t = getVideoDuration();
if (t) return t;
sleep(800);
log("重试识别...");
return getVideoDuration();
}
function runDouyinSkip() {
threads.start(function () {
console.show();
isPause = false;
app.launchApp("抖音");
sleep(4000);
skipLoop();
});
}
function skipLoop() {
var count = 0;
while (running) {
if (isPause) {
sleep(300);
continue;
}
count++;
var limit = parseInt(ui.homeOverTimeVal.text()) || 120;
log("\n======= 第" + count + "个视频 =======");
// 进入新视频随机等待 500~1000ms
sleep(random(500, 1000));
if (!hasSeekBar()) {
log("图文内容,直接上滑");
swipeUp();
continue;
}
var sec = getVideoDurationWithRetry();
if (sec && sec > 0) {
log("视频时长:" + sec + "秒 / 限制:" + limit + "秒");
if (sec > limit) {
log("长视频,立即跳过");
swipeUp();
} else {
var play = sec + 1;
log("短视频,播放 " + play + " 秒");
sleep(play * 1000);
log("播放完毕,上滑");
swipeUp();
}
} else {
log("识别失败,10秒后跳过");
sleep(10000);
swipeUp();
}
}
}
// 随机滑动坐标 + 随机手势时长
function swipeUp() {
if(isPause) return;
var baseX = device.width / 2;
var randomX = baseX + random(-10, 10);
var startY = device.height 0.75 + random(-10, 10);
var endY = device.height 0.25 + random(-10, 10);
var duration = random(300, 500);
swipe(randomX, startY, randomX, endY, duration);
sleep(700);
}
function saveconfig() {
config.put("homeOverTime", ui.homeOverTime.checked);
config.put("homeOverTimeVal", ui.homeOverTimeVal.text());
}
function readconfig() {
ui.homeOverTime.checked = config.get("homeOverTime", true);
ui.homeOverTimeVal.setText(config.get("homeOverTimeVal", "120"));
}
qdui();