feat: add loon and mihomo compatibility outputs from surge source

This commit is contained in:
袁震
2026-04-06 13:39:12 +08:00
parent b7bdad17d3
commit f8ff7279c6
10 changed files with 514 additions and 23 deletions
+4 -1
View File
@@ -56,6 +56,7 @@ jobs:
SOURCE_REF="${SOURCE_REF:-main}" SOURCE_REF="${SOURCE_REF:-main}"
SOURCE_ROOT="${SOURCE_ROOT:-rule/Surge}" SOURCE_ROOT="${SOURCE_ROOT:-rule/Surge}"
CLASH_NO_RESOLVE="${CLASH_NO_RESOLVE:-false}" CLASH_NO_RESOLVE="${CLASH_NO_RESOLVE:-false}"
MIHOMO_NO_RESOLVE="${MIHOMO_NO_RESOLVE:-false}"
cat > config.runtime.json <<JSON cat > config.runtime.json <<JSON
{ {
@@ -74,7 +75,8 @@ jobs:
}, },
"output": { "output": {
"dir": "dist", "dir": "dist",
"clash_no_resolve": ${CLASH_NO_RESOLVE} "clash_no_resolve": ${CLASH_NO_RESOLVE},
"mihomo_no_resolve": ${MIHOMO_NO_RESOLVE}
} }
} }
JSON JSON
@@ -85,6 +87,7 @@ jobs:
SOURCE_REF: ${{ vars.SOURCE_REF }} SOURCE_REF: ${{ vars.SOURCE_REF }}
SOURCE_ROOT: ${{ vars.SOURCE_ROOT }} SOURCE_ROOT: ${{ vars.SOURCE_ROOT }}
CLASH_NO_RESOLVE: ${{ vars.CLASH_NO_RESOLVE }} CLASH_NO_RESOLVE: ${{ vars.CLASH_NO_RESOLVE }}
MIHOMO_NO_RESOLVE: ${{ vars.MIHOMO_NO_RESOLVE }}
- name: Generate rules - name: Generate rules
if: steps.preflight.outputs.skip != 'true' if: steps.preflight.outputs.skip != 'true'
+10
View File
@@ -6,7 +6,9 @@
- 输入按目录分类(默认读取 `rule/Surge/<Name>/<Name>.list` - 输入按目录分类(默认读取 `rule/Surge/<Name>/<Name>.list`
- 输出仅包含你要的两种格式: - 输出仅包含你要的两种格式:
- `dist/surge/<Name>.list` - `dist/surge/<Name>.list`
- `dist/loon/<Name>.list`
- `dist/clash/<Name>.yaml` - `dist/clash/<Name>.yaml`
- `dist/mihomo/<Name>.yaml`
- 当前策略:**只使用 Surge 作为上游源,Clash 由 Surge 规则转换生成** - 当前策略:**只使用 Surge 作为上游源,Clash 由 Surge 规则转换生成**
## 为什么这个方案适合你 ## 为什么这个方案适合你
@@ -88,10 +90,17 @@ USER-AGENT,*youtube*
## 转换规则说明 ## 转换规则说明
- Surge 输出:保留源规则(去重、清理空白) - Surge 输出:保留源规则(去重、清理空白)
- Loon 输出:基于 Surge 规则直接输出(同样去重、清理空白)
- Clash 输出: - Clash 输出:
- 自动移除注释/空行 - 自动移除注释/空行
- `USER-AGENT``URL-REGEX` 默认跳过(并在头部记录 `SKIPPED-*` - `USER-AGENT``URL-REGEX` 默认跳过(并在头部记录 `SKIPPED-*`
- `DEST-PORT` 自动映射为 `DST-PORT`
- `IP-CIDR`/`IP-CIDR6` 可通过 `clash_no_resolve` 控制是否追加 `no-resolve` - `IP-CIDR`/`IP-CIDR6` 可通过 `clash_no_resolve` 控制是否追加 `no-resolve`
- Mihomo 输出:
- 使用与 Clash 相同的 provider YAML 结构
- `DEST-PORT` 自动映射为 `DST-PORT`
- `USER-AGENT``URL-REGEX` 默认跳过(并在头部记录 `SKIPPED-*`
- `IP-CIDR`/`IP-CIDR6` 可通过 `mihomo_no_resolve` 控制是否追加 `no-resolve`
## 定时更新建议 ## 定时更新建议
@@ -140,5 +149,6 @@ USER-AGENT,*youtube*
- `SOURCE_ROOT`(默认 `rule/Surge` - `SOURCE_ROOT`(默认 `rule/Surge`
- `TARGET_BRANCH`(默认 `main` - `TARGET_BRANCH`(默认 `main`
- `CLASH_NO_RESOLVE`(默认 `false` - `CLASH_NO_RESOLVE`(默认 `false`
- `MIHOMO_NO_RESOLVE`(默认 `false`
该工作流当前定时为 `15 3 * * *`UTC),对应北京时间每天 `11:15`,也支持手动触发。 该工作流当前定时为 `15 3 * * *`UTC),对应北京时间每天 `11:15`,也支持手动触发。
+2 -1
View File
@@ -14,6 +14,7 @@
}, },
"output": { "output": {
"dir": "dist", "dir": "dist",
"clash_no_resolve": false "clash_no_resolve": false,
"mihomo_no_resolve": false
} }
} }
+2
View File
@@ -16,3 +16,5 @@ exclude_categories = []
dir = "dist" dir = "dist"
# true: IP-CIDR/IP-CIDR6 in Clash payload include no-resolve # true: IP-CIDR/IP-CIDR6 in Clash payload include no-resolve
clash_no_resolve = false clash_no_resolve = false
# true: IP-CIDR/IP-CIDR6 in Mihomo payload include no-resolve
mihomo_no_resolve = false
+2 -1
View File
@@ -682,6 +682,7 @@
}, },
"output": { "output": {
"dir": "dist", "dir": "dist",
"clash_no_resolve": false "clash_no_resolve": false,
"mihomo_no_resolve": false
} }
} }
+1 -1
View File
@@ -1,7 +1,7 @@
# NAME: YouTube # NAME: YouTube
# AUTHOR: gitea-shunt-rules # AUTHOR: gitea-shunt-rules
# SOURCE: rule/Surge/YouTube/YouTube.list # SOURCE: rule/Surge/YouTube/YouTube.list
# UPDATED: 2026-04-06 05:01:32 UTC # UPDATED: 2026-04-06 05:38:50 UTC
# DOMAIN-KEYWORD: 1 # DOMAIN-KEYWORD: 1
# DOMAIN-SUFFIX: 179 # DOMAIN-SUFFIX: 179
# IP-CIDR: 2 # IP-CIDR: 2
+200
View File
@@ -0,0 +1,200 @@
# NAME: YouTube
# AUTHOR: gitea-shunt-rules
# SOURCE: rule/Surge/YouTube/YouTube.list
# UPDATED: 2026-04-06 05:38:50 UTC
# DOMAIN-KEYWORD: 1
# DOMAIN-SUFFIX: 179
# IP-CIDR: 2
# IP-CIDR6: 1
# USER-AGENT: 7
# TOTAL: 190
DOMAIN-SUFFIX,ggpht.cn
DOMAIN-SUFFIX,ggpht.com
DOMAIN-SUFFIX,googlevideo.com
DOMAIN-SUFFIX,gvt1.com
DOMAIN-SUFFIX,gvt2.com
DOMAIN-SUFFIX,video.google.com
DOMAIN-SUFFIX,wide-youtube.l.google.com
DOMAIN-SUFFIX,withyoutube.com
DOMAIN-SUFFIX,youtu.be
DOMAIN-SUFFIX,youtube
DOMAIN-SUFFIX,youtube-nocookie.com
DOMAIN-SUFFIX,youtube-ui.l.google.com
DOMAIN-SUFFIX,youtube.ae
DOMAIN-SUFFIX,youtube.al
DOMAIN-SUFFIX,youtube.am
DOMAIN-SUFFIX,youtube.at
DOMAIN-SUFFIX,youtube.az
DOMAIN-SUFFIX,youtube.ba
DOMAIN-SUFFIX,youtube.be
DOMAIN-SUFFIX,youtube.bg
DOMAIN-SUFFIX,youtube.bh
DOMAIN-SUFFIX,youtube.bo
DOMAIN-SUFFIX,youtube.by
DOMAIN-SUFFIX,youtube.ca
DOMAIN-SUFFIX,youtube.cat
DOMAIN-SUFFIX,youtube.ch
DOMAIN-SUFFIX,youtube.cl
DOMAIN-SUFFIX,youtube.co
DOMAIN-SUFFIX,youtube.co.ae
DOMAIN-SUFFIX,youtube.co.at
DOMAIN-SUFFIX,youtube.co.cr
DOMAIN-SUFFIX,youtube.co.hu
DOMAIN-SUFFIX,youtube.co.id
DOMAIN-SUFFIX,youtube.co.il
DOMAIN-SUFFIX,youtube.co.in
DOMAIN-SUFFIX,youtube.co.jp
DOMAIN-SUFFIX,youtube.co.ke
DOMAIN-SUFFIX,youtube.co.kr
DOMAIN-SUFFIX,youtube.co.ma
DOMAIN-SUFFIX,youtube.co.nz
DOMAIN-SUFFIX,youtube.co.th
DOMAIN-SUFFIX,youtube.co.tz
DOMAIN-SUFFIX,youtube.co.ug
DOMAIN-SUFFIX,youtube.co.uk
DOMAIN-SUFFIX,youtube.co.ve
DOMAIN-SUFFIX,youtube.co.za
DOMAIN-SUFFIX,youtube.co.zw
DOMAIN-SUFFIX,youtube.com
DOMAIN-SUFFIX,youtube.com.ar
DOMAIN-SUFFIX,youtube.com.au
DOMAIN-SUFFIX,youtube.com.az
DOMAIN-SUFFIX,youtube.com.bd
DOMAIN-SUFFIX,youtube.com.bh
DOMAIN-SUFFIX,youtube.com.bo
DOMAIN-SUFFIX,youtube.com.br
DOMAIN-SUFFIX,youtube.com.by
DOMAIN-SUFFIX,youtube.com.co
DOMAIN-SUFFIX,youtube.com.do
DOMAIN-SUFFIX,youtube.com.ec
DOMAIN-SUFFIX,youtube.com.ee
DOMAIN-SUFFIX,youtube.com.eg
DOMAIN-SUFFIX,youtube.com.es
DOMAIN-SUFFIX,youtube.com.gh
DOMAIN-SUFFIX,youtube.com.gr
DOMAIN-SUFFIX,youtube.com.gt
DOMAIN-SUFFIX,youtube.com.hk
DOMAIN-SUFFIX,youtube.com.hn
DOMAIN-SUFFIX,youtube.com.hr
DOMAIN-SUFFIX,youtube.com.jm
DOMAIN-SUFFIX,youtube.com.jo
DOMAIN-SUFFIX,youtube.com.kw
DOMAIN-SUFFIX,youtube.com.lb
DOMAIN-SUFFIX,youtube.com.lv
DOMAIN-SUFFIX,youtube.com.ly
DOMAIN-SUFFIX,youtube.com.mk
DOMAIN-SUFFIX,youtube.com.mt
DOMAIN-SUFFIX,youtube.com.mx
DOMAIN-SUFFIX,youtube.com.my
DOMAIN-SUFFIX,youtube.com.ng
DOMAIN-SUFFIX,youtube.com.ni
DOMAIN-SUFFIX,youtube.com.om
DOMAIN-SUFFIX,youtube.com.pa
DOMAIN-SUFFIX,youtube.com.pe
DOMAIN-SUFFIX,youtube.com.ph
DOMAIN-SUFFIX,youtube.com.pk
DOMAIN-SUFFIX,youtube.com.pt
DOMAIN-SUFFIX,youtube.com.py
DOMAIN-SUFFIX,youtube.com.qa
DOMAIN-SUFFIX,youtube.com.ro
DOMAIN-SUFFIX,youtube.com.sa
DOMAIN-SUFFIX,youtube.com.sg
DOMAIN-SUFFIX,youtube.com.sv
DOMAIN-SUFFIX,youtube.com.tn
DOMAIN-SUFFIX,youtube.com.tr
DOMAIN-SUFFIX,youtube.com.tw
DOMAIN-SUFFIX,youtube.com.ua
DOMAIN-SUFFIX,youtube.com.uy
DOMAIN-SUFFIX,youtube.com.ve
DOMAIN-SUFFIX,youtube.cr
DOMAIN-SUFFIX,youtube.cz
DOMAIN-SUFFIX,youtube.de
DOMAIN-SUFFIX,youtube.dk
DOMAIN-SUFFIX,youtube.ee
DOMAIN-SUFFIX,youtube.es
DOMAIN-SUFFIX,youtube.fi
DOMAIN-SUFFIX,youtube.fr
DOMAIN-SUFFIX,youtube.ge
DOMAIN-SUFFIX,youtube.googleapis.com
DOMAIN-SUFFIX,youtube.gr
DOMAIN-SUFFIX,youtube.gt
DOMAIN-SUFFIX,youtube.hk
DOMAIN-SUFFIX,youtube.hr
DOMAIN-SUFFIX,youtube.hu
DOMAIN-SUFFIX,youtube.ie
DOMAIN-SUFFIX,youtube.in
DOMAIN-SUFFIX,youtube.iq
DOMAIN-SUFFIX,youtube.is
DOMAIN-SUFFIX,youtube.it
DOMAIN-SUFFIX,youtube.jo
DOMAIN-SUFFIX,youtube.jp
DOMAIN-SUFFIX,youtube.kr
DOMAIN-SUFFIX,youtube.kz
DOMAIN-SUFFIX,youtube.la
DOMAIN-SUFFIX,youtube.lk
DOMAIN-SUFFIX,youtube.lt
DOMAIN-SUFFIX,youtube.lu
DOMAIN-SUFFIX,youtube.lv
DOMAIN-SUFFIX,youtube.ly
DOMAIN-SUFFIX,youtube.ma
DOMAIN-SUFFIX,youtube.md
DOMAIN-SUFFIX,youtube.me
DOMAIN-SUFFIX,youtube.mk
DOMAIN-SUFFIX,youtube.mn
DOMAIN-SUFFIX,youtube.mx
DOMAIN-SUFFIX,youtube.my
DOMAIN-SUFFIX,youtube.ng
DOMAIN-SUFFIX,youtube.ni
DOMAIN-SUFFIX,youtube.nl
DOMAIN-SUFFIX,youtube.no
DOMAIN-SUFFIX,youtube.pa
DOMAIN-SUFFIX,youtube.pe
DOMAIN-SUFFIX,youtube.ph
DOMAIN-SUFFIX,youtube.pk
DOMAIN-SUFFIX,youtube.pl
DOMAIN-SUFFIX,youtube.pr
DOMAIN-SUFFIX,youtube.pt
DOMAIN-SUFFIX,youtube.qa
DOMAIN-SUFFIX,youtube.ro
DOMAIN-SUFFIX,youtube.rs
DOMAIN-SUFFIX,youtube.ru
DOMAIN-SUFFIX,youtube.sa
DOMAIN-SUFFIX,youtube.se
DOMAIN-SUFFIX,youtube.sg
DOMAIN-SUFFIX,youtube.si
DOMAIN-SUFFIX,youtube.sk
DOMAIN-SUFFIX,youtube.sn
DOMAIN-SUFFIX,youtube.soy
DOMAIN-SUFFIX,youtube.sv
DOMAIN-SUFFIX,youtube.tn
DOMAIN-SUFFIX,youtube.tv
DOMAIN-SUFFIX,youtube.ua
DOMAIN-SUFFIX,youtube.ug
DOMAIN-SUFFIX,youtube.uy
DOMAIN-SUFFIX,youtube.vn
DOMAIN-SUFFIX,youtubeeducation.com
DOMAIN-SUFFIX,youtubeembeddedplayer.googleapis.com
DOMAIN-SUFFIX,youtubefanfest.com
DOMAIN-SUFFIX,youtubegaming.com
DOMAIN-SUFFIX,youtubego.co.id
DOMAIN-SUFFIX,youtubego.co.in
DOMAIN-SUFFIX,youtubego.com
DOMAIN-SUFFIX,youtubego.com.br
DOMAIN-SUFFIX,youtubego.id
DOMAIN-SUFFIX,youtubego.in
DOMAIN-SUFFIX,youtubei.googleapis.com
DOMAIN-SUFFIX,youtubekids.com
DOMAIN-SUFFIX,youtubemobilesupport.com
DOMAIN-SUFFIX,yt.be
DOMAIN-SUFFIX,ytimg.com
DOMAIN-KEYWORD,youtube
IP-CIDR,172.110.32.0/21,no-resolve
IP-CIDR,216.73.80.0/20,no-resolve
IP-CIDR6,2620:120:e000::/40,no-resolve
USER-AGENT,*YouTubeMusic*
USER-AGENT,*com.google.ios.youtubemusic*
USER-AGENT,*youtube*
USER-AGENT,YouTube*
USER-AGENT,YouTubeMusic*
USER-AGENT,com.google.ios.youtube*
USER-AGENT,com.google.ios.youtubemusic*
+194
View File
@@ -0,0 +1,194 @@
# NAME: YouTube
# AUTHOR: gitea-shunt-rules-mihomo
# SOURCE: rule/Surge/YouTube/YouTube.list
# UPDATED: 2026-04-06 05:38:50 UTC
# DOMAIN-KEYWORD: 1
# DOMAIN-SUFFIX: 179
# IP-CIDR: 2
# IP-CIDR6: 1
# SKIPPED-USER-AGENT: 7
# TOTAL: 183
payload:
- DOMAIN-SUFFIX,ggpht.cn
- DOMAIN-SUFFIX,ggpht.com
- DOMAIN-SUFFIX,googlevideo.com
- DOMAIN-SUFFIX,gvt1.com
- DOMAIN-SUFFIX,gvt2.com
- DOMAIN-SUFFIX,video.google.com
- DOMAIN-SUFFIX,wide-youtube.l.google.com
- DOMAIN-SUFFIX,withyoutube.com
- DOMAIN-SUFFIX,youtu.be
- DOMAIN-SUFFIX,youtube
- DOMAIN-SUFFIX,youtube-nocookie.com
- DOMAIN-SUFFIX,youtube-ui.l.google.com
- DOMAIN-SUFFIX,youtube.ae
- DOMAIN-SUFFIX,youtube.al
- DOMAIN-SUFFIX,youtube.am
- DOMAIN-SUFFIX,youtube.at
- DOMAIN-SUFFIX,youtube.az
- DOMAIN-SUFFIX,youtube.ba
- DOMAIN-SUFFIX,youtube.be
- DOMAIN-SUFFIX,youtube.bg
- DOMAIN-SUFFIX,youtube.bh
- DOMAIN-SUFFIX,youtube.bo
- DOMAIN-SUFFIX,youtube.by
- DOMAIN-SUFFIX,youtube.ca
- DOMAIN-SUFFIX,youtube.cat
- DOMAIN-SUFFIX,youtube.ch
- DOMAIN-SUFFIX,youtube.cl
- DOMAIN-SUFFIX,youtube.co
- DOMAIN-SUFFIX,youtube.co.ae
- DOMAIN-SUFFIX,youtube.co.at
- DOMAIN-SUFFIX,youtube.co.cr
- DOMAIN-SUFFIX,youtube.co.hu
- DOMAIN-SUFFIX,youtube.co.id
- DOMAIN-SUFFIX,youtube.co.il
- DOMAIN-SUFFIX,youtube.co.in
- DOMAIN-SUFFIX,youtube.co.jp
- DOMAIN-SUFFIX,youtube.co.ke
- DOMAIN-SUFFIX,youtube.co.kr
- DOMAIN-SUFFIX,youtube.co.ma
- DOMAIN-SUFFIX,youtube.co.nz
- DOMAIN-SUFFIX,youtube.co.th
- DOMAIN-SUFFIX,youtube.co.tz
- DOMAIN-SUFFIX,youtube.co.ug
- DOMAIN-SUFFIX,youtube.co.uk
- DOMAIN-SUFFIX,youtube.co.ve
- DOMAIN-SUFFIX,youtube.co.za
- DOMAIN-SUFFIX,youtube.co.zw
- DOMAIN-SUFFIX,youtube.com
- DOMAIN-SUFFIX,youtube.com.ar
- DOMAIN-SUFFIX,youtube.com.au
- DOMAIN-SUFFIX,youtube.com.az
- DOMAIN-SUFFIX,youtube.com.bd
- DOMAIN-SUFFIX,youtube.com.bh
- DOMAIN-SUFFIX,youtube.com.bo
- DOMAIN-SUFFIX,youtube.com.br
- DOMAIN-SUFFIX,youtube.com.by
- DOMAIN-SUFFIX,youtube.com.co
- DOMAIN-SUFFIX,youtube.com.do
- DOMAIN-SUFFIX,youtube.com.ec
- DOMAIN-SUFFIX,youtube.com.ee
- DOMAIN-SUFFIX,youtube.com.eg
- DOMAIN-SUFFIX,youtube.com.es
- DOMAIN-SUFFIX,youtube.com.gh
- DOMAIN-SUFFIX,youtube.com.gr
- DOMAIN-SUFFIX,youtube.com.gt
- DOMAIN-SUFFIX,youtube.com.hk
- DOMAIN-SUFFIX,youtube.com.hn
- DOMAIN-SUFFIX,youtube.com.hr
- DOMAIN-SUFFIX,youtube.com.jm
- DOMAIN-SUFFIX,youtube.com.jo
- DOMAIN-SUFFIX,youtube.com.kw
- DOMAIN-SUFFIX,youtube.com.lb
- DOMAIN-SUFFIX,youtube.com.lv
- DOMAIN-SUFFIX,youtube.com.ly
- DOMAIN-SUFFIX,youtube.com.mk
- DOMAIN-SUFFIX,youtube.com.mt
- DOMAIN-SUFFIX,youtube.com.mx
- DOMAIN-SUFFIX,youtube.com.my
- DOMAIN-SUFFIX,youtube.com.ng
- DOMAIN-SUFFIX,youtube.com.ni
- DOMAIN-SUFFIX,youtube.com.om
- DOMAIN-SUFFIX,youtube.com.pa
- DOMAIN-SUFFIX,youtube.com.pe
- DOMAIN-SUFFIX,youtube.com.ph
- DOMAIN-SUFFIX,youtube.com.pk
- DOMAIN-SUFFIX,youtube.com.pt
- DOMAIN-SUFFIX,youtube.com.py
- DOMAIN-SUFFIX,youtube.com.qa
- DOMAIN-SUFFIX,youtube.com.ro
- DOMAIN-SUFFIX,youtube.com.sa
- DOMAIN-SUFFIX,youtube.com.sg
- DOMAIN-SUFFIX,youtube.com.sv
- DOMAIN-SUFFIX,youtube.com.tn
- DOMAIN-SUFFIX,youtube.com.tr
- DOMAIN-SUFFIX,youtube.com.tw
- DOMAIN-SUFFIX,youtube.com.ua
- DOMAIN-SUFFIX,youtube.com.uy
- DOMAIN-SUFFIX,youtube.com.ve
- DOMAIN-SUFFIX,youtube.cr
- DOMAIN-SUFFIX,youtube.cz
- DOMAIN-SUFFIX,youtube.de
- DOMAIN-SUFFIX,youtube.dk
- DOMAIN-SUFFIX,youtube.ee
- DOMAIN-SUFFIX,youtube.es
- DOMAIN-SUFFIX,youtube.fi
- DOMAIN-SUFFIX,youtube.fr
- DOMAIN-SUFFIX,youtube.ge
- DOMAIN-SUFFIX,youtube.googleapis.com
- DOMAIN-SUFFIX,youtube.gr
- DOMAIN-SUFFIX,youtube.gt
- DOMAIN-SUFFIX,youtube.hk
- DOMAIN-SUFFIX,youtube.hr
- DOMAIN-SUFFIX,youtube.hu
- DOMAIN-SUFFIX,youtube.ie
- DOMAIN-SUFFIX,youtube.in
- DOMAIN-SUFFIX,youtube.iq
- DOMAIN-SUFFIX,youtube.is
- DOMAIN-SUFFIX,youtube.it
- DOMAIN-SUFFIX,youtube.jo
- DOMAIN-SUFFIX,youtube.jp
- DOMAIN-SUFFIX,youtube.kr
- DOMAIN-SUFFIX,youtube.kz
- DOMAIN-SUFFIX,youtube.la
- DOMAIN-SUFFIX,youtube.lk
- DOMAIN-SUFFIX,youtube.lt
- DOMAIN-SUFFIX,youtube.lu
- DOMAIN-SUFFIX,youtube.lv
- DOMAIN-SUFFIX,youtube.ly
- DOMAIN-SUFFIX,youtube.ma
- DOMAIN-SUFFIX,youtube.md
- DOMAIN-SUFFIX,youtube.me
- DOMAIN-SUFFIX,youtube.mk
- DOMAIN-SUFFIX,youtube.mn
- DOMAIN-SUFFIX,youtube.mx
- DOMAIN-SUFFIX,youtube.my
- DOMAIN-SUFFIX,youtube.ng
- DOMAIN-SUFFIX,youtube.ni
- DOMAIN-SUFFIX,youtube.nl
- DOMAIN-SUFFIX,youtube.no
- DOMAIN-SUFFIX,youtube.pa
- DOMAIN-SUFFIX,youtube.pe
- DOMAIN-SUFFIX,youtube.ph
- DOMAIN-SUFFIX,youtube.pk
- DOMAIN-SUFFIX,youtube.pl
- DOMAIN-SUFFIX,youtube.pr
- DOMAIN-SUFFIX,youtube.pt
- DOMAIN-SUFFIX,youtube.qa
- DOMAIN-SUFFIX,youtube.ro
- DOMAIN-SUFFIX,youtube.rs
- DOMAIN-SUFFIX,youtube.ru
- DOMAIN-SUFFIX,youtube.sa
- DOMAIN-SUFFIX,youtube.se
- DOMAIN-SUFFIX,youtube.sg
- DOMAIN-SUFFIX,youtube.si
- DOMAIN-SUFFIX,youtube.sk
- DOMAIN-SUFFIX,youtube.sn
- DOMAIN-SUFFIX,youtube.soy
- DOMAIN-SUFFIX,youtube.sv
- DOMAIN-SUFFIX,youtube.tn
- DOMAIN-SUFFIX,youtube.tv
- DOMAIN-SUFFIX,youtube.ua
- DOMAIN-SUFFIX,youtube.ug
- DOMAIN-SUFFIX,youtube.uy
- DOMAIN-SUFFIX,youtube.vn
- DOMAIN-SUFFIX,youtubeeducation.com
- DOMAIN-SUFFIX,youtubeembeddedplayer.googleapis.com
- DOMAIN-SUFFIX,youtubefanfest.com
- DOMAIN-SUFFIX,youtubegaming.com
- DOMAIN-SUFFIX,youtubego.co.id
- DOMAIN-SUFFIX,youtubego.co.in
- DOMAIN-SUFFIX,youtubego.com
- DOMAIN-SUFFIX,youtubego.com.br
- DOMAIN-SUFFIX,youtubego.id
- DOMAIN-SUFFIX,youtubego.in
- DOMAIN-SUFFIX,youtubei.googleapis.com
- DOMAIN-SUFFIX,youtubekids.com
- DOMAIN-SUFFIX,youtubemobilesupport.com
- DOMAIN-SUFFIX,yt.be
- DOMAIN-SUFFIX,ytimg.com
- DOMAIN-KEYWORD,youtube
- IP-CIDR,172.110.32.0/21
- IP-CIDR,216.73.80.0/20
- IP-CIDR6,2620:120:e000::/40
+1 -1
View File
@@ -1,7 +1,7 @@
# NAME: YouTube # NAME: YouTube
# AUTHOR: gitea-shunt-rules # AUTHOR: gitea-shunt-rules
# SOURCE: rule/Surge/YouTube/YouTube.list # SOURCE: rule/Surge/YouTube/YouTube.list
# UPDATED: 2026-04-06 05:01:32 UTC # UPDATED: 2026-04-06 05:38:50 UTC
# DOMAIN-KEYWORD: 1 # DOMAIN-KEYWORD: 1
# DOMAIN-SUFFIX: 179 # DOMAIN-SUFFIX: 179
# IP-CIDR: 2 # IP-CIDR: 2
+98 -18
View File
@@ -24,7 +24,10 @@ DEFAULT_LIMIT = 100
UNSUPPORTED_CLASH_TYPES = { UNSUPPORTED_CLASH_TYPES = {
"USER-AGENT", "USER-AGENT",
"URL-REGEX", "URL-REGEX",
"DEST-PORT", # Surge alias, Clash usually uses DST-PORT }
UNSUPPORTED_MIHOMO_TYPES = {
"USER-AGENT",
"URL-REGEX",
} }
@@ -41,6 +44,7 @@ class Config:
include_categories: list[str] include_categories: list[str]
exclude_categories: list[str] exclude_categories: list[str]
clash_no_resolve: bool clash_no_resolve: bool
mihomo_no_resolve: bool
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -152,6 +156,7 @@ def load_config(path: Path) -> Config:
include_categories=source.get("include_categories", []), include_categories=source.get("include_categories", []),
exclude_categories=source.get("exclude_categories", []), exclude_categories=source.get("exclude_categories", []),
clash_no_resolve=output.get("clash_no_resolve", False), clash_no_resolve=output.get("clash_no_resolve", False),
mihomo_no_resolve=output.get("mihomo_no_resolve", False),
) )
@@ -177,19 +182,25 @@ def parse_rules(content: str) -> list[RuleLine]:
return rules return rules
def to_clash_payload_line(rule: RuleLine, no_resolve: bool) -> str | None: def to_payload_line(
rule: RuleLine,
no_resolve: bool,
unsupported_types: set[str],
type_mapping: dict[str, str] | None = None,
) -> str | None:
parts = [p.strip() for p in rule.raw.split(",") if p.strip()] parts = [p.strip() for p in rule.raw.split(",") if p.strip()]
if not parts: if not parts:
return None return None
rule_type = parts[0].upper() rule_type = parts[0].upper()
parts[0] = rule_type mapped_type = (type_mapping or {}).get(rule_type, rule_type)
parts[0] = mapped_type
if rule_type in UNSUPPORTED_CLASH_TYPES: if rule_type in unsupported_types:
return None return None
if rule_type in {"IP-CIDR", "IP-CIDR6"}: if mapped_type in {"IP-CIDR", "IP-CIDR6"}:
payload = [rule_type, parts[1]] if len(parts) >= 2 else parts payload = [mapped_type, parts[1]] if len(parts) >= 2 else parts
if no_resolve: if no_resolve:
payload.append("no-resolve") payload.append("no-resolve")
return ",".join(payload) return ",".join(payload)
@@ -217,7 +228,33 @@ def format_surge(name: str, rules: list[RuleLine], source_path: str) -> str:
return "\n".join(header + body) + "\n" return "\n".join(header + body) + "\n"
def format_clash(name: str, rules: list[RuleLine], source_path: str, no_resolve: bool) -> str: def format_loon(name: str, rules: list[RuleLine], source_path: str) -> str:
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
count = Counter(rule.rule_type for rule in rules)
header = [
f"# NAME: {name}",
"# AUTHOR: gitea-shunt-rules",
f"# SOURCE: {source_path}",
f"# UPDATED: {now}",
]
for k in sorted(count):
header.append(f"# {k}: {count[k]}")
header.append(f"# TOTAL: {len(rules)}")
body = [rule.raw for rule in rules]
return "\n".join(header + body) + "\n"
def format_yaml_payload(
name: str,
rules: list[RuleLine],
source_path: str,
no_resolve: bool,
unsupported_types: set[str],
type_mapping: dict[str, str] | None = None,
author_name: str = "gitea-shunt-rules",
) -> tuple[str, int]:
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
payload: list[str] = [] payload: list[str] = []
@@ -225,7 +262,12 @@ def format_clash(name: str, rules: list[RuleLine], source_path: str, no_resolve:
skipped: Counter[str] = Counter() skipped: Counter[str] = Counter()
for rule in rules: for rule in rules:
converted = to_clash_payload_line(rule, no_resolve=no_resolve) converted = to_payload_line(
rule,
no_resolve=no_resolve,
unsupported_types=unsupported_types,
type_mapping=type_mapping,
)
if converted is None: if converted is None:
skipped[rule.rule_type] += 1 skipped[rule.rule_type] += 1
continue continue
@@ -234,7 +276,7 @@ def format_clash(name: str, rules: list[RuleLine], source_path: str, no_resolve:
lines = [ lines = [
f"# NAME: {name}", f"# NAME: {name}",
"# AUTHOR: gitea-shunt-rules", f"# AUTHOR: {author_name}",
f"# SOURCE: {source_path}", f"# SOURCE: {source_path}",
f"# UPDATED: {now}", f"# UPDATED: {now}",
] ]
@@ -246,7 +288,7 @@ def format_clash(name: str, rules: list[RuleLine], source_path: str, no_resolve:
lines.append("payload:") lines.append("payload:")
lines.extend(f" - {item}" for item in payload) lines.extend(f" - {item}" for item in payload)
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n", len(payload)
def should_include_category(name: str, cfg: Config, cli_names: set[str]) -> bool: def should_include_category(name: str, cfg: Config, cli_names: set[str]) -> bool:
@@ -285,7 +327,7 @@ def find_categories(client: GiteaClient, cfg: Config, cli_names: set[str]) -> li
return sorted(categories) return sorted(categories)
def build_one_category(client: GiteaClient, cfg: Config, name: str, base_out: Path) -> tuple[int, int]: def build_one_category(client: GiteaClient, cfg: Config, name: str, base_out: Path) -> tuple[int, int, int, int]:
filename_base = cfg.source_filename_pattern.format(name=name) filename_base = cfg.source_filename_pattern.format(name=name)
candidate_filenames = [ candidate_filenames = [
filename_base, filename_base,
@@ -318,21 +360,48 @@ def build_one_category(client: GiteaClient, cfg: Config, name: str, base_out: Pa
rules = parse_rules(source_content) rules = parse_rules(source_content)
surge_out = base_out / "surge" / f"{name}.list" surge_out = base_out / "surge" / f"{name}.list"
loon_out = base_out / "loon" / f"{name}.list"
clash_out = base_out / "clash" / f"{name}.yaml" clash_out = base_out / "clash" / f"{name}.yaml"
mihomo_out = base_out / "mihomo" / f"{name}.yaml"
surge_out.parent.mkdir(parents=True, exist_ok=True) surge_out.parent.mkdir(parents=True, exist_ok=True)
loon_out.parent.mkdir(parents=True, exist_ok=True)
clash_out.parent.mkdir(parents=True, exist_ok=True) clash_out.parent.mkdir(parents=True, exist_ok=True)
mihomo_out.parent.mkdir(parents=True, exist_ok=True)
surge_out.write_text(format_surge(name, rules, source_rel_path), encoding="utf-8") surge_out.write_text(format_surge(name, rules, source_rel_path), encoding="utf-8")
clash_out.write_text( loon_out.write_text(format_loon(name, rules, source_rel_path), encoding="utf-8")
format_clash(name, rules, source_rel_path, no_resolve=cfg.clash_no_resolve),
clash_text, clash_cnt = format_yaml_payload(
name,
rules,
source_rel_path,
no_resolve=cfg.clash_no_resolve,
unsupported_types=UNSUPPORTED_CLASH_TYPES,
type_mapping={"DEST-PORT": "DST-PORT"},
author_name="gitea-shunt-rules",
)
clash_out.write_text(clash_text, encoding="utf-8")
mihomo_text, mihomo_cnt = format_yaml_payload(
name,
rules,
source_rel_path,
no_resolve=cfg.mihomo_no_resolve,
unsupported_types=UNSUPPORTED_MIHOMO_TYPES,
type_mapping={"DEST-PORT": "DST-PORT"},
author_name="gitea-shunt-rules-mihomo",
)
mihomo_out.write_text(
mihomo_text,
encoding="utf-8", encoding="utf-8",
) )
return len(rules), sum(1 for r in rules if to_clash_payload_line(r, no_resolve=cfg.clash_no_resolve) is not None) # source and loon keep the same parsed set
return len(rules), len(rules), clash_cnt, mihomo_cnt
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(description="Generate Surge/Clash rules from Gitea source repo.") p = argparse.ArgumentParser(description="Generate Surge/Loon/Clash/Mihomo rules from Gitea source repo.")
p.add_argument("--config", default="config.toml", help="Path to config TOML file") p.add_argument("--config", default="config.toml", help="Path to config TOML file")
p.add_argument("--names", default="", help="Comma-separated category names, e.g. YouTube,Netflix") p.add_argument("--names", default="", help="Comma-separated category names, e.g. YouTube,Netflix")
return p.parse_args() return p.parse_args()
@@ -354,19 +423,30 @@ def main() -> int:
out_dir.mkdir(parents=True, exist_ok=True) out_dir.mkdir(parents=True, exist_ok=True)
total_source = 0 total_source = 0
total_loon = 0
total_clash = 0 total_clash = 0
total_mihomo = 0
print(f"Found {len(categories)} categories under {cfg.source_root}") print(f"Found {len(categories)} categories under {cfg.source_root}")
for idx, name in enumerate(categories, start=1): for idx, name in enumerate(categories, start=1):
try: try:
s_cnt, c_cnt = build_one_category(client, cfg, name, out_dir) s_cnt, l_cnt, c_cnt, m_cnt = build_one_category(client, cfg, name, out_dir)
total_source += s_cnt total_source += s_cnt
total_loon += l_cnt
total_clash += c_cnt total_clash += c_cnt
print(f"[{idx}/{len(categories)}] {name}: source={s_cnt}, clash={c_cnt}") total_mihomo += m_cnt
print(f"[{idx}/{len(categories)}] {name}: source={s_cnt}, loon={l_cnt}, clash={c_cnt}, mihomo={m_cnt}")
except Exception as exc: except Exception as exc:
print(f"[{idx}/{len(categories)}] {name}: failed: {exc}", file=sys.stderr) print(f"[{idx}/{len(categories)}] {name}: failed: {exc}", file=sys.stderr)
print(f"Done. source_rules={total_source}, clash_rules={total_clash}, output={out_dir.resolve()}") print(
"Done. "
f"source_rules={total_source}, "
f"loon_rules={total_loon}, "
f"clash_rules={total_clash}, "
f"mihomo_rules={total_mihomo}, "
f"output={out_dir.resolve()}"
)
return 0 return 0