's'
This commit is contained in:
@@ -42,6 +42,13 @@ def _safe_channel_file_id(channel_id: str) -> str:
|
||||
return s or "default"
|
||||
|
||||
|
||||
def _display_channel_name(raw_name: str, display_index: int) -> str:
|
||||
name = (raw_name or "").strip()
|
||||
if not name or re.fullmatch(r"频道\s*\d+", name):
|
||||
return f"频道 {display_index}"
|
||||
return name
|
||||
|
||||
|
||||
def url_config_path_for_channel(config_dir: Path, channel_id: str) -> Path:
|
||||
return relay_pro_dir(config_dir) / f"url_config.{_safe_channel_file_id(channel_id)}.ini"
|
||||
|
||||
@@ -170,11 +177,11 @@ def list_channels_for_pro(config_dir: Path) -> dict[str, Any]:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
cid = str(item.get("id") or "").strip()
|
||||
name = str(item.get("name") or "").strip() or "未命名"
|
||||
durl = str(item.get("douyinUrl") or item.get("douyin_url") or "").strip()
|
||||
key = str(item.get("youtubeKey") or item.get("key") or "").strip()
|
||||
if not cid:
|
||||
if not cid or not key:
|
||||
continue
|
||||
name = _display_channel_name(str(item.get("name") or ""), len(out) + 1)
|
||||
kp = _mask_key_preview(key)
|
||||
out.append(
|
||||
{
|
||||
@@ -265,20 +272,25 @@ def update_channel_keys_list(
|
||||
return False, "同一线路内不能重复同一串流密钥"
|
||||
ai = max(0, min(int(active_index), len(cleaned) - 1))
|
||||
active_raw = cleaned[ai]
|
||||
claimed_norms = set(norms)
|
||||
moved_from: list[str] = []
|
||||
for item in arr:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
oid = str(item.get("id") or "").strip()
|
||||
if oid == channel_id:
|
||||
continue
|
||||
for ok in _all_keys_for_channel(item):
|
||||
okn = _norm_stream_key(ok)
|
||||
if not okn:
|
||||
continue
|
||||
for ck in cleaned:
|
||||
ckn = _norm_stream_key(ck)
|
||||
if ckn and ckn == okn:
|
||||
return False, "禁止多条线路共用同一 YouTube 串流密钥"
|
||||
old_keys = _all_keys_for_channel(item)
|
||||
kept = [ok for ok in old_keys if _norm_stream_key(ok) not in claimed_norms]
|
||||
if len(kept) == len(old_keys):
|
||||
continue
|
||||
moved_from.append(str(item.get("name") or oid or "未命名线路"))
|
||||
if kept:
|
||||
item["youtubeKeys"] = kept
|
||||
item["youtubeKey"] = kept[0]
|
||||
else:
|
||||
item["youtubeKey"] = ""
|
||||
item.pop("youtubeKeys", None)
|
||||
for item in arr:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
@@ -287,6 +299,10 @@ def update_channel_keys_list(
|
||||
item["youtubeKeys"] = cleaned
|
||||
item["youtubeKey"] = active_raw
|
||||
channels_json_path(config_dir).write_text(json.dumps(doc, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
if moved_from:
|
||||
names = "、".join(moved_from[:3])
|
||||
more = f" 等 {len(moved_from)} 条线路" if len(moved_from) > 3 else ""
|
||||
return True, f"已保存;该 YouTube key 已从其它线路移除:{names}{more}"
|
||||
return True, "已保存"
|
||||
return False, "未找到该频道"
|
||||
|
||||
@@ -297,10 +313,13 @@ def extract_url_lines_from_ini(text: str) -> list[str]:
|
||||
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)
|
||||
m = re.search(r"https?://(?:live|v|www)\.douyin\.com/[^\s,,#;]+", t, re.I)
|
||||
if m:
|
||||
lines.append(m.group(0).replace("http://", "https://", 1))
|
||||
continue
|
||||
m = re.search(r"(?:live|v|www)\.douyin\.com/[^\s,,#;]+", t, re.I)
|
||||
if m:
|
||||
lines.append("https://" + m.group(0))
|
||||
return lines
|
||||
|
||||
|
||||
@@ -421,6 +440,60 @@ def sync_channel_to_legacy_files(config_dir: Path, channel_id: str) -> tuple[boo
|
||||
return True, "已同步到 youtube.ini 与 URL_config.ini"
|
||||
|
||||
|
||||
def first_relay_channel_id(config_dir: Path) -> str:
|
||||
doc, _ = load_channels_document(config_dir)
|
||||
if doc is None:
|
||||
return ""
|
||||
arr = doc.get("channels")
|
||||
if not isinstance(arr, list):
|
||||
return ""
|
||||
for item in arr:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
cid = str(item.get("id") or "").strip()
|
||||
if cid:
|
||||
return cid
|
||||
return ""
|
||||
|
||||
|
||||
def sync_legacy_files_to_first_channel(config_dir: Path) -> tuple[bool, str, str | None]:
|
||||
"""将单频道 youtube.ini + URL_config.ini 同步到 Pro 第一线路。"""
|
||||
cid = first_relay_channel_id(config_dir)
|
||||
if not cid:
|
||||
return False, "缺少 Pro 第一线路", None
|
||||
|
||||
ini = Path(config_dir) / "youtube.ini"
|
||||
key = ""
|
||||
if ini.exists():
|
||||
try:
|
||||
parsed = parse_youtube_ini(ini.read_text(encoding="utf-8-sig"))
|
||||
keys = parsed.get("keys") if isinstance(parsed, dict) else []
|
||||
ai = parsed.get("activeIndex", 0) if isinstance(parsed, dict) else 0
|
||||
if isinstance(keys, list) and keys:
|
||||
idx = max(0, min(int(ai or 0), len(keys) - 1))
|
||||
key = str(keys[idx] or "").strip()
|
||||
except Exception:
|
||||
key = ""
|
||||
if not key:
|
||||
return False, "单频道未配置 YouTube 串流密钥", cid
|
||||
|
||||
ok, msg = update_channel_keys_list(config_dir, cid, [key], 0)
|
||||
if not ok:
|
||||
return False, msg, cid
|
||||
|
||||
url_ini = Path(config_dir) / "URL_config.ini"
|
||||
url_body = ""
|
||||
if url_ini.exists():
|
||||
try:
|
||||
url_body = url_ini.read_text(encoding="utf-8-sig")
|
||||
except Exception as e:
|
||||
return False, f"读取单频道地址失败: {e}", cid
|
||||
ok, msg = write_url_config_for_channel(config_dir, cid, url_body)
|
||||
if not ok:
|
||||
return False, msg, cid
|
||||
return True, "已将单频道配置同步到 Pro 第一线路", cid
|
||||
|
||||
|
||||
def parse_relay_meta_lines(log_text: str) -> list[dict[str, Any]]:
|
||||
out: list[dict[str, Any]] = []
|
||||
for line in log_text.splitlines():
|
||||
@@ -531,15 +604,19 @@ def _match_channel_by_key_suffix(config_dir: Path, suffix: str) -> tuple[str, st
|
||||
if not doc or not isinstance(doc.get("channels"), list):
|
||||
return "", ""
|
||||
su = suffix.strip().lower()
|
||||
display_index = 0
|
||||
for item in doc["channels"]:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
if not _all_keys_for_channel(item):
|
||||
continue
|
||||
display_index += 1
|
||||
for k in _all_keys_for_channel(item):
|
||||
tail = _key_tail_for_match(k)
|
||||
if not tail:
|
||||
continue
|
||||
if su == tail or (len(su) >= 4 and tail.endswith(su)):
|
||||
return str(item.get("id") or ""), str(item.get("name") or "")
|
||||
return str(item.get("id") or ""), _display_channel_name(str(item.get("name") or ""), display_index)
|
||||
return "", ""
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user