|
|
资讯缩略图:
资讯发布日期:2026-10-15
资讯发布简介:找图方式快手发视频全流程
资讯关键词:找图方式快手发视频全流程
资讯所属分类:IT资讯 SEO资讯
联系:
① 本信息收集于网络,如有不对的地方欢迎联系我纠正! ② 本信息免费收录,不存在价格的问题! ③ 如果您的网站也想这样出现在这里,请您加好友情链接,我当天会审核通过!
④友情链接关键字:软件网站分类目录 网址:http://www.postbbs.com/
资讯详细描述
 | |  |  | 找图方式快手发视频全流程
- /**
- * 案例 10:快手发视频全流程(固定 .cv 五连:加号→录制→下一步→填标题→发布)
- *
- * 只教一件事:一条发视频流水线怎么用图色工程串起来——
- * 每步都是:findImagesEx 找 .cv → 中心坐标点击 → 等加载 → 下一步。
- * 录制是"点一下开始 → 等 20 秒 → 再点一下停止",不是长按。
- *
- * 用到的 .cv(放进项目 资源 文件夹):
- * 点击加号.cv —— 底部中间 "+" 发布入口
- * 按住录制.cv —— 录制按钮(点击开始 → 录 20 秒 → 点击停止)
- * 录完下一步.cv —— 录制完成后的"下一步"
- * 点击OCR文字粘帖内容.cv —— 标题输入区(OCREx 定位后点击输入)
- * 点击发布按钮.cv —— 发布
- *
- * 运行:真机/模拟器 + 无障碍,不用 HID;LOOP_COUNT 控制循环发几次。
- */
- // ==================== 配置(改这里)====================
- var PKG = "com.smile.gifmaker"; // 快手
- var VIDEO_TITLE = "AIWROK 自动发布测试"; // 每次发布的标题
- var RECORD_MS = 20000; // 录制时长:点击开始后录 20 秒再点停止
- var LOOP_COUNT = 2; // 循环发几次
- var STEP_WAIT = 3000; // 每步点完等页面加载(ms)
- // ==================== 全局变量放顶部 ====================
- var gStep = 0;
- // ==================== 工具 ====================
- function doSleep(ms) { sleep.millisecond(ms); }
- function stepTitle(name) {
- gStep++;
- printl("========== " + gStep + ") " + name + " ==========");
- }
- // ==================== 找图 + 点击(核心套路,全片复用)====================
- // 点击用 detect.click() —— 实测这个能真正点中(auto.clickPoint 点不中)
- function findAndClick(cvFile, waitMs) {
- var detects = opencv.findImagesEx(cvFile);
- if (detects != null && detects.length > 0) {
- printl("[找图] " + cvFile + " 命中 " + detects.length + " 个");
- printl(detects);
- detects[0].click();
- doSleep(waitMs == null ? STEP_WAIT : waitMs);
- return true;
- }
- printl("[找图] 未找到 " + cvFile);
- return false;
- }
- // 带重试的找图点击:转场/处理耗时的步骤用这个,maxT 次没找到才放弃
- function findAndClickRetry(cvFile, maxT, intervalMs, waitMs) {
- for (var i = 0; i < maxT; i++) {
- var detects = opencv.findImagesEx(cvFile);
- if (detects != null && detects.length > 0) {
- printl("[找图] " + cvFile + " 命中 " + detects.length + " 个(第 " + (i + 1) + " 次)");
- printl(detects);
- detects[0].click();
- doSleep(waitMs == null ? STEP_WAIT : waitMs);
- return true;
- }
- printl("[找图] " + cvFile + " 未找到,重试 " + (i + 1) + "/" + maxT);
- doSleep(intervalMs);
- }
- printl("[找图] ❌ " + cvFile + " 重试 " + maxT + " 次仍未找到");
- return false;
- }
- // ==================== 录制:点魔法 → 收面板 → 点击开始 → 等 20 秒 → 点击停止 ====================
- function holdRecord() {
- // 先点一次"魔法"(拍摄页左侧,有特效才录得成)
- var ocr1 = opencv.OCREx('魔法.cv');
- if (ocr1 != null) {
- printl("[魔法] 识别到: " + ocr1.getAllString());
- var magicAll = ocr1.getAllDetect();
- if (magicAll != null && magicAll.length > 0) {
- magicAll[0].click();
- doSleep(2000);
- // 收起魔法面板:点用户实测有效的空白位置(百分比坐标,自动适配分辨率)
- printl("[魔法] 点空白收起面板 (0.5283, 0.3292)");
- auto.clickPercent(0.5283, 0.3292);
- doSleep(1500);
- }
- } else {
- printl("[魔法] 魔法.cv 未命中,跳过,直接录制");
- }
- // 再倒计时缓冲,等拍摄页加载完再去点(不然找太快,页面没出来点不到)
- for (var s = 3; s >= 1; s--) {
- printl("[录制] " + s + " 秒后开始录制...");
- doSleep(1000);
- }
- // 找录制按钮,最多试 5 次(页面还在加载时可能第一下找不到)
- var detects = null;
- for (var t = 0; t < 5; t++) {
- detects = opencv.findImagesEx("按住录制.cv");
- if (detects != null && detects.length > 0) break;
- printl("[录制] 未找到 按住录制.cv,重试 " + (t + 1) + "/5");
- doSleep(1500);
- }
- if (detects == null || detects.length == 0) {
- printl("[找图] 未找到 按住录制.cv");
- return false;
- }
- // 页面刚从拍摄页切换过来可能还在动画,找到按钮后再稳 2 秒,让页面完全静止再点
- printl("[录制] 找到录制按钮,等 2 秒页面静止后点击...");
- doSleep(2000);
- // —— 用户实测可用的点击代码,原样嵌入 ——
- //找图返回目标数组
- var detects = opencv.findImagesEx('按住录制.cv');
- if (detects != null) {
- printl(detects);
- detects[0].click();
- }
- // 蒙层/弹窗可能吃掉第一次点击,隔 1 秒补点一次(没弹窗时录制刚开始,
- // 第二次点击会被录制中的按钮忽略,无副作用)
- doSleep(1000);
- var d1 = opencv.findImagesEx('按住录制.cv');
- if (d1 != null) {
- d1[0].click();
- printl("[录制] ✅ 已点击,开始录制 " + (RECORD_MS / 1000) + " 秒");
- } else {
- printl("[录制] ✅ 已点击(按钮已变录制态,第一次就生效)");
- }
- // 等待录制时长(期间显示剩余秒数)
- var waited = 0;
- while (waited < RECORD_MS) {
- doSleep(1000);
- waited += 1000;
- var left = Math.ceil((RECORD_MS - waited) / 1000);
- if (left % 5 == 0 || left <= 3) {
- printl("[录制] 还剩 " + left + " 秒");
- }
- }
- // 录完点停止:找到就点,找不到直接跳过
- var d2 = opencv.findImagesEx("按住录制.cv");
- if (d2 != null && d2.length > 0) {
- printl("[停止] 找到停止按钮");
- d2[0].click();
- printl("[录制] ✅ 已停止录制");
- } else {
- printl("[停止] 没找到,跳过");
- }
- doSleep(2500); // 等录制结算/转场
- return true;
- }
- // ==================== 填标题:OCREx 定位输入区 → 点击 → 输入 ====================
- function fillTitleByOcr() {
- var ocr1 = opencv.OCREx("点击OCR文字粘帖内容.cv");
- if (ocr1 == null) {
- printl("[OCR] 点击OCR文字粘帖内容.cv 未命中");
- return false;
- }
- printl("[OCR] 识别到: " + ocr1.getAllString());
- // 拿第一个识别目标的中心坐标点击输入区(ocrResult 无 getRect,必须 getAllDetect 拿 detect[])
- var all = ocr1.getAllDetect();
- if (all == null || all.length == 0) {
- printl("[OCR] 有结果但拿不到目标坐标");
- return false;
- }
- var det = all[0];
- det.click(); // detect.click 实测能点中
- printl("[点击] 输入区");
- doSleep(1500); // 等键盘弹出
- // 输入标题:auto 没有 inputText 方法,只能走节点级 node.input()(项目里已验证的写法)
- // 快手标题框可能不是标准 EditText,四路尝试,全失败时打印节点树诊断
- var ok = inputViaNode(VIDEO_TITLE);
- if (!ok) return false;
- doSleep(800);
- auto.back(); // 收起键盘
- doSleep(800);
- return true;
- }
- // ==================== 节点输入:四路尝试(auto 没有 inputText,只有 node.input 能用)====================
- function inputViaNode(text) {
- // 路线1:标准 EditText
- if (tryInput(2000, "android.widget.EditText", text, "路线1 EditText")) return true;
- // 路线2:任意可编辑控件(快手可能是自定义输入框)
- if (tryInputEditable(text, "路线2 editable")) return true;
- // 路线3:按占位文字找节点 → focus 激活后再输入
- if (tryInputByHint(text, "路线3 按占位文字")) return true;
- // 路线4:剪贴板粘贴到可编辑节点
- if (tryClipboardInput(text, "路线4 剪贴板粘贴")) return true;
- // 四路全失败:打印可编辑节点树,贴日志给 AI 看就知道输入框长什么样
- printl("[输入] ❌ 四路全部失败,诊断节点树:");
- try {
- var ed = auto.nodeSearch(1500).editable(true);
- if (ed != '' && ed.length > 0) {
- printl("[输入] 可编辑节点数: " + ed.length);
- printl("[输入] XML: " + ed.getXmls());
- } else {
- printl("[输入] 页面上没有 editable 节点 → 点击没把焦点给到输入框");
- printl("[输入] 页面XML片段: " + auto.getXml());
- }
- } catch (e) { printl("[输入] 诊断失败: " + e); }
- return false;
- }
- // 按 className 找节点输入
- function tryInput(timeout, cls, text, tag) {
- try {
- var nodesObj = auto.nodeSearch(timeout).className(cls);
- if (nodesObj == '' || nodesObj.length == 0) {
- printl("[输入] " + tag + ": 没找到 " + cls);
- return false;
- }
- var nodeObj = nodesObj.getNode(0);
- if (nodeObj.isEditable()) {
- nodeObj.input(text);
- printl("[输入] ✅ " + tag + " 成功: " + text);
- doSleep(800);
- return true;
- }
- printl("[输入] " + tag + ": 节点不可编辑");
- } catch (e) {
- printl("[输入] " + tag + " 异常: " + e);
- }
- return false;
- }
- // 按 editable 过滤找节点输入
- function tryInputEditable(text, tag) {
- try {
- var nodesObj = auto.nodeSearch(2000).editable(true);
- if (nodesObj == '' || nodesObj.length == 0) {
- printl("[输入] " + tag + ": 没有 editable 节点");
- return false;
- }
- var nodeObj = nodesObj.getNode(0);
- nodeObj.focus(); // 先聚焦
- doSleep(300);
- nodeObj.input(text);
- printl("[输入] ✅ " + tag + " 成功: " + text);
- doSleep(800);
- return true;
- } catch (e) {
- printl("[输入] " + tag + " 异常: " + e);
- }
- return false;
- }
- // 按占位文字找节点 → focus → input
- function tryInputByHint(text, tag) {
- try {
- var nodesObj = auto.nodeSearch(2000).textIncludes("添加合适");
- if (nodesObj == '' || nodesObj.length == 0) {
- printl("[输入] " + tag + ": 没找到占位文字节点");
- return false;
- }
- var nodeObj = nodesObj.getNode(0);
- nodeObj.focus();
- doSleep(300);
- nodeObj.input(text);
- printl("[输入] ✅ " + tag + " 成功: " + text);
- doSleep(800);
- return true;
- } catch (e) {
- printl("[输入] " + tag + " 异常: " + e);
- }
- return false;
- }
- // 剪贴板粘贴到可编辑节点
- function tryClipboardInput(text, tag) {
- try {
- var nodesObj = auto.nodeSearch(2000).editable(true);
- if (nodesObj == '' || nodesObj.length == 0) {
- printl("[输入] " + tag + ": 没有 editable 节点");
- return false;
- }
- var nodeObj = nodesObj.getNode(0);
- nodeObj.focus();
- Clipboard.copy(text);
- doSleep(300);
- nodeObj.clipboardInput(text);
- printl("[输入] ✅ " + tag + " 成功: " + text);
- doSleep(800);
- return true;
- } catch (e) {
- printl("[输入] " + tag + " 异常: " + e);
- }
- return false;
- }
- // ==================== 关弹窗:打开后弹窗拦路,找到就点掉,没弹窗跳过 ====================
- function dismissPopup() {
- var detects = opencv.findImagesEx("弹窗点取消.cv");
- if (detects == null || detects.length == 0) {
- printl("[弹窗] 无弹窗");
- return false;
- }
- printl("[弹窗] 发现弹窗,点取消");
- printl(detects);
- detects[0].click(); // detect.click 实测能点中
- doSleep(1500);
- return true;
- }
- // ==================== 单次发视频流程 ====================
- function publishOnce(loopNo) {
- printl("╔════ 第 " + loopNo + "/" + LOOP_COUNT + " 次发布开始 ════╗");
- // 0. 重启打开快手(先关干净再开,避免残留上次状态)
- stepTitle("重启快手");
- try { app.closeApp(PKG); doSleep(1500); } catch (e) { printl("[重启] 关闭失败(可能没开): " + e); }
- app.openApp(PKG);
- printl("[重启] 已重新打开快手");
- doSleep(5000); // 等启动 + 开屏广告
- // 0.5 关弹窗(有就关,最多试 3 次,弹窗可能一层套一层)
- stepTitle("关弹窗");
- for (var t = 0; t < 3; t++) {
- if (!dismissPopup()) break;
- }
- // 1. 点加号进拍摄页
- stepTitle("点 + 进拍摄页");
- if (!findAndClick("点击加号.cv")) return false;
- dismissPopup(); // 点加号后偶尔还会弹,顺手关一下
- // 2. 点击录制 20 秒
- stepTitle("点击录制 20 秒");
- if (!holdRecord()) return false;
- // 3. 录完点下一步(停止→编辑页有转场+视频处理耗时,必须重试,单次找不到不能中止)
- stepTitle("录完点下一步");
- if (!findAndClickRetry("录完下一步.cv", 8, 1500, 4000)) {
- printl("[中止] 录完下一步 8 次都没找到,第 " + loopNo + " 次流程中断");
- return false;
- }
- // 4. 填标题(OCREx 定位输入区)
- stepTitle("填标题");
- fillTitleByOcr(); // 填不了不拦路,继续发布
- // 5. 点发布
- stepTitle("点发布");
- if (!findAndClick("点击发布按钮.cv", 5000)) return false;
- // 6. 确认弹窗:发布后会弹"继续"确认,不点会发布失败
- stepTitle("点继续");
- clickContinue();
- // 7. 进"我"的主页,删除刚发布的视频
- stepTitle("删除刚发布的视频");
- deleteLastVideo();
- printl("✅ 第 " + loopNo + " 次发布流程完成");
- try { toast.show("第 " + loopNo + " 次发布完成"); } catch (e) {}
- return true;
- }
- // ==================== 删除刚发布的视频:点"我" → 上滑 → 点第一个视频 → 弹出删除 → 确认删除 ====================
- function deleteLastVideo() {
- // 1) OCREx 点"我"
- var ocr1 = opencv.OCREx("点击我.cv");
- if (ocr1 == null) { printl("[删除] 点击我.cv 未命中,跳过删除"); return false; }
- printl("[删除] 点我: " + ocr1.getAllString());
- var all = ocr1.getAllDetect();
- if (all == null || all.length == 0) { printl("[删除] 识别到但没拿到坐标,跳过"); return false; }
- all[0].click();
- doSleep(4000); // 等个人主页加载
- // 2) 往上滑,露出作品网格
- printl("[删除] 往上滑露出作品");
- swipPercent(0.5, 0.8, 0.5, 0.35, 600);
- doSleep(2500);
- // 3) 点第一个视频(网格里第一个就是刚发布的)
- var first = findNodeWithWords(["作品", "草稿"]);
- if (first == null) {
- // 没有文字锚点就兜底:作品网格里的可点击格子
- first = auto.nodeSearch(2000).clickable(true);
- if (first == '' || first.length == 0) { printl("[删除] 没找到作品格子"); return false; }
- first = first.getNode(Math.min(3, first.length - 1));
- }
- first.click();
- doSleep(4500); // 等视频页打开并开始播放
- // 4) 点"更多"弹出删除菜单
- var detects = opencv.findImagesEx('弹出删除.cv');
- if (detects == null) { printl("[删除] 弹出删除.cv 未命中"); return false; }
- printl(detects);
- detects[0].click();
- doSleep(1800);
- // 5) 确认删除
- var ocr2 = opencv.OCREx('删除.cv');
- if (ocr2 == null) { printl("[删除] 删除.cv 未命中"); return false; }
- printl("[删除] 确认: " + ocr2.getAllString());
- var all2 = ocr2.getAllDetect();
- if (all2 == null || all2.length == 0) { printl("[删除] 识别到但没拿到坐标"); return false; }
- all2[0].click();
- doSleep(1800);
- // 6) 最后还会弹一次"再次确定删除"
- var ocr3 = opencv.OCREx('再次确定删除.cv');
- if (ocr3 != null) {
- printl("[删除] 再次确定: " + ocr3.getAllString());
- var all3 = ocr3.getAllDetect();
- if (all3 != null && all3.length > 0) {
- all3[0].click();
- doSleep(2500);
- }
- } else {
- printl("[删除] 没弹【再次确定删除】,跳过");
- }
- printl("✅ 已删除刚发布的视频");
- return true;
- }
- // 找第一个匹配任一关键词的节点(用于定位"作品"入口)
- function findNodeWithWords(words) {
- for (var w = 0; w < words.length; w++) {
- try {
- var nodesObj = auto.nodeSearch(1500).textIncludes(words[w]);
- if (nodesObj != '' && nodesObj.length > 0) return nodesObj.getNode(0);
- } catch (e) {}
- }
- return null;
- }
- // ==================== 发布后确认:OCREx 找"继续"并点击(弹窗可能延迟弹出,轮询多次)====================
- function clickContinue() {
- var clicked = false;
- for (var i = 0; i < 4; i++) {
- var ocr1 = opencv.OCREx("点继续.cv");
- if (ocr1 != null) {
- printl("[继续] 识别到: " + ocr1.getAllString());
- var all = ocr1.getAllDetect();
- if (all != null && all.length > 0) {
- all[0].click(); // detect.click 实测能点中
- printl("[继续] ✅ 已点击继续");
- clicked = true;
- doSleep(1500);
- break;
- }
- }
- if (i < 3) {
- printl("[继续] 暂无弹窗,等 2 秒重试 " + (i + 1) + "/3");
- doSleep(2000);
- }
- }
- if (!clicked) printl("[继续] 没有弹【继续】弹窗,跳过");
- return clicked;
- }
- // ==================== 清理(顺序不能变)====================
- function cleanup() {
- printl("[结束] 发视频流程执行完毕");
- runTime.stopAllRuning(); // 永远放最后
- stop();
- }
- event.onStopBtnEvent(function () { cleanup(); });
- // ==================== 主流程 ====================
- function runMain() {
- printl("╔════════════════════════════════════════╗");
- printl("║ 案例10:快手发视频(.cv 五连流程) ║");
- printl("╚════════════════════════════════════════╝");
- printl("[配置] 标题=" + VIDEO_TITLE + " 录制=" + (RECORD_MS / 1000) + "s 循环=" + LOOP_COUNT + " 次");
- for (var i = 1; i <= LOOP_COUNT; i++) {
- var ok = publishOnce(i);
- if (!ok) {
- printl("[中止] 第 " + i + " 次流程中断,停止循环");
- break;
- }
- if (i < LOOP_COUNT) {
- printl("[间隔] 等 5 秒后开始下一次...");
- doSleep(5000);
- }
- }
- cleanup();
- }
- runMain();
复制代码
| |  | |  |
|
untoclickPercent天然适配不同分辨率找控件点击nextnocontent
|