1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
#!/usr/bin/env python3
import os
import re
import xml.etree.ElementTree as ET
TAGS_FILE = "src/tags_plugin_name.hpp"
KCFG_DIR = "src/contents/kcfg"
OUTPUT_FILE = "src/contents/docs/database/plugins_properties.md"
def get_tags():
ids = []
if not os.path.exists(TAGS_FILE):
print(f"Error: {TAGS_FILE} not found.")
return ids
regex = re.compile(r'CREATE_PROPERTY\s*\(\s*QString\s*,\s*\w+\s*,\s*QStringLiteral\s*\(\s*"([^"]+)"\s*\)\s*\)')
with open(TAGS_FILE, 'r') as f:
for line in f:
if m := regex.search(line):
ids.append(m.group(1))
return sorted(ids)
def main():
if not os.path.exists(KCFG_DIR):
print(f"Error: Directory '{KCFG_DIR}' not found.")
exit(1)
tags = get_tags()
lines = [
"<!--",
" AUTOGENERATED FILE - DO NOT MODIFY MANUALLY.",
" This file is generated by: util/generate_command_docs.py",
"-->",
"",
"# Plugin Properties",
"",
"The following properties can be modified or queried via the [local server](../user_interface/local_server.md).",
"",
"| Plugin ID | Property | Type | Default |",
"| :--- | :--- | :--- | :--- |"
]
found_plugins = 0
for tag in tags:
kcfg_path = os.path.join(KCFG_DIR, f"easyeffects_db_{tag}.kcfg")
if not os.path.exists(kcfg_path):
continue
try:
tree = ET.parse(kcfg_path)
ns = {"kcfg": "http://www.kde.org/standards/kcfg/1.0"}
esc = lambda s: str(s).replace("|", r"\|") if s else ""
entries = {
entry.get('name'): entry
for group in tree.findall("kcfg:group", ns)
for entry in group.findall("kcfg:entry", ns)
}
enum_defs = {}
for name, entry in entries.items():
# case for StringList labels used with Int properties
if name.endswith("Labels") and entry.get('type') == 'StringList':
base = name[:-6] # strip 'Labels'
default_node = entry.find("kcfg:default", ns)
if default_node is not None and default_node.text:
enum_defs[base.lower()] = default_node.text.split(',')
# case for Enum properties as is
if entry.get('type') == "Enum":
choices_node = entry.find("kcfg:choices", ns)
if choices_node is not None:
labels = []
for c in choices_node.findall("kcfg:choice", ns):
lbl = c.find("kcfg:label", ns)
text = lbl.text if (lbl is not None and lbl.text) else c.get("name")
labels.append(text)
enum_defs[name.lower()] = labels
hidden_props = {
name for name in entries if name.lower()[:-6] in enum_defs and name.endswith("Labels")
}
for name, entry in entries.items():
if name in hidden_props:
continue
prop_type = entry.get('type')
default_val = "-"
default_node = entry.find("kcfg:default", ns)
if default_node is not None:
if default_node.get("code") == "true":
default_val = "*calculated*"
elif default_node.text:
default_val = default_node.text
choices = []
if prop_type in ("Int", "Enum"):
if name.lower() in enum_defs:
choices = enum_defs[name.lower()]
else:
# suffix match among enums (longest wins) for banded props
# e.g. band1StereoSplitSource -> StereoSplitSource Enum
matches = [k for k in enum_defs if name.lower().endswith(k)]
if matches:
best_key = max(matches, key=len)
choices = enum_defs[best_key]
def_col = f"`{esc(default_val)}`"
if choices:
prop_type = "Enum"
fmt_choices = [f"{i}: `{c}`" for i, c in enumerate(choices)]
choices_str = ", ".join(fmt_choices)
def_col += f" <br><small>(Choices: {choices_str})</small>"
lines.append(f"| **{esc(tag)}** | `{esc(name)}` | {esc(prop_type)} | {def_col} |")
found_plugins += 1
except Exception as e:
print(f"Error parsing {kcfg_path}: {e}")
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
f.write("\n".join(lines))
f.write("\n")
print(f"Documentation generated at: {OUTPUT_FILE} for {found_plugins} plugins.")
if __name__ == "__main__":
main()
|