mirror of
https://github.com/ihabunek/twitch-dl
synced 2024-08-30 18:32:25 +00:00
149 lines
3.6 KiB
Python
Executable File
149 lines
3.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Auto-generates documentation from command defs in console.py.
|
|
"""
|
|
|
|
import click
|
|
import html
|
|
import os
|
|
import re
|
|
import shutil
|
|
import textwrap
|
|
|
|
from click import Command
|
|
|
|
from twitchdl.cli import cli
|
|
|
|
|
|
START_MARKER = "<!-- ------------------- generated docs start ------------------- -->"
|
|
END_MARKER = "<!-- ------------------- generated docs end ------------------- -->"
|
|
|
|
|
|
def main():
|
|
update_changelog()
|
|
|
|
parent_ctx = click.Context(cli, info_name="twitch-dl")
|
|
for name, command in cli.commands.items():
|
|
ctx = click.Context(cli, info_name=name, parent=parent_ctx)
|
|
update_docs(command, ctx)
|
|
|
|
|
|
def update_changelog():
|
|
print("Updating: docs/changelog.md")
|
|
root = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
|
|
source = os.path.join(root, "CHANGELOG.md")
|
|
target = os.path.join(root, "docs/changelog.md")
|
|
shutil.copy(source, target)
|
|
|
|
|
|
def update_docs(command: Command, ctx: click.Context):
|
|
path = os.path.join("docs", "commands", f"{command.name}.md")
|
|
content = render_command(command, ctx)
|
|
|
|
if not os.path.exists(path):
|
|
print(f"Creating: {path}")
|
|
write(path, content)
|
|
else:
|
|
print(f"Updating: {path}")
|
|
[_, handwritten] = read(path).split(END_MARKER)
|
|
content = f"{content.strip()}\n\n{END_MARKER}\n\n{handwritten.strip()}"
|
|
write(path, content)
|
|
|
|
|
|
def render_command(command: Command, ctx: click.Context):
|
|
content = START_MARKER
|
|
content += f"\n# twitch-dl {command.name}\n\n"
|
|
|
|
if command.help:
|
|
content += command.help + "\n\n"
|
|
|
|
content += render_usage(ctx, command)
|
|
content += render_options(ctx, command)
|
|
return content
|
|
|
|
|
|
def render_usage(ctx: click.Context, command: Command):
|
|
content = "### USAGE\n\n"
|
|
content += "```\n"
|
|
content += command.get_usage(ctx).replace("Usage: ", "")
|
|
|
|
content += "\n```\n\n"
|
|
return content
|
|
|
|
|
|
def render_options(ctx, command: Command):
|
|
options = list(get_options(command))
|
|
|
|
if not options:
|
|
return ""
|
|
|
|
content = "### OPTIONS\n\n"
|
|
|
|
content += "<table>\n"
|
|
content += "<tbody>"
|
|
for opts, help in options:
|
|
content += textwrap.dedent(f"""
|
|
<tr>
|
|
<td class="code">{escape(opts)}</td>
|
|
<td>{escape(help)}</td>
|
|
</tr>
|
|
""")
|
|
content += "</tbody>\n"
|
|
content += "</table>\n\n"
|
|
|
|
return content
|
|
|
|
|
|
def get_options(command: Command):
|
|
for option in command.params:
|
|
if isinstance(option, click.Option):
|
|
opts = ", ".join(option.opts)
|
|
opts += option_type(option)
|
|
|
|
help = option.help or ""
|
|
help = re.sub(r"\s+", " ", help)
|
|
help += choices(option)
|
|
if option.default:
|
|
help += f" [default: `{option.default}`]"
|
|
|
|
yield opts, help
|
|
|
|
|
|
def option_type(option: click.Option):
|
|
match option.type:
|
|
case click.types.StringParamType():
|
|
return " TEXT"
|
|
case click.types.Choice():
|
|
return " TEXT"
|
|
case click.types.IntParamType():
|
|
return " INTEGER"
|
|
case _:
|
|
return ""
|
|
|
|
def choices(option: click.Option):
|
|
if isinstance(option.type, click.Choice):
|
|
choices = ", ".join(f"`{c}`" for c in option.type.choices)
|
|
return f" Possible values: {choices}."
|
|
return ""
|
|
|
|
|
|
def read(path):
|
|
with open(path, "r") as f:
|
|
return f.read()
|
|
|
|
|
|
def write(path, content):
|
|
with open(path, "w") as f:
|
|
return f.write(content)
|
|
|
|
|
|
def escape(text: str):
|
|
text = html.escape(text)
|
|
text = re.sub(r"`([\S]+)`", "<code>\\1</code>", text)
|
|
return text
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|