aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorWolfgang Müller2024-10-26 20:59:35 +0200
committerWolfgang Müller2024-11-06 21:29:59 +0100
commitec4750b5e2f801fec7e1100299114483d4a04f4e (patch)
tree6f4f36d43f25ae69c022aa1bf1ac08cf3a2fae48
parent9e7ede5afcbc99a3670ee8232ecfbce79da0dde8 (diff)
downloadlater-ec4750b5e2f801fec7e1100299114483d4a04f4e.tar.gz
Use getopt-based parser for command-line arguments
Upcoming commits will want to make use of bespoke argument handling alongside subcommands and using Python's argparse proved unable to easily handle that kind of complexity. Therefore switch to the more basic getopt-based parser with which we can implement our own logic. Without argparse we're no longer bound to having an autogenerated help listing, so stop mentioning that in the manual. Instead have later(1) output a concise usage listing when given an unknown argument. Since we'll be adding more commands to later(1) in the future, also specify an explicit "list" command.
-rwxr-xr-xlater89
-rw-r--r--later.120
2 files changed, 87 insertions, 22 deletions
diff --git a/later b/later
index a5a25c8..3eb9ea0 100755
--- a/later
+++ b/later
@@ -1,13 +1,14 @@
#!/usr/bin/env python3
-import argparse
import datetime
+import getopt
import json
import os
import re
import sys
-from dataclasses import dataclass
+from dataclasses import dataclass, field
from datetime import datetime as dt
+from typing import Protocol
class YTDLPLogger:
@@ -25,6 +26,18 @@ def exit(message=""):
sys.exit(f"later: {message}" if message else 0)
+def usage(message=""):
+ lines = []
+ if message:
+ lines.append(f"later: {message}")
+ lines.append("")
+
+ lines.append("usage: later [options] [list]")
+ lines.append("options: [-u | --update-titles]")
+
+ sys.exit("\n".join(lines))
+
+
def get_xdg(directory, fallback):
env = f"XDG_{directory.upper()}_HOME"
if env in os.environ:
@@ -138,6 +151,54 @@ class TitleMap:
exit(f"cannot write title cache: {err}")
+class CommandFunction(Protocol):
+ def __call__(self, args: "Arguments", title_map: TitleMap) -> None:
+ pass
+
+
+@dataclass
+class Command:
+ name: str
+ fun: CommandFunction
+ implies_list: bool = True
+ args: bool = True
+
+
+@dataclass
+class Arguments:
+ command: Command
+ update_titles: bool = False
+ rest: list[str] = field(default_factory=list)
+
+
+def parse_args(argv, commands, default):
+ try:
+ options, args = getopt.gnu_getopt(argv, "u", "update-titles")
+ except getopt.GetoptError as e:
+ usage(e)
+
+ parsed_args = Arguments(command=commands[default])
+
+ for option in options:
+ match option:
+ case ("-u", _) | ("--update-titles", _):
+ parsed_args.update_titles = True
+
+ if args:
+ if args[0] in commands:
+ parsed_args.command = commands[args.pop(0)]
+
+ parsed_args.rest = args
+
+ if parsed_args.rest and not parsed_args.command.args:
+ usage(f'unexpected argument for "{parsed_args.command.name}"')
+
+ if not parsed_args.rest and parsed_args.command.args:
+ usage(f'"{parsed_args.command.name}" requires an argument')
+
+ return parsed_args
+
+
def entries(title_map):
def get_mtime(entry):
return entry.stat().st_mtime
@@ -163,20 +224,20 @@ def entries(title_map):
yield WatchLaterEntry(name=name, path=entry.path, mtime=mtime)
-parser = argparse.ArgumentParser(
- prog="later", description="List mpv's watch_later entries"
-)
-parser.add_argument(
- "-u",
- "--update-titles",
- action="store_true",
- help="update titles of videos using yt-dlp",
-)
-args = parser.parse_args()
+def list_entries(args, title_map):
+ for entry in entries(title_map):
+ print(entry.format(title_map))
+
+
+commands = {"list": Command("list", list_entries, implies_list=False, args=False)}
+
+args = parse_args(sys.argv[1:], commands, "list")
title_map = TitleMap(title_map_file, update=args.update_titles)
-for entry in entries(title_map):
- print(entry.format(title_map))
+args.command.fun(args=args, title_map=title_map)
+
+if args.command.implies_list:
+ list_entries(args, title_map)
title_map.maybe_commit()
diff --git a/later.1 b/later.1
index 4464da5..a4be6b0 100644
--- a/later.1
+++ b/later.1
@@ -1,4 +1,4 @@
-.Dd September 20, 2024
+.Dd October 26, 2024
.Dt LATER 1
.Os
.Sh NAME
@@ -6,12 +6,11 @@
.Nd list mpv's watch_later entries
.Sh SYNOPSIS
.Nm
-.Op Fl u | \-update-titles
-.Nm
-.Fl h | \-help
+.Op options
+.Op Ic list
.Sh DESCRIPTION
.Nm
-lists all watch_later entries as saved by
+is a program to manage watch_later entries as created by
.Xr mpv 1 .
.Pp
In order to display file names and URLs for an entry, the
@@ -19,12 +18,17 @@ In order to display file names and URLs for an entry, the
option must be set in
.Xr mpv 1 .
.Pp
-The options are as follows:
+The following options can be given for any command:
.Bl -tag -width "-h, --update-titles"
.It Fl u , Fl \-update-titles
update titles of videos using yt-dlp
-.It Fl h , Fl \-help
-show a concise help message
+.El
+.Pp
+The commands are as follows:
+.Bl -tag -width Ds
+.It Sy list
+Lists all watch_later entries.
+This command is implied if there are no remaining arguments.
.El
.Sh FILES
.Bl -tag -width "1"