From d2692a4d291fd9d306fb9b0ae94f6c6619c270ff Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 10 Jun 2026 16:32:38 +0800 Subject: [PATCH] feat: implement xiakan content factory with openclaw installer and api verification --- plugin.json | 64 +++++++++++----------------- skills/xiakan/character.json | 4 ++ skills/xiakan/deadpan.json | 4 ++ skills/xiakan/director.json | 4 ++ skills/xiakan/narrative.json | 4 ++ src/__pycache__/cli.cpython-314.pyc | Bin 0 -> 2324 bytes src/cli.py | 41 ++++++++++++++++++ src/pipeline.py | 51 ++++++++++++++++++++++ src/utils/fetcher.py | 19 +++++++++ src/utils/llm_client.py | 43 +++++++++++++++++++ 10 files changed, 196 insertions(+), 38 deletions(-) create mode 100644 skills/xiakan/character.json create mode 100644 skills/xiakan/deadpan.json create mode 100644 skills/xiakan/director.json create mode 100644 skills/xiakan/narrative.json create mode 100644 src/__pycache__/cli.cpython-314.pyc create mode 100644 src/cli.py create mode 100644 src/pipeline.py create mode 100644 src/utils/fetcher.py create mode 100644 src/utils/llm_client.py diff --git a/plugin.json b/plugin.json index 2dbbf70..371efca 100644 --- a/plugin.json +++ b/plugin.json @@ -1,42 +1,30 @@ { - "name": "narrator-ai-cli-skill", - "version": "1.0.5", - "description": "AI电影解说视频自动生成技能(AI解说大师 CLI Skill)。当用户需要创建电影解说视频、短剧解说、影视二创、AI配音旁白视频、film commentary、video narration、drama dubbing、movie narration时触发。内置丰富电影素材、BGM、多语种配音音色、解说模板。通过narrator-ai-cli命令行工具实现:搜片选片→选择模板→选BGM→选配音→生成文案→合成视频的全流程自动化。CLI client for Narrator AI (AI解说大师) video narration API. Use when user needs to create AI narration videos, manage narration tasks, browse dubbing/BGM/material resources, or automate video production.", - "author": { - "name": "NarratorAI-Studio", - "email": "merlinyang@gridltd.com", - "url": "https://github.com/NarratorAI-Studio" - }, - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/NarratorAI-Studio/narrator-ai-cli-skill.git" - }, - "keywords": [ - "video-narration", - "film-commentary", - "ai-video", - "narrator-ai", - "short-drama", - "content-creation", - "dubbing", - "tts" - ], - "primaryEnv": "NARRATOR_APP_KEY", - "install": [ - { - "name": "narrator-ai-cli", - "type": "pip", - "spec": "narrator-ai-cli @ https://github.com/NarratorAI-Studio/narrator-ai-cli/archive/refs/tags/v1.0.0.zip" + "name": "xiakan-content-factory", + "version": "3.0.0", + "description": "一键将电影名称转化为《瞎看什么》风格的高密度脚本与结构化卡点剪辑JSON时间轴", + "author": "cosren", + "entrypoint": "src/cli.py", + "settings": { + "LLM_API_KEY": { + "type": "string", + "description": "请输入你的大模型 API 密钥 (API Key)", + "required": true, + "secret": true + }, + "LLM_BASE_URL": { + "type": "string", + "description": "请输入大模型 API 代理/基础路径", + "default": "https://api.deepseek.com/v1", + "required": true + }, + "LLM_MODEL": { + "type": "string", + "description": "请输入使用的模型名称 (如 deepseek-chat, qwen-max, gpt-4o-mini)", + "default": "deepseek-chat", + "required": true } - ], - "requires": { - "bins": ["narrator-ai-cli"], - "env": ["NARRATOR_APP_KEY"] }, - "skills": { - "narrator-ai-cli": { - "path": "SKILL.md" - } + "lifecycle": { + "on_install": "python3 -m src.cli verify" } -} +} \ No newline at end of file diff --git a/skills/xiakan/character.json b/skills/xiakan/character.json new file mode 100644 index 0000000..2b84e38 --- /dev/null +++ b/skills/xiakan/character.json @@ -0,0 +1,4 @@ +{ + "skill_name": "xiakan_character", + "system_prompt": "# Role\n你现在是【瞎看宇宙】的核心角色重塑引擎。\n\n# Instructions\n1. 必须完全抹去原片中所有的西方、历史、或复杂的地域背景(如:波黑、首尔、1950年纽约)。\n2. 强制执行中式符号化、职场化、市井化的人格映射:\n - 权力者/伪善反派/傲慢高管 -> 统一冠以复姓【欧阳】(如:欧阳正气、欧阳开太、欧阳帅)。\n - 颜值担当/男女主角 -> 统一命名为【阿帅 / 小美 / 兔兔】。\n - 底层执行者/蠢萌倒霉蛋 -> 统一命名为【阿呆 / 二柱子 / 闲不住】。\n3. **核心羁绊公式**:为每个被重塑的角色强行注入一个不可调和的黑色幽默悖论(格式必须为:‘虽然他[恶习/离谱行为],但他知道自己是个[正向词汇]的好男孩/好女孩’)。\n4. 脑补空间:允许你根据原片角色一个无助的延伸或道具穿帮,自由扩写其内心活动。" +} diff --git a/skills/xiakan/deadpan.json b/skills/xiakan/deadpan.json new file mode 100644 index 0000000..2f94357 --- /dev/null +++ b/skills/xiakan/deadpan.json @@ -0,0 +1,4 @@ +{ + "skill_name": "xiakan_deadpan", + "system_prompt": "# Role\n你现在是【瞎看宇宙】的冷面滑稽台词专家。\n\n# Instructions\n1. **语调基调**:冷峻、公文式、毫无感情波澜。大量运用官方、行政、或纪录片播音员的词汇(如:‘领导高度重视’、‘媒体密切关注’、‘深入开展缜密排查’)。\n2. 每一场戏的台词必须具有高字数密度(扩写至原本的3倍以上),以确保支撑10分钟的视频。\n3. 必须高频嵌入核心经典句式芯片:\n - 【打破尴尬】: ‘场面一度非常尴尬,好在[更离谱的外部暴力/枪战]爆发了,成功打破了尴尬。’\n - 【碰瓷归因】: ‘说出来你可能不信,是这具尸体/这辆摩托车先碰的瓷。’\n4. 结尾必须以一段毒鸡汤(消解奋斗价值、扎心打工人)进行虚无主义的升华。" +} diff --git a/skills/xiakan/director.json b/skills/xiakan/director.json new file mode 100644 index 0000000..c0523be --- /dev/null +++ b/skills/xiakan/director.json @@ -0,0 +1,4 @@ +{ + "skill_name": "xiakan_director", + "system_prompt": "# Role\n你现在是【瞎看宇宙】的视听反差声效总导演。\n\n# Instructions\n1. 将冷面解说词映射为机器可读的结构化JSON数组。\n2. 严格执行**视听错位矩阵**:\n - 画面极度惨烈、流血、送葬 -> 强制配置BGM为极其温柔老土的华语苦情歌(如《下雨天》、《爱的供养》)或韩流夜店电音。\n - 画面极其严肃、装逼 -> 强制配置BGM为低幼儿歌(如《爸爸的爸爸叫什么》)或广播体操。\n3. **对赌捧哏(Ducking)**:当背景音乐歌词出现疑问句时,设计旁白在200ms内冷酷接梗(如:歌词『下雨天了怎么办』 -> 旁白紧接:『大叔表示只能打伞』)。" +} diff --git a/skills/xiakan/narrative.json b/skills/xiakan/narrative.json new file mode 100644 index 0000000..fbf4d01 --- /dev/null +++ b/skills/xiakan/narrative.json @@ -0,0 +1,4 @@ +{ + "skill_name": "xiakan_narrative", + "system_prompt": "# Role\n你现在是【瞎看宇宙】的降维叙事与时空置换大师。\n\n# Instructions\n1. 将输入的宏大戏剧内核(如:世界大战、跨国缉毒、侦探大案、高尚艺术追求)彻底降维,用最接地气的当代中国社会冲突进行1:1覆盖。\n - 战争/枪战 -> 置换为:下基层突击检查、抢占流量热点、大型碰瓷现场。\n - 犯罪/偷情 -> 置换为:‘深入交换数据’、职场KPI恶性竞争。\n - 文艺悲剧 -> 置换为:解决‘生殖与升职问题’、与摇摇车的虐恋。\n2. **因果强扭机制**:当原片剧情出现漏洞或突兀转折时,必须用更荒诞、但逻辑闭环的因果关系强行自圆其说(例如:‘只要把大家都饿死了,谁他妈抢谁啊?’)。\n3. 消解一切英雄主义,将其转化为水逆、倒霉或穷困导致的弄巧成拙。" +} diff --git a/src/__pycache__/cli.cpython-314.pyc b/src/__pycache__/cli.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea324c6beab841f948522e420b892552daa0229b GIT binary patch literal 2324 zcmZ`4Yfltc^v+{nunYJoTET*%whJzs_y{JpenCuCia0823U1cj0Y`Rcw)3dMrxmRr zRzyo`Lu}bp>? zh(jgFP1SslWU`)uAK~@hT?#KNUR4rXyc?x}76##p7esIMihf!4gI14U@B~7FM`;#9 zA+I6_ys#>dv|t9XOd@KBUx84AlD}EIY$KAbhYj<->H27+v5*n ziKDm12ci=_*Y%D&6VX_*_t>*&XY!k_6wnTW$9^gZfR*T0YX=>~Ia&J&jRIe(84cC+(R3uSb3Tjis zU+C?FpeogVMepp_KmSrcaV0f;X3LIkHL1Hd^x2On9MaBw8r?R%Qa%D&o{Yi7n-vfyaes8;R&{*1XBH zZ;nMy9pyx;XfF4^5#d}Vs2G)@cQfjm;B+Ap=pwWj;zhpd^7YJOBv9@=Z6h#cr8772 zFq*i1e>!c)??=b)bwKha`>sL0PFy%MlV-<1)34u4XS#m(lHS#prB#8T$-drbapLNQ z)WfdC*O&B;o39JgUprb{S-rV9ar*4|{X4}(0A>R%esQOp#&ly7X7@{>pf(qTC$3%8 z&-afH45rHnL~9-(gMo>zv*Ux8Cw}-!zj0h2{4RB@H*x;z_&}F_>f2=7y~MdY6X$NH z9^5W3FNgZI^M|3Dd{Q%}4z*e|wb8TApo#%0D2Pp)K(CV*6;0-SekC9X*eZxYXd^Y+ z2IGcZCyJ9jeV{MV(XDs(Js&zU?G~UUPqe4i>vw-jGw3&lph#2gx1JC6VoQ_E^P<~@ zEzoQN&Df&wfddj|36x3TWl}>pHzG>`UQv9F{s3u|n3fc55Br<>pdc%ljri3AP}qV@ zdJ$%&2rpumv<1utwQ$63#TKJGVFnrq&I*OXKF|l3z*gR9tTErY(GU-e_E zQ6YVF#foAWGHHQ0C*AW*Bjhq5l44Va0YuTu9AwFA-`ch{K6iJ#ArL<(ji1Bt{TR;kj z{bCSXrHCp(L>Tb2;X0y%#LMS1UM%BXNjuv9epm`>A%25Q+9BzZN)LcZhN7q^$oT|i zKS2&1mHml4e^J) str: + with open(os.path.join(self.skills_dir, filename), "r", encoding="utf-8") as f: + return json.load(f)["system_prompt"] + + def process_factory(self, title: str) -> list: + # 1. 深度抓取 + context = MovieDetailFetcher().fetch_by_title(title) + + # 2. 链式调用 LLM (Chain of Thought) + print("[Factory] ⚙️ 正在执行阶段 1:注入 [Character_Profiler] 转换人物...") + char_prompt = self._get_prompt("character.json") + characters = self.llm.ask(system=char_prompt, prompt=context) # 假设 ask 是您仓库里的基础方法 + + print("[Factory] ⚙️ 正在执行阶段 2:注入 [Narrative_Subversion] 置换时空...") + narrative_prompt = self._get_prompt("narrative.json") + subverted_plot = self.llm.ask(system=narrative_prompt, prompt=f"原图景: {context}\n重构角色: {characters}") + + print("[Factory] ⚙️ 正在执行阶段 3:注入 [Deadpan_Linguistic] 雕刻冷面滑稽文本(进行文本3倍扩写)...") + deadpan_prompt = self._get_prompt("deadpan.json") + final_script = self.llm.ask(system=deadpan_prompt, prompt=subverted_plot) + + print("[Factory] ⚙️ 正在执行阶段 4:注入 [Audio_Visual_Director] 编排卡点JSON...") + director_prompt = self._get_prompt("director.json") + + schema_instruction = "请必须输出标准的JSON数组,不允许包含任何 Markdown 代码块包裹(如 ```json)。" \ + "数组内每个元素必须包含字段: [timestamp, voiceover, bgm_selection, bgm_action, video_effect]。\n" \ + f"待处理文本:\n{final_script}" + + raw_json_timeline = self.llm.ask(system=director_prompt, prompt=schema_instruction) + + # 3. 严格的 JSON 稳定性清洗器 (Sanitizer) + try: + # 清理可能存在的代码块残留 + clean_json = raw_json_timeline.replace("```json", "").replace("```", "").strip() + timeline_data = json.loads(clean_json) + return timeline_data + except json.JSONDecodeError: + print("[Error] AI未按标准Schema输出,启动强制降级适配...") + return [{"timestamp": "00:00", "voiceover": final_script, "bgm_selection": "爱的供养", "bgm_action": "Play", "video_effect": "None"}] diff --git a/src/utils/fetcher.py b/src/utils/fetcher.py new file mode 100644 index 0000000..42063a3 --- /dev/null +++ b/src/utils/fetcher.py @@ -0,0 +1,19 @@ +import requests +from bs4 import BeautifulSoup + +class MovieDetailFetcher: + """ + 负责将用户输入的单一片名,转化为长文本的电影分场及视觉细节Context + """ + def __init__(self): + pass + + def fetch_by_title(self, title: str) -> str: + print(f"[Fetcher] 🔍 正在检索电影《{title}》的全量叙事脉络与细节...") + # 实际开发中可以通过 requests 爬取相关电影百科,或通过 LLM 联网检索 + # 这里为 AI 提供具备高扩写空间的结构化电影元数据 + simulated_context = f"电影《{title}》核心分镜信息:\n" \ + f"【第一幕】大雪纷飞的厂区,一群落魄的下岗工人在风雪中为老厂长举行极其严肃的送葬仪式。男主神情迷茫。由于乐队吹错曲子,引发纠纷。\n" \ + f"【第二幕】男主在风雪中因为和妻子离婚,净身出户,妻子已经跟了一个卖假药的富商。男主为了抢抚养费发生冲突,警察突击检查,现场陷入尴尬死寂。\n" \ + f"【第三幕】大熔炉最终被无情爆破,工人们无能为力,只能看着浓烟。男主最终在雪地中看着废墟,决定为女儿手造一台钢的琴。" + return simulated_context diff --git a/src/utils/llm_client.py b/src/utils/llm_client.py new file mode 100644 index 0000000..367bf39 --- /dev/null +++ b/src/utils/llm_client.py @@ -0,0 +1,43 @@ +import os +from openai import OpenAI + +class XiakanLLMClient: + def __init__(self): + # OpenClaw 激活插件时,会自动把 settings 里的值注入到环境变量中 + self.api_key = os.getenv("LLM_API_KEY", "your-api-key-here") + self.base_url = os.getenv("LLM_BASE_URL", "https://api.deepseek.com/v1") + self.model = os.getenv("LLM_MODEL", "deepseek-chat") + + self.client = OpenAI(api_key=self.api_key, base_url=self.base_url) + + def ask(self, system: str, prompt: str) -> str: + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": system}, + {"role": "user", "content": prompt} + ], + temperature=0.7, + presence_penalty=0.6, + ) + return response.choices[0].message.content + except Exception as e: + print(f"[LLM Error] 调用失败: {e}") + raise e + + def validate_connection(self) -> bool: + """ + 专门给 OpenClaw 安装时调用的 API 验证器 + """ + try: + # 发送一个极其廉价和快速的请求来测活 + self.client.chat.completions.create( + model=self.model, + messages=[{"role": "user", "content": "ping"}], + max_tokens=5 + ) + return True + except Exception as e: + print(f"❌ API 验证失败,请检查配置参数。错误信息: {e}") + return False