diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/references/kr宝塔_网络与服务器卡顿_检查与处理.md b/01_卡资(金)/金仓_存储备份/服务器管理/references/kr宝塔_网络与服务器卡顿_检查与处理.md index 7b2ebb9f..b2ea0ba8 100644 --- a/01_卡资(金)/金仓_存储备份/服务器管理/references/kr宝塔_网络与服务器卡顿_检查与处理.md +++ b/01_卡资(金)/金仓_存储备份/服务器管理/references/kr宝塔_网络与服务器卡顿_检查与处理.md @@ -186,7 +186,19 @@ limit_rate 500k; # 单连接限速 500KB/s,可按需改 **带宽占比**:当前瞬时连接数较少时,无法单次采样得到稳定占比。请在服务器上运行 **6.4 的脚本** 或执行 `nethogs -t` 采样 10~30 秒,即可得到各进程的实时带宽占比(KB/s 或 %)。 -### 6.6 502 Bad Gateway 修复(含 soul.quwanzhi.com/admin) +### 6.6 502 Bad Gateway 修复(含 soul、wzdj、word) + +**Node 项目 502**(如 wzdj.quwanzhi.com、word.quwanzhi.com): + +```bash +# 本机执行,免 SSH +./01_卡资(金)/金仓_存储备份/服务器管理/scripts/.venv_tx/bin/python \ + "01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_修复502_Node项目.py" wzdj word +``` + +若 word 仍 502,在宝塔 **Node 项目** 中查看 word 的启动日志,可能是 MODULE_NOT_FOUND 或 Node 版本不匹配,按 `references/Node项目未启动_MODULE_NOT_FOUND修复指南.md` 修正启动命令。 + +**含 soul 的 502**: **原因**:Nginx 能通,但上游(Node/后端)无响应或挂掉,导致 502。 diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_Node批量修复.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_Node批量修复.py index 4116e2b0..fae360d5 100644 --- a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_Node批量修复.py +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_Node批量修复.py @@ -79,16 +79,23 @@ def ports(it): return sorted(set(ps)) items = post("/project/nodejs/get_project_list").get("data") or post("/project/nodejs/get_project_list").get("list") or [] for it in items: - name = it.get("name") - if not name or it.get("run") is True: continue - for port in ports(it): - for pid in pids(port): subprocess.call("kill -9 %s" % pid, shell=True) - pf = "/www/server/nodejs/vhost/pids/%s.pid" % name - if os.path.exists(pf): open(pf,"w").write("0") - post("/project/nodejs/stop_project", {"project_name": name}) - r = post("/project/nodejs/start_project", {"project_name": name}) - ok = r.get("status") is True or "成功" in str(r.get("msg","")) - print("%s: %s" % (name, "OK" if ok else "FAIL")) + try: + name = it.get("name") + if not name or it.get("run") is True: continue + for port in ports(it): + for pid in pids(port): + try: subprocess.call("kill -9 %s" % pid, shell=True) + except: pass + pf = "/www/server/nodejs/vhost/pids/%s.pid" % name + if os.path.exists(pf): + try: open(pf,"w").write("0") + except: pass + post("/project/nodejs/stop_project", {"project_name": name}) + r = post("/project/nodejs/start_project", {"project_name": name}) + ok = r.get("status") is True or "成功" in str(r.get("msg","")) + print("%s: %s" % (name, "OK" if ok else "FAIL")) + except Exception as e: + print("%s: ERR %s" % (name, str(e)[:80])) time.sleep(1) time.sleep(4) items2 = post("/project/nodejs/get_project_list").get("data") or [] diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_存客宝诊断443.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_存客宝诊断443.py new file mode 100644 index 00000000..3c09462c --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_存客宝诊断443.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +腾讯云 TAT 在存客宝上执行 443/SSL 诊断,并返回输出 +凭证:00_账号与API索引.md 或环境变量 +""" +import base64 +import os +import re +import sys +import time + +CKB_INSTANCE_ID = "ins-ciyv2mxa" +REGION = "ap-guangzhou" + +CMD = """echo "=== iptables INPUT 80/443 ===" && iptables -L INPUT -n -v 2>/dev/null | head -30 || true +echo "=== firewalld 80/443 ===" && firewall-cmd --list-all 2>/dev/null || true +echo "=== 安全组/防火墙摘要 ===" && echo "服务器内 80/443 均应由 Nginx 监听,若外网 80 通 443 不通,多为腾讯云安全组/轻量防火墙未放行 443" +echo "=== DONE ===" +""" + +def _find_root(): + d = os.path.dirname(os.path.abspath(__file__)) + for _ in range(6): + if os.path.basename(d) == "卡若AI" or (os.path.isdir(os.path.join(d, "运营中枢")) and os.path.isdir(os.path.join(d, "01_卡资(金)"))): + return d + d = os.path.dirname(d) + return None + +def _read_creds(): + root = _find_root() + if not root: + return None, None + path = os.path.join(root, "运营中枢", "工作台", "00_账号与API索引.md") + if not os.path.isfile(path): + return None, None + with open(path, "r", encoding="utf-8") as f: + text = f.read() + sid = skey = None + in_t = False + for line in text.splitlines(): + if "### 腾讯云" in line: + in_t = True + continue + if in_t and line.strip().startswith("###"): + break + if not in_t: + continue + m = re.search(r"\|\s*[^|]*(?:SecretId|密钥)[^|]*\|\s*`([^`]+)`", line, re.I) + if m and m.group(1).strip().startswith("AKID"): + sid = m.group(1).strip() + m = re.search(r"\|\s*SecretKey\s*\|\s*`([^`]+)`", line, re.I) + if m: + skey = m.group(1).strip() + return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY") + +def main(): + secret_id, secret_key = _read_creds() + if not secret_id or not secret_key: + print("❌ 未配置腾讯云 SecretId/SecretKey") + return 1 + try: + from tencentcloud.common import credential + from tencentcloud.tat.v20201028 import tat_client, models + except ImportError: + print("pip install tencentcloud-sdk-python-common tencentcloud-sdk-python-tat") + return 1 + + cred = credential.Credential(secret_id, secret_key) + client = tat_client.TatClient(cred, REGION) + req = models.RunCommandRequest() + req.Content = base64.b64encode(CMD.encode()).decode() + req.InstanceIds = [CKB_INSTANCE_ID] + req.CommandType = "SHELL" + req.Timeout = 30 + req.CommandName = "CKB_443Diagnose" + resp = client.RunCommand(req) + inv_id = resp.InvocationId + print("⏳ TAT 已下发,等待 20s 获取输出...") + time.sleep(20) + + try: + req2 = models.DescribeInvocationTasksRequest() + f = models.Filter() + f.Name = "invocation-id" + f.Values = [inv_id] + req2.Filters = [f] + resp2 = client.DescribeInvocationTasks(req2) + for t in (resp2.InvocationTaskSet or []): + status = getattr(t, "TaskStatus", "N/A") + print(" 任务状态:", status) + for attr in ("Output", "OutputUrl", "TaskResult", "ErrorInfo"): + v = getattr(t, attr, None) + if v: + print(" %s:" % attr, str(v)[:2500]) + except Exception as e: + print(" 查询异常:", e) + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_轻量放行443.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_轻量放行443.py new file mode 100644 index 00000000..9d14c272 --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_轻量放行443.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""若存客宝为轻量应用服务器,放行 443 防火墙""" +import os, re, sys + +def _creds(): + root = os.path.dirname(os.path.abspath(__file__)) + for _ in range(6): + if os.path.basename(root) == "卡若AI": + break + root = os.path.dirname(root) + p = os.path.join(root, "运营中枢", "工作台", "00_账号与API索引.md") + if not os.path.isfile(p): + return None, None + with open(p, "r") as f: + t = f.read() + sid = skey = None + in_t = False + for line in t.splitlines(): + if "### 腾讯云" in line: in_t = True + elif in_t and line.strip().startswith("###"): break + elif in_t: + m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", line, re.I) + if m and "AKID" in m.group(1): sid = m.group(1).strip() + m = re.search(r"SecretKey[^|]*\|\s*`([^`]+)`", line, re.I) + if m: skey = m.group(1).strip() + return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY") + +def main(): + sid, skey = _creds() + if not sid or not skey: + print("❌ 无凭证"); return 1 + try: + from tencentcloud.common import credential + from tencentcloud.lighthouse.v20200324 import lighthouse_client, models + except ImportError: + print("pip install tencentcloud-sdk-python-lighthouse"); return 1 + cred = credential.Credential(sid, skey) + c = lighthouse_client.LighthouseClient(cred, "ap-guangzhou") + req = models.DescribeInstancesRequest() + req.Limit = 100 + resp = c.DescribeInstances(req) + for ins in (resp.InstanceSet or []): + for ip in (getattr(ins, "PublicAddresses", None) or []): + if ip == "42.194.245.239": + iid = getattr(ins, "InstanceId", None) + print("存客宝为轻量实例:", iid) + req2 = models.CreateFirewallRulesRequest() + req2.InstanceId = iid + r = models.FirewallRule() + r.Protocol = "TCP" + r.Port = "443" + r.CidrBlock = "0.0.0.0/0" + r.Action = "ACCEPT" + req2.FirewallRules = [r] + c.CreateFirewallRules(req2) + print("✅ 已添加 443 防火墙规则"); return 0 + print("42.194.245.239 非轻量实例(为 CVM)"); return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/03_卡木(木)/木叶_视频内容/视频切片/SKILL.md b/03_卡木(木)/木叶_视频内容/视频切片/SKILL.md index 779e46f1..74ca334a 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/SKILL.md +++ b/03_卡木(木)/木叶_视频内容/视频切片/SKILL.md @@ -20,17 +20,32 @@ updated: "2026-02-16" ### 一键命令(Soul派对专用) +#### 一体化流水线(推荐) + +```bash +cd 03_卡木(木)/木叶_视频内容/视频切片/脚本 +conda activate mlx-whisper +python3 soul_slice_pipeline.py --video "/path/to/soul派对会议第57场.mp4" --clips 6 +``` + +流程:**转录 → 高光识别(AI/规则) → 批量切片 → 增强(Hook+CTA)** + +#### 分步命令 + ```bash # 1. 转录(MLX Whisper,约3分钟/2.5小时视频) eval "$(~/miniforge3/bin/conda shell.zsh hook)" conda activate mlx-whisper mlx_whisper audio.wav --model mlx-community/whisper-small-mlx --language zh --output-format all -# 2. 切片(生成原始切片) -python3 batch_clip.py +# 2. 高光识别(Gemini AI,失败时自动用规则切分) +python3 identify_highlights.py -t transcript.srt -o highlights.json -n 6 -# 3. 增强处理(封面+字幕+加速+去语气词) -python3 enhance_clips.py +# 3. 切片 +python3 batch_clip.py -i 视频.mp4 -l highlights.json -o clips/ + +# 4. 增强处理(Hook+CTA 封面文字) +python3 enhance_clips.py -c clips/ -l highlights.json -o clips_enhanced/ ``` ### 增强功能说明 diff --git a/03_卡木(木)/木叶_视频内容/视频切片/脚本/identify_highlights.py b/03_卡木(木)/木叶_视频内容/视频切片/脚本/identify_highlights.py index 9daef1d7..7ecac64e 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/脚本/identify_highlights.py +++ b/03_卡木(木)/木叶_视频内容/视频切片/脚本/identify_highlights.py @@ -19,6 +19,59 @@ MIN_DURATION = 45 MAX_DURATION = 150 +def parse_srt_segments(srt_path: str) -> list: + """解析 SRT 为 [{start, end, text}, ...]""" + with open(srt_path, "r", encoding="utf-8") as f: + content = f.read() + segments = [] + pattern = r"(\d+)\n(\d{2}):(\d{2}):(\d{2}),(\d{3}) --> (\d{2}):(\d{2}):(\d{2}),(\d{3})\n(.*?)(?=\n\n|\Z)" + for m in re.findall(pattern, content, re.DOTALL): + sh, sm, ss = int(m[1]), int(m[2]), int(m[3]) + eh, em, es = int(m[5]), int(m[6]), int(m[7]) + start_sec = sh * 3600 + sm * 60 + ss + end_sec = eh * 3600 + em * 60 + es + text = m[9].strip().replace("\n", " ") + if len(text) > 3: + segments.append({ + "start_sec": start_sec, "end_sec": end_sec, + "start_time": f"{sh:02d}:{sm:02d}:{ss:02d}", + "end_time": f"{eh:02d}:{em:02d}:{es:02d}", + "text": text, + }) + return segments + + +def fallback_highlights(transcript_path: str, clip_count: int) -> list: + """规则备用:按时长均匀切分,取每段首句为 Hook""" + segments = parse_srt_segments(transcript_path) + if not segments: + return [] + total = segments[-1]["end_sec"] if segments else 0 + interval = max(60, total / (clip_count + 1)) + result = [] + for i in range(clip_count): + start_sec = int(interval * (i + 0.2)) + end_sec = min(int(start_sec + 90), int(total - 5)) + if end_sec <= start_sec + 30: + continue + # 找该时间段内的字幕 + texts = [s["text"] for s in segments if s["end_sec"] >= start_sec and s["start_sec"] <= end_sec] + excerpt = (texts[0][:50] + "..." if texts and len(texts[0]) > 50 else (texts[0] if texts else "")) + hook = (excerpt[:15] + "..." if len(excerpt) > 15 else excerpt) or f"精彩片段{i+1}" + h, m, s = start_sec // 3600, (start_sec % 3600) // 60, start_sec % 60 + eh, em, es = end_sec // 3600, (end_sec % 3600) // 60, end_sec % 60 + result.append({ + "title": hook[:20], + "start_time": f"{h:02d}:{m:02d}:{s:02d}", + "end_time": f"{eh:02d}:{em:02d}:{es:02d}", + "hook_3sec": hook, + "cta_ending": DEFAULT_CTA, + "transcript_excerpt": excerpt, + "reason": "按时间均匀切分", + }) + return result + + def srt_to_timestamped_text(srt_path: str) -> str: """将 SRT 转为带时间戳的纯文本""" with open(srt_path, "r", encoding="utf-8") as f: @@ -36,8 +89,7 @@ def call_gemini(transcript: str, clip_count: int = CLIP_COUNT) -> str: """调用 Gemini 分析并返回 JSON(REST API,无额外依赖)""" import urllib.request import urllib.error - # gemini-pro 兼容性最好 -url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key={GEMINI_KEY}" + url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key={GEMINI_KEY}" prompt = f"""你是一个专业的短视频内容策划师,擅长从长视频中找出最有传播力的片段。 分析以下视频文字稿,找出 {clip_count} 个最适合做短视频的「高光片段」。 @@ -81,12 +133,10 @@ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:gener data = json.loads(r.read().decode()) except urllib.error.HTTPError as e: err_body = e.read().decode() if e.fp else "" - print(f"Gemini API 错误: {e.code} {err_body[:500]}", file=sys.stderr) - sys.exit(1) + raise RuntimeError(f"Gemini API 错误: {e.code} {err_body[:300]}") candidates = data.get("candidates", []) if not candidates: - print("Gemini 未返回内容", file=sys.stderr) - sys.exit(1) + raise RuntimeError("Gemini 未返回内容") text = candidates[0].get("content", {}).get("parts", [{}])[0].get("text", "").strip() if text.startswith("```"): text = re.sub(r"^```(?:json)?\s*", "", text) @@ -108,14 +158,17 @@ def main(): if len(text) < 100: print("❌ 文字稿过短,请检查 SRT 格式", file=sys.stderr) sys.exit(1) - print("正在调用 Gemini 分析高光片段...") - raw = call_gemini(text, args.clips) + # 尝试 Gemini,失败则用规则备用 + data = None try: + print("正在调用 Gemini 分析高光片段...") + raw = call_gemini(text, args.clips) data = json.loads(raw) - except json.JSONDecodeError as e: - print(f"JSON 解析失败: {e}", file=sys.stderr) - print("原始输出:", raw[:500], file=sys.stderr) - sys.exit(1) + except Exception as e: + print(f"Gemini 调用失败 ({e}),使用规则备用切分", file=sys.stderr) + data = fallback_highlights(str(transcript_path), args.clips) + if not data: + data = fallback_highlights(str(transcript_path), args.clips) if not isinstance(data, list): data = [data] out_path = Path(args.output) diff --git a/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_slice_pipeline.py b/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_slice_pipeline.py index 5449a7f9..e61d0b13 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_slice_pipeline.py +++ b/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_slice_pipeline.py @@ -146,9 +146,9 @@ def main(): timeout=300, ) - # 4. 增强(封面 + Hook + CTA) + # 4. 增强(封面 + Hook + CTA);若 FFmpeg 无 drawtext 则直接复制切片 enhanced_dir.mkdir(parents=True, exist_ok=True) - run( + ok = run( [ sys.executable, str(SCRIPT_DIR / "enhance_clips.py"), @@ -161,7 +161,14 @@ def main(): ], "增强处理(Hook+CTA)", timeout=600, + check=False, ) + import shutil + enhanced_count = len(list(enhanced_dir.glob("*.mp4"))) + if enhanced_count == 0 and clips_list: + print(" (FFmpeg 无 drawtext 滤镜,复制原始切片到 clips_enhanced)") + for f in sorted(clips_dir.glob("*.mp4")): + shutil.copy(f, enhanced_dir / f.name) print() print("=" * 60) diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index 1a268801..d7fe760f 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -57,3 +57,4 @@ | 2026-02-22 06:08:29 | 🔄 卡若AI 同步 2026-02-22 06:08 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 | | 2026-02-22 06:39:21 | 🔄 卡若AI 同步 2026-02-22 06:39 | 更新:总索引与入口、水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 8 个 | | 2026-02-22 06:47:15 | 🔄 卡若AI 同步 2026-02-22 06:47 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 8 个 | +| 2026-02-22 07:20:22 | 🔄 卡若AI 同步 2026-02-22 07:20 | 更新:总索引与入口、金仓、卡木、运营中枢工作台 | 排除 >20MB: 9 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index e4a58170..abeed8a2 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -60,3 +60,4 @@ | 2026-02-22 06:08:29 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 06:08 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-22 06:39:21 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 06:39 | 更新:总索引与入口、水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-22 06:47:15 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 06:47 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 8 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-02-22 07:20:22 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 07:20 | 更新:总索引与入口、金仓、卡木、运营中枢工作台 | 排除 >20MB: 9 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |