's'
This commit is contained in:
@@ -10,8 +10,13 @@ import time
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Awaitable
|
||||
|
||||
from web2_youtube_ini import parse_youtube_ini, serialize_youtube_ini
|
||||
from web2_youtube_pro_runtime import (
|
||||
channel_pm2_name,
|
||||
default_youtube_pro_pm2_name,
|
||||
normalize_youtube_pro_channels,
|
||||
)
|
||||
from web2_config_governance import backup_config_file
|
||||
from web2_youtube_ini import parse_youtube_ini, serialize_youtube_ini
|
||||
|
||||
# 与 web2_client_overview 一致的去重键
|
||||
def _norm_douyin_url(url: str) -> str:
|
||||
@@ -181,6 +186,8 @@ def list_channels_for_pro(config_dir: Path) -> dict[str, Any]:
|
||||
{
|
||||
"id": cid,
|
||||
"name": name,
|
||||
"pm2Name": channel_pm2_name(item),
|
||||
"notes": str(item.get("notes") or "").strip(),
|
||||
"douyinUrl": durl,
|
||||
"keyPreview": kp,
|
||||
"youtubeWatchUrl": get_youtube_watch_url(config_dir, cid),
|
||||
@@ -312,6 +319,9 @@ def get_channel_detail(config_dir: Path, channel_id: str) -> dict[str, Any] | No
|
||||
return {
|
||||
"id": str(ch.get("id") or "").strip(),
|
||||
"name": str(ch.get("name") or "").strip() or "未命名",
|
||||
"pm2Name": channel_pm2_name(ch),
|
||||
"notes": str(ch.get("notes") or "").strip(),
|
||||
"urlLines": str(ch.get("urlLines") or "").strip(),
|
||||
"douyinUrl": str(ch.get("douyinUrl") or ch.get("douyin_url") or "").strip(),
|
||||
"youtubeKey": active,
|
||||
"youtubeKeys": keys,
|
||||
@@ -368,16 +378,30 @@ def update_channel_keys_list(
|
||||
return False, "未找到该频道"
|
||||
|
||||
|
||||
_DOUYIN_LIVE_ROOM_RE = re.compile(
|
||||
r"(?:https?://(?:www\.)?)?live\.douyin\.com/([a-zA-Z0-9_-]+)", re.IGNORECASE
|
||||
)
|
||||
|
||||
|
||||
def extract_douyin_url_from_config_line(line: str) -> str:
|
||||
"""从 URL_config 行解析抖音直播间 URL(兼容 d2ypp2:url,主播: 名 / 原画,url,主播: 名)。"""
|
||||
raw = (line or "").strip().lstrip("\ufeff")
|
||||
if not raw or raw.startswith("#"):
|
||||
return ""
|
||||
m = _DOUYIN_LIVE_ROOM_RE.search(raw)
|
||||
if m:
|
||||
return f"https://live.douyin.com/{m.group(1)}"
|
||||
return ""
|
||||
|
||||
|
||||
def extract_url_lines_from_ini(text: str) -> list[str]:
|
||||
lines: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for raw in text.splitlines():
|
||||
t = raw.strip()
|
||||
if not t or t.startswith("#"):
|
||||
continue
|
||||
if "," in t:
|
||||
t = t.split(",")[-1].strip()
|
||||
if "douyin" in t.lower() or "v.douyin.com" in t.lower() or "live.douyin.com" in t.lower():
|
||||
lines.append(t)
|
||||
url = extract_douyin_url_from_config_line(raw)
|
||||
if url and url not in seen:
|
||||
seen.add(url)
|
||||
lines.append(url)
|
||||
return lines
|
||||
|
||||
|
||||
@@ -448,6 +472,10 @@ def read_url_config_for_channel(config_dir: Path, channel_id: str) -> str:
|
||||
|
||||
|
||||
def write_url_config_for_channel(config_dir: Path, channel_id: str, content: str) -> tuple[bool, str]:
|
||||
if not extract_url_lines_from_ini(content):
|
||||
existing = read_url_config_for_channel(config_dir, channel_id)
|
||||
if extract_url_lines_from_ini(existing):
|
||||
return False, "源站地址为空,已拒绝覆盖已有配置"
|
||||
errs = validate_url_config_no_cross_duplicate(config_dir, channel_id, content)
|
||||
if errs:
|
||||
return False, errs[0]
|
||||
@@ -517,8 +545,34 @@ def parse_relay_meta_lines(log_text: str) -> list[dict[str, Any]]:
|
||||
return out
|
||||
|
||||
|
||||
def relay_pm2_name_for_channel(channel_id: str) -> str:
|
||||
return f"youtube2__{channel_id}"
|
||||
def relay_pm2_name_for_channel(channel_id: str, config_dir: Path | None = None) -> str:
|
||||
cfg = config_dir or Path("config")
|
||||
ch = get_channel_by_id(cfg, channel_id)
|
||||
if ch:
|
||||
return channel_pm2_name(ch)
|
||||
return default_youtube_pro_pm2_name(channel_id)
|
||||
|
||||
|
||||
def save_channels_document(config_dir: Path, channels: list[dict[str, Any]]) -> tuple[bool, str]:
|
||||
try:
|
||||
normalized = normalize_youtube_pro_channels(channels)
|
||||
except ValueError as exc:
|
||||
return False, str(exc)
|
||||
doc, errs = load_channels_document(config_dir)
|
||||
if doc is None:
|
||||
return False, errs[0] if errs else "无法读取配置"
|
||||
doc["channels"] = normalized
|
||||
channels_json_path(config_dir).write_text(json.dumps(doc, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
return True, "已保存频道配置"
|
||||
|
||||
|
||||
def channel_id_for_pm2_name(config_dir: Path, pm2: str) -> str:
|
||||
doc, _ = load_channels_document(config_dir)
|
||||
if not doc or not isinstance(doc.get("channels"), list):
|
||||
return ""
|
||||
from web2_youtube_pro_runtime import channel_id_for_pm2_name as _cid_for_pm2
|
||||
|
||||
return _cid_for_pm2(doc["channels"], pm2)
|
||||
|
||||
|
||||
async def build_relay_live_status(
|
||||
@@ -540,7 +594,7 @@ async def build_relay_live_status(
|
||||
if not cid:
|
||||
continue
|
||||
cname = str(c.get("name") or "").strip() or "未命名"
|
||||
pname = relay_pm2_name_for_channel(cid)
|
||||
pname = relay_pm2_name_for_channel(cid, config_dir)
|
||||
try:
|
||||
info = await get_process_info(pname)
|
||||
except Exception:
|
||||
@@ -650,6 +704,8 @@ def add_relay_channel(
|
||||
row: dict[str, Any] = {
|
||||
"id": new_id,
|
||||
"name": nm,
|
||||
"pm2Name": default_youtube_pro_pm2_name(new_id),
|
||||
"notes": "",
|
||||
"douyinUrl": "",
|
||||
"youtubeKey": key,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user