mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-06 11:57:31 +08:00
211 lines
7.5 KiB
Python
211 lines
7.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Code generator for AnySearch CLI scripts.
|
|
|
|
Reads constants.json from scripts/shared/ and injects the domain list
|
|
and doc command implementation into each CLI script. Eliminates duplication
|
|
across all 4 language implementations.
|
|
|
|
Usage:
|
|
python scripts/generate.py # Generate all scripts
|
|
python scripts/generate.py --check # Verify scripts are up-to-date (for CI)
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
SHARED_DIR = os.path.join(SCRIPT_DIR, "shared")
|
|
|
|
# --- Marker format per language ---
|
|
# Each script uses paired comments to delimit generated sections:
|
|
# BEGIN GENERATED:<section_name>
|
|
# ... generated content ...
|
|
# END GENERATED:<section_name>
|
|
|
|
MARKERS = {
|
|
".py": ("# BEGIN GENERATED:{name}", "# END GENERATED:{name}"),
|
|
".js": ("// BEGIN GENERATED:{name}", "// END GENERATED:{name}"),
|
|
".ps1": ("# BEGIN GENERATED:{name}", "# END GENERATED:{name}"),
|
|
".sh": ("# BEGIN GENERATED:{name}", "# END GENERATED:{name}"),
|
|
}
|
|
|
|
|
|
def load_constants():
|
|
with open(os.path.join(SHARED_DIR, "constants.json"), "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
|
|
|
|
def render_constants(ext, constants):
|
|
"""Render constants block in the target language syntax."""
|
|
domains = constants["available_domains"]
|
|
|
|
if ext == ".py":
|
|
lines = []
|
|
lines.append("AVAILABLE_DOMAINS = [")
|
|
for i in range(0, len(domains), 6):
|
|
chunk = domains[i:i+6]
|
|
lines.append(" " + ", ".join(f'"{d}"' for d in chunk) + ",")
|
|
lines.append("]")
|
|
return "\n".join(lines)
|
|
|
|
elif ext == ".js":
|
|
lines = []
|
|
lines.append("const AVAILABLE_DOMAINS = [")
|
|
for i in range(0, len(domains), 6):
|
|
chunk = domains[i:i+6]
|
|
lines.append(" " + ",".join(f'"{d}"' for d in chunk) + ",")
|
|
lines.append("];")
|
|
return "\n".join(lines)
|
|
|
|
elif ext == ".ps1":
|
|
lines = []
|
|
lines.append("$AVAILABLE_DOMAINS = @(")
|
|
chunks = [domains[i:i+6] for i in range(0, len(domains), 6)]
|
|
for idx, chunk in enumerate(chunks):
|
|
suffix = "," if idx < len(chunks) - 1 else ""
|
|
lines.append(" " + ", ".join(f'"{d}"' for d in chunk) + suffix)
|
|
lines.append(")")
|
|
return "\n".join(lines)
|
|
|
|
elif ext == ".sh":
|
|
lines = []
|
|
lines.append("AVAILABLE_DOMAINS=(" + " ".join(f'"{d}"' for d in domains) + ")")
|
|
return "\n".join(lines)
|
|
|
|
raise ValueError(f"Unsupported extension: {ext}")
|
|
|
|
|
|
def render_doc_block(ext, constants):
|
|
"""Generate code that reads and renders doc_spec.md at runtime."""
|
|
if ext == ".py":
|
|
return '''def _render_doc():
|
|
import json as _json
|
|
_dir = os.path.dirname(os.path.abspath(__file__))
|
|
_shared = os.path.join(_dir, "shared")
|
|
with open(os.path.join(_shared, "doc_spec.md"), "r", encoding="utf-8") as _f:
|
|
_tpl = _f.read()
|
|
with open(os.path.join(_shared, "constants.json"), "r", encoding="utf-8") as _f:
|
|
_c = _json.load(_f)
|
|
_tpl = _tpl.replace("{{LANG_NAME}}", "Python")
|
|
_tpl = _tpl.replace("{{LANG_CODEBLOCK}}", "")
|
|
_tpl = _tpl.replace("{{LANG_INVOKE}}", "python scripts/anysearch_cli.py")
|
|
_tpl = _tpl.replace("{{DOMAINS_SPACE}}", " ".join(_c["available_domains"]))
|
|
return _tpl'''
|
|
|
|
elif ext == ".js":
|
|
return '''function renderDoc() {
|
|
const shared = path.join(__dirname, "shared");
|
|
let tpl = fs.readFileSync(path.join(shared, "doc_spec.md"), "utf-8");
|
|
const c = JSON.parse(fs.readFileSync(path.join(shared, "constants.json"), "utf-8"));
|
|
tpl = tpl.replace(/\\{\\{LANG_NAME\\}\\}/g, "Node.js");
|
|
tpl = tpl.replace(/\\{\\{LANG_CODEBLOCK\\}\\}/g, "");
|
|
tpl = tpl.replace(/\\{\\{LANG_INVOKE\\}\\}/g, "node scripts/anysearch_cli.js");
|
|
tpl = tpl.replace(/\\{\\{DOMAINS_SPACE\\}\\}/g, c.available_domains.join(" "));
|
|
return tpl;
|
|
}'''
|
|
|
|
elif ext == ".ps1":
|
|
return '''function Render-Doc {
|
|
$shared = Join-Path (Split-Path -Parent $MyInvocation.ScriptName) "shared"
|
|
$tpl = Get-Content (Join-Path $shared "doc_spec.md") -Raw -Encoding UTF8
|
|
$c = Get-Content (Join-Path $shared "constants.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$tpl = $tpl.Replace("{{LANG_NAME}}", "PowerShell")
|
|
$tpl = $tpl.Replace("{{LANG_CODEBLOCK}}", "powershell")
|
|
$tpl = $tpl.Replace("{{LANG_INVOKE}}", "powershell -ExecutionPolicy Bypass -File scripts/anysearch_cli.ps1")
|
|
$tpl = $tpl.Replace("{{DOMAINS_SPACE}}", ($c.available_domains -join " "))
|
|
return $tpl
|
|
}'''
|
|
|
|
elif ext == ".sh":
|
|
return r'''_cmd_doc() {
|
|
local shared="$SCRIPT_DIR/shared"
|
|
local tpl
|
|
tpl=$(cat "$shared/doc_spec.md")
|
|
local domains
|
|
domains=$(jq -r '.available_domains | join(" ")' "$shared/constants.json")
|
|
tpl="${tpl//\{\{LANG_NAME\}\}/Bash}"
|
|
tpl="${tpl//\{\{LANG_CODEBLOCK\}\}/bash}"
|
|
tpl="${tpl//\{\{LANG_INVOKE\}\}\}/bash scripts/anysearch_cli.sh}"
|
|
tpl="${tpl//\{\{DOMAINS_SPACE\}\}/$domains}"
|
|
printf '%s\n' "$tpl"
|
|
}'''
|
|
|
|
raise ValueError(f"Unsupported extension: {ext}")
|
|
|
|
|
|
def replace_marker_section(content, ext, section_name, new_text):
|
|
"""Replace everything between marker comments for section_name with new_text."""
|
|
begin_tag, end_tag = MARKERS[ext]
|
|
begin = begin_tag.format(name=section_name)
|
|
end = end_tag.format(name=section_name)
|
|
|
|
if begin not in content:
|
|
raise ValueError(f"BEGIN marker '{begin_tag.format(name=section_name)}' not found")
|
|
if end not in content:
|
|
raise ValueError(f"END marker '{end_tag.format(name=section_name)}' not found")
|
|
|
|
before, rest = content.split(begin, 1)
|
|
_, after = rest.split(end, 1)
|
|
return before + begin + "\n" + new_text + "\n" + end + after
|
|
|
|
|
|
def generate_script(script_path, constants):
|
|
"""Regenerate the constants and doc blocks in a CLI script."""
|
|
ext = os.path.splitext(script_path)[1]
|
|
if ext not in MARKERS:
|
|
raise ValueError(f"Unsupported extension: {ext}")
|
|
|
|
with open(script_path, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
constants_text = render_constants(ext, constants)
|
|
content = replace_marker_section(content, ext, "CONSTANTS", constants_text)
|
|
|
|
doc_block = render_doc_block(ext, constants)
|
|
content = replace_marker_section(content, ext, "DOC_SPEC", doc_block)
|
|
|
|
return content
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="Generate AnySearch CLI scripts from shared data")
|
|
parser.add_argument("--check", action="store_true", help="Verify scripts are up-to-date (for CI)")
|
|
args = parser.parse_args()
|
|
|
|
constants = load_constants()
|
|
|
|
scripts_changed = False
|
|
|
|
for ext in [".py", ".js", ".ps1", ".sh"]:
|
|
script_name = f"anysearch_cli{ext}"
|
|
script_path = os.path.join(SCRIPT_DIR, script_name)
|
|
|
|
try:
|
|
new_content = generate_script(script_path, constants)
|
|
with open(script_path, "r", encoding="utf-8") as f:
|
|
old_content = f.read()
|
|
|
|
if new_content != old_content:
|
|
scripts_changed = True
|
|
if not args.check:
|
|
with open(script_path, "w", encoding="utf-8") as f:
|
|
f.write(new_content)
|
|
print(f"Generated: {script_name}")
|
|
else:
|
|
print(f"CHANGED: {script_name} (run generate.py to update)")
|
|
else:
|
|
print(f"OK: {script_name}")
|
|
except Exception as e:
|
|
print(f"ERROR in {script_name}: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if args.check and scripts_changed:
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|