corrscope/ovgenpy/cli.py

172 wiersze
5.3 KiB
Python
Czysty Zwykły widok Historia

from itertools import count
from pathlib import Path
from typing import Optional, List, Tuple
import click
from ovgenpy.channel import ChannelConfig
from ovgenpy.config import OvgenError, yaml
from ovgenpy.outputs import FFmpegOutputConfig, FFplayOutputConfig
from ovgenpy.ovgenpy import default_config, Config, Ovgen
Folder = click.Path(exists=True, file_okay=False)
File = click.Path(exists=True, dir_okay=False)
2018-08-20 01:53:13 +00:00
OutFile = click.Path(dir_okay=False)
# https://github.com/pallets/click/issues/473
# @platformio requires some functionality which doesn't work in Click 6.
# Click 6 is marked as stable, but http://click.pocoo.org/ redirects to /5/.
# wat
# If multiple `--` names are supplied to @click.option, the last one will be used.
# possible_names = [('-', 'w'), ('--', 'write')]
# possible_names.sort(key=lambda x: len(x[0]))
# name = possible_names[-1][1].replace('-', '_').lower()
# List of recognized Config file extensions.
YAML_EXTS = ['.yaml']
# Default extension when writing Config.
YAML_NAME = YAML_EXTS[0]
DEFAULT_CONFIG_PATH = Path('ovgenpy').with_suffix(YAML_NAME)
PROFILE_DUMP_NAME = 'cprofile'
@click.command()
# Inputs
@click.argument('files', nargs=-1)
# Override default .yaml settings (only if YAML file not supplied)
# Incorrect [option] name order: https://github.com/pallets/click/issues/793
@click.option('--audio', '-a', type=File, help=
'Config: Input path for master audio file')
2018-08-20 01:53:13 +00:00
@click.option('--video-output', '-o', type=OutFile, help=
'Config: Output video path')
# Disables GUI
@click.option('--write', '-w', is_flag=True, help=
"Write config YAML file to path (don't open GUI).")
@click.option('--play', '-p', is_flag=True, help=
"Preview or render (don't open GUI).")
# Debugging
@click.option('--profile', is_flag=True, help=
'Debug: Write CProfiler snapshot')
def main(
files: Tuple[str],
# cfg
audio: Optional[str],
video_output: Optional[str],
# gui
write: bool,
play: bool,
profile: bool,
):
"""Intelligent oscilloscope visualizer for .wav files.
FILES can be one or more .wav files (or wildcards), one folder, or one
.yaml config.
"""
# GUI:
# ovgenpy
# ovgenpy file.yaml
# ovgenpy wildcard/wav/folder ... [--options]
#
# CLI:
# ovgenpy wildcard/wav/folder ... [--options] --write-cfg file.yaml [--play]
# ovgenpy wildcard/wav/folder ... --play
# ovgenpy file.yaml --play
# ovgenpy file.yaml --write-yaml
#
# - You can specify as many wildcards or wav files as you want.
# - You can only supply one folder, with no files/wildcards.
2018-08-20 01:53:52 +00:00
show_gui = (not write and not play)
# Create cfg: Config object.
cfg: Config = None
wav_prefix = Path()
wav_list: List[Path] = []
for name in files:
path = Path(name)
if path.is_dir():
# Add a directory.
if len(files) > 1:
raise click.ClickException(
f'When supplying folder {path}, you cannot supply other files/folders')
wav_prefix = path
matches = sorted(path.glob('*.wav'))
wav_list += matches
break
elif path.suffix in YAML_EXTS:
# Load a YAML file to cfg, and skip default_config().
if len(files) > 1:
raise click.ClickException(
f'When supplying config {path}, you cannot supply other files/folders')
cfg = yaml.load(path)
break
else:
# Load one or more wav files.
matches = sorted(Path().glob(name))
if not matches:
matches = [path]
if not path.exists():
raise click.ClickException(
f'Supplied nonexistent file or wildcard: {path}')
wav_list += matches
if not cfg:
wav_prefix = str(wav_prefix)
wav_list = [str(wav_path) for wav_path in wav_list]
channels = [ChannelConfig(wav_path) for wav_path in wav_list]
if video_output:
outputs = [FFmpegOutputConfig(video_output)]
else:
outputs = [FFplayOutputConfig()]
# TODO test cfg, ensure wav_prefix and wav_list are correct
# maybe I should use a list comprehension to parse cfg.channels to List[str].
cfg = default_config(
master_audio=audio,
# fps=default,
wav_prefix=wav_prefix,
channels=channels,
# width_ms...trigger=default,
# amplification...render=default,
outputs=outputs
)
if show_gui:
raise OvgenError('GUI not implemented')
else:
2018-08-20 01:53:52 +00:00
if write:
if audio:
write_path = Path(audio).with_suffix(YAML_NAME)
else:
write_path = DEFAULT_CONFIG_PATH
# TODO test writing YAML file
yaml.dump(cfg, write_path)
if play:
if profile:
import cProfile
# Write stats to unused filename
for i in count():
path = Path(PROFILE_DUMP_NAME + str(i))
if not path.exists():
break
cProfile.runctx('Ovgen(cfg).play()', globals(), locals(), path)
else:
Ovgen(cfg).play()