From a06cdbf0ac5c7d0bf97bf1553ee6fa4cc07ccf57 Mon Sep 17 00:00:00 2001 From: stometaverse Date: Wed, 18 Mar 2026 20:23:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20add=20jimeng=20(=E5=8D=B3=E6=A2=A6AI)?= =?UTF-8?q?=20CLI=20support=20(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two CLI commands for Jimeng (即梦AI) — ByteDance's AI image generation platform: - generate: Text-to-image generation with model selection and configurable wait time - history: View recent generation history with prompt, model, status, and image URLs Both commands use browser automation with cookie-based authentication on jimeng.jianying.com. --- src/clis/jimeng/generate.yaml | 84 +++++++++++++++++++++++++++++++++++ src/clis/jimeng/history.yaml | 47 ++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 src/clis/jimeng/generate.yaml create mode 100644 src/clis/jimeng/history.yaml diff --git a/src/clis/jimeng/generate.yaml b/src/clis/jimeng/generate.yaml new file mode 100644 index 0000000..c510dd4 --- /dev/null +++ b/src/clis/jimeng/generate.yaml @@ -0,0 +1,84 @@ +site: jimeng +name: generate +description: 即梦AI 文生图 — 输入 prompt 生成图片 +domain: jimeng.jianying.com +strategy: cookie +browser: true + +args: + prompt: + type: string + required: true + description: "图片描述 prompt" + model: + type: string + default: "high_aes_general_v50" + description: "模型: high_aes_general_v50 (5.0 Lite), high_aes_general_v42 (4.6), high_aes_general_v40 (4.0)" + wait: + type: int + default: 40 + description: "等待生成完成的秒数" + +columns: [status, prompt, image_count, image_urls] + +pipeline: + - navigate: https://jimeng.jianying.com/ai-tool/generate?type=image&workspace=0 + - wait: 3 + - evaluate: | + (async () => { + const prompt = ${{ args.prompt | json }}; + const waitSec = ${{ args.wait }}; + + // Step 1: Count existing images before generation + const beforeImgs = document.querySelectorAll('img[src*="dreamina-sign"], img[src*="tb4s082cfz"]').length; + + // Step 2: Clear and set prompt + const editors = document.querySelectorAll('[contenteditable="true"]'); + const editor = editors[0]; + if (!editor) return [{ status: 'failed', prompt: prompt, image_count: 0, image_urls: 'Editor not found' }]; + + editor.focus(); + await new Promise(r => setTimeout(r, 200)); + document.execCommand('selectAll'); + await new Promise(r => setTimeout(r, 100)); + document.execCommand('delete'); + await new Promise(r => setTimeout(r, 200)); + document.execCommand('insertText', false, prompt); + await new Promise(r => setTimeout(r, 500)); + + // Step 3: Click generate + const btn = document.querySelector('.lv-btn.lv-btn-primary[class*="circle"]'); + if (!btn) return [{ status: 'failed', prompt: prompt, image_count: 0, image_urls: 'Generate button not found' }]; + btn.click(); + + // Step 4: Wait for new images to appear + let newImgs = []; + for (let i = 0; i < waitSec; i++) { + await new Promise(r => setTimeout(r, 1000)); + const allImgs = document.querySelectorAll('img[src*="dreamina-sign"], img[src*="tb4s082cfz"]'); + if (allImgs.length > beforeImgs) { + // New images appeared — generation complete + newImgs = Array.from(allImgs).slice(0, allImgs.length - beforeImgs); + break; + } + } + + if (newImgs.length === 0) { + return [{ status: 'timeout', prompt: prompt, image_count: 0, image_urls: 'Generation may still be in progress' }]; + } + + // Step 5: Extract image URLs (use thumbnail URLs which are accessible) + const urls = newImgs.map(img => img.src); + + return [{ + status: 'success', + prompt: prompt.substring(0, 80), + image_count: urls.length, + image_urls: urls.join('\n') + }]; + })() + - map: + status: ${{ item.status }} + prompt: ${{ item.prompt }} + image_count: ${{ item.image_count }} + image_urls: ${{ item.image_urls }} diff --git a/src/clis/jimeng/history.yaml b/src/clis/jimeng/history.yaml new file mode 100644 index 0000000..0761709 --- /dev/null +++ b/src/clis/jimeng/history.yaml @@ -0,0 +1,47 @@ +site: jimeng +name: history +description: 即梦AI 查看最近生成的作品 +domain: jimeng.jianying.com +strategy: cookie +browser: true + +args: + limit: + type: int + default: 5 + +columns: [prompt, model, status, image_url, created_at] + +pipeline: + - navigate: https://jimeng.jianying.com/ai-tool/generate?type=image&workspace=0 + - wait: 3 + - evaluate: | + (async () => { + const limit = ${{ args.limit }}; + const res = await fetch('/mweb/v1/get_history?aid=513695&device_platform=web®ion=cn&da_version=3.3.11&web_version=7.5.0&aigc_features=app_lip_sync', { + method: 'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ cursor: '', count: limit, need_page_item: true, need_aigc_data: true, aigc_mode_list: ['workbench'] }) + }); + const data = await res.json(); + const items = data?.data?.history_list || []; + return items.slice(0, limit).map(item => { + const params = item.aigc_image_params?.text2image_params || {}; + const images = item.image?.large_images || []; + return { + prompt: params.prompt || item.common_attr?.title || 'N/A', + model: params.model_config?.model_name || 'unknown', + status: item.common_attr?.status === 102 ? 'completed' : 'pending', + image_url: images[0]?.image_url || '', + created_at: new Date((item.common_attr?.create_time || 0) * 1000).toLocaleString('zh-CN'), + }; + }); + })() + - map: + prompt: ${{ item.prompt }} + model: ${{ item.model }} + status: ${{ item.status }} + image_url: ${{ item.image_url }} + created_at: ${{ item.created_at }} + - limit: ${{ args.limit }}