🔄 卡若AI 同步 2026-02-22 07:27 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 9 个
This commit is contained in:
@@ -186,7 +186,19 @@ limit_rate 500k; # 单连接限速 500KB/s,可按需改
|
|||||||
|
|
||||||
**带宽占比**:当前瞬时连接数较少时,无法单次采样得到稳定占比。请在服务器上运行 **6.4 的脚本** 或执行 `nethogs -t` 采样 10~30 秒,即可得到各进程的实时带宽占比(KB/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。
|
**原因**:Nginx 能通,但上游(Node/后端)无响应或挂掉,导致 502。
|
||||||
|
|
||||||
|
|||||||
@@ -79,16 +79,23 @@ def ports(it):
|
|||||||
return sorted(set(ps))
|
return sorted(set(ps))
|
||||||
items = post("/project/nodejs/get_project_list").get("data") or post("/project/nodejs/get_project_list").get("list") or []
|
items = post("/project/nodejs/get_project_list").get("data") or post("/project/nodejs/get_project_list").get("list") or []
|
||||||
for it in items:
|
for it in items:
|
||||||
name = it.get("name")
|
try:
|
||||||
if not name or it.get("run") is True: continue
|
name = it.get("name")
|
||||||
for port in ports(it):
|
if not name or it.get("run") is True: continue
|
||||||
for pid in pids(port): subprocess.call("kill -9 %s" % pid, shell=True)
|
for port in ports(it):
|
||||||
pf = "/www/server/nodejs/vhost/pids/%s.pid" % name
|
for pid in pids(port):
|
||||||
if os.path.exists(pf): open(pf,"w").write("0")
|
try: subprocess.call("kill -9 %s" % pid, shell=True)
|
||||||
post("/project/nodejs/stop_project", {"project_name": name})
|
except: pass
|
||||||
r = post("/project/nodejs/start_project", {"project_name": name})
|
pf = "/www/server/nodejs/vhost/pids/%s.pid" % name
|
||||||
ok = r.get("status") is True or "成功" in str(r.get("msg",""))
|
if os.path.exists(pf):
|
||||||
print("%s: %s" % (name, "OK" if ok else "FAIL"))
|
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(1)
|
||||||
time.sleep(4)
|
time.sleep(4)
|
||||||
items2 = post("/project/nodejs/get_project_list").get("data") or []
|
items2 = post("/project/nodejs/get_project_list").get("data") or []
|
||||||
|
|||||||
101
01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_存客宝诊断443.py
Normal file
101
01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_TAT_存客宝诊断443.py
Normal file
@@ -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())
|
||||||
61
01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_轻量放行443.py
Normal file
61
01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云_轻量放行443.py
Normal file
@@ -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())
|
||||||
@@ -20,17 +20,32 @@ updated: "2026-02-16"
|
|||||||
|
|
||||||
### 一键命令(Soul派对专用)
|
### 一键命令(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
|
```bash
|
||||||
# 1. 转录(MLX Whisper,约3分钟/2.5小时视频)
|
# 1. 转录(MLX Whisper,约3分钟/2.5小时视频)
|
||||||
eval "$(~/miniforge3/bin/conda shell.zsh hook)"
|
eval "$(~/miniforge3/bin/conda shell.zsh hook)"
|
||||||
conda activate mlx-whisper
|
conda activate mlx-whisper
|
||||||
mlx_whisper audio.wav --model mlx-community/whisper-small-mlx --language zh --output-format all
|
mlx_whisper audio.wav --model mlx-community/whisper-small-mlx --language zh --output-format all
|
||||||
|
|
||||||
# 2. 切片(生成原始切片)
|
# 2. 高光识别(Gemini AI,失败时自动用规则切分)
|
||||||
python3 batch_clip.py
|
python3 identify_highlights.py -t transcript.srt -o highlights.json -n 6
|
||||||
|
|
||||||
# 3. 增强处理(封面+字幕+加速+去语气词)
|
# 3. 切片
|
||||||
python3 enhance_clips.py
|
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/
|
||||||
```
|
```
|
||||||
|
|
||||||
### 增强功能说明
|
### 增强功能说明
|
||||||
|
|||||||
@@ -19,6 +19,59 @@ MIN_DURATION = 45
|
|||||||
MAX_DURATION = 150
|
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:
|
def srt_to_timestamped_text(srt_path: str) -> str:
|
||||||
"""将 SRT 转为带时间戳的纯文本"""
|
"""将 SRT 转为带时间戳的纯文本"""
|
||||||
with open(srt_path, "r", encoding="utf-8") as f:
|
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,无额外依赖)"""
|
"""调用 Gemini 分析并返回 JSON(REST API,无额外依赖)"""
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.error
|
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"""你是一个专业的短视频内容策划师,擅长从长视频中找出最有传播力的片段。
|
prompt = f"""你是一个专业的短视频内容策划师,擅长从长视频中找出最有传播力的片段。
|
||||||
|
|
||||||
分析以下视频文字稿,找出 {clip_count} 个最适合做短视频的「高光片段」。
|
分析以下视频文字稿,找出 {clip_count} 个最适合做短视频的「高光片段」。
|
||||||
@@ -81,12 +133,10 @@ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:gener
|
|||||||
data = json.loads(r.read().decode())
|
data = json.loads(r.read().decode())
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
err_body = e.read().decode() if e.fp else ""
|
err_body = e.read().decode() if e.fp else ""
|
||||||
print(f"Gemini API 错误: {e.code} {err_body[:500]}", file=sys.stderr)
|
raise RuntimeError(f"Gemini API 错误: {e.code} {err_body[:300]}")
|
||||||
sys.exit(1)
|
|
||||||
candidates = data.get("candidates", [])
|
candidates = data.get("candidates", [])
|
||||||
if not candidates:
|
if not candidates:
|
||||||
print("Gemini 未返回内容", file=sys.stderr)
|
raise RuntimeError("Gemini 未返回内容")
|
||||||
sys.exit(1)
|
|
||||||
text = candidates[0].get("content", {}).get("parts", [{}])[0].get("text", "").strip()
|
text = candidates[0].get("content", {}).get("parts", [{}])[0].get("text", "").strip()
|
||||||
if text.startswith("```"):
|
if text.startswith("```"):
|
||||||
text = re.sub(r"^```(?:json)?\s*", "", text)
|
text = re.sub(r"^```(?:json)?\s*", "", text)
|
||||||
@@ -108,14 +158,17 @@ def main():
|
|||||||
if len(text) < 100:
|
if len(text) < 100:
|
||||||
print("❌ 文字稿过短,请检查 SRT 格式", file=sys.stderr)
|
print("❌ 文字稿过短,请检查 SRT 格式", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
print("正在调用 Gemini 分析高光片段...")
|
# 尝试 Gemini,失败则用规则备用
|
||||||
raw = call_gemini(text, args.clips)
|
data = None
|
||||||
try:
|
try:
|
||||||
|
print("正在调用 Gemini 分析高光片段...")
|
||||||
|
raw = call_gemini(text, args.clips)
|
||||||
data = json.loads(raw)
|
data = json.loads(raw)
|
||||||
except json.JSONDecodeError as e:
|
except Exception as e:
|
||||||
print(f"JSON 解析失败: {e}", file=sys.stderr)
|
print(f"Gemini 调用失败 ({e}),使用规则备用切分", file=sys.stderr)
|
||||||
print("原始输出:", raw[:500], file=sys.stderr)
|
data = fallback_highlights(str(transcript_path), args.clips)
|
||||||
sys.exit(1)
|
if not data:
|
||||||
|
data = fallback_highlights(str(transcript_path), args.clips)
|
||||||
if not isinstance(data, list):
|
if not isinstance(data, list):
|
||||||
data = [data]
|
data = [data]
|
||||||
out_path = Path(args.output)
|
out_path = Path(args.output)
|
||||||
|
|||||||
@@ -146,9 +146,9 @@ def main():
|
|||||||
timeout=300,
|
timeout=300,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 4. 增强(封面 + Hook + CTA)
|
# 4. 增强(封面 + Hook + CTA);若 FFmpeg 无 drawtext 则直接复制切片
|
||||||
enhanced_dir.mkdir(parents=True, exist_ok=True)
|
enhanced_dir.mkdir(parents=True, exist_ok=True)
|
||||||
run(
|
ok = run(
|
||||||
[
|
[
|
||||||
sys.executable,
|
sys.executable,
|
||||||
str(SCRIPT_DIR / "enhance_clips.py"),
|
str(SCRIPT_DIR / "enhance_clips.py"),
|
||||||
@@ -161,7 +161,14 @@ def main():
|
|||||||
],
|
],
|
||||||
"增强处理(Hook+CTA)",
|
"增强处理(Hook+CTA)",
|
||||||
timeout=600,
|
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()
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|||||||
@@ -57,3 +57,4 @@
|
|||||||
| 2026-02-22 06:08:29 | 🔄 卡若AI 同步 2026-02-22 06:08 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 |
|
| 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: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 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 个 |
|
||||||
|
|||||||
@@ -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: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: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 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) |
|
||||||
|
|||||||
Reference in New Issue
Block a user