Merge pull request #382 from corrscope/custom-render-path

pull/383/head
nyanpasu64 2021-07-03 11:45:31 -07:00 zatwierdzone przez GitHub
commit a0dcca236d
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
7 zmienionych plików z 48 dodań i 16 usunięć

Wyświetl plik

@ -1,5 +1,9 @@
## 0.7.1 (unreleased) ## 0.7.1 (unreleased)
### Major Changes
- Change `-r/--render` command line flag to take an output path (#382)
### Changelog ### Changelog
- Update NumPy so `poetry install` on Python 3.8+ won't build NumPy from source (#371) - Update NumPy so `poetry install` on Python 3.8+ won't build NumPy from source (#371)

Wyświetl plik

@ -83,8 +83,8 @@ poetry run corr (args)
1. Play (requires ffmpeg): 1. Play (requires ffmpeg):
- `corrscope master.yaml -p/--play` - `corrscope master.yaml -p/--play`
1. Render and encode MP4 video (requires ffmpeg) 1. Render and encode video (requires ffmpeg)
- `corrscope master.yaml -r/--render` - `corrscope master.yaml -r/--render file.mp4` (other file extensions supported)
## Contributing ## Contributing

Wyświetl plik

@ -34,7 +34,7 @@ YAML_EXTS = [".yaml"]
# Default extension when writing Config. # Default extension when writing Config.
YAML_NAME = YAML_EXTS[0] YAML_NAME = YAML_EXTS[0]
# Default output extension # Default output extension, only used in GUI and unit tests
VIDEO_NAME = ".mp4" VIDEO_NAME = ".mp4"
@ -85,14 +85,14 @@ CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
# Override default .yaml settings (only if YAML file not supplied) # Override default .yaml settings (only if YAML file not supplied)
# Incorrect [option] name order: https://github.com/pallets/click/issues/793 # Incorrect [option] name order: https://github.com/pallets/click/issues/793
@click.option('--audio', '-a', type=File, help= @click.option('--audio', '-a', type=File, help=
'Config: Input path for master audio file') 'Input path for master audio file')
# Disables GUI # Disables GUI
@click.option('--write', '-w', is_flag=True, help= @click.option('--write', '-w', is_flag=True, help=
"Write config YAML file to current directory (don't open GUI).") "Write config YAML file to current directory (don't open GUI).")
@click.option('--play', '-p', is_flag=True, help= @click.option('--play', '-p', is_flag=True, help=
"Preview (don't open GUI).") "Preview (don't open GUI).")
@click.option('--render', '-r', is_flag=True, help= @click.option('--render', '-r', type=OutFile, help=
"Render and encode MP4 video (don't open GUI).") "Render and encode video to file (don't open GUI).")
# Debugging # Debugging
@click.option('--profile', is_flag=True, help= @click.option('--profile', is_flag=True, help=
'Debug: Write CProfiler snapshot') 'Debug: Write CProfiler snapshot')
@ -105,7 +105,7 @@ def main(
# gui # gui
write: bool, write: bool,
play: bool, play: bool,
render: bool, render: Optional[str],
profile: bool, profile: bool,
): ):
"""Intelligent oscilloscope visualizer for .wav files. """Intelligent oscilloscope visualizer for .wav files.
@ -227,7 +227,7 @@ def main(
outputs.append(FFplayOutputConfig()) outputs.append(FFplayOutputConfig())
if render: if render:
video_path = _get_file_name(cfg_path, cfg, ext=VIDEO_NAME) video_path = render
outputs.append(cfg.get_ffmpeg_cfg(video_path)) outputs.append(cfg.get_ffmpeg_cfg(video_path))
if outputs: if outputs:

Wyświetl plik

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os.path
import time import time
from contextlib import ExitStack, contextmanager from contextlib import ExitStack, contextmanager
from enum import unique from enum import unique
@ -91,7 +92,7 @@ class Config(
ffmpeg_cli: FFmpegOutputConfig = attr.ib(factory=lambda: FFmpegOutputConfig(None)) ffmpeg_cli: FFmpegOutputConfig = attr.ib(factory=lambda: FFmpegOutputConfig(None))
def get_ffmpeg_cfg(self, video_path: str) -> FFmpegOutputConfig: def get_ffmpeg_cfg(self, video_path: str) -> FFmpegOutputConfig:
return attr.evolve(self.ffmpeg_cli, path=video_path) return attr.evolve(self.ffmpeg_cli, path=os.path.abspath(video_path))
benchmark_mode: BenchmarkMode = attr.ib( benchmark_mode: BenchmarkMode = attr.ib(
BenchmarkMode.NONE, converter=BenchmarkMode.by_name BenchmarkMode.NONE, converter=BenchmarkMode.by_name

Wyświetl plik

@ -166,9 +166,11 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
- Otherwise empty string. - Otherwise empty string.
- self.get_save_filename() calls cli.get_file_stem(). - self.get_save_filename() calls cli.get_file_stem().
CLI filename is the same, CLI YAML filename is the same,
but defaults to "corrscope.{yaml, mp4}" instead of empty string. but defaults to "corrscope.yaml" instead of empty string.
- cli._get_file_name() calls cli.get_file_stem(). - cli._get_file_name() calls cli.get_file_stem().
CLI video filename is explicitly specified by the user.
""" """
def __init__(self, cfg_or_path: Union[Config, Path]): def __init__(self, cfg_or_path: Union[Config, Path]):

Wyświetl plik

@ -22,6 +22,8 @@ class IOutputConfig(DumpableAttrs):
cls: "ClassVar[Type[Output]]" cls: "ClassVar[Type[Output]]"
def __call__(self, corr_cfg: "Config") -> "Output": def __call__(self, corr_cfg: "Config") -> "Output":
"""Must be called in the .yaml file's directory.
This is used to properly resolve corr_cfg.master_audio."""
return self.cls(corr_cfg, cfg=self) return self.cls(corr_cfg, cfg=self)
@ -217,6 +219,25 @@ class PipeOutput(Output):
class FFmpegOutputConfig(IOutputConfig): class FFmpegOutputConfig(IOutputConfig):
# path=None writes to stdout. # path=None writes to stdout.
#
# This parameter is not loaded from disk, but set when the user picks a render path
# on the GUI or calls the CLI with `--render out.mp4`.
#
# It must be an absolute path. How is this ensured?
#
# - Paths supplied from the GUI are always absolute.
# - Paths supplied from the CLI must be resolved before storing in
# FFmpegOutputConfig.
#
# Why are relative paths not allowed? Currently, to resolve `corr_cfg.master_audio`
# relative to the config file, we change directories to the config dir, then call
# `abspath(corr_cfg.master_audio)`. As a result, if we called `corr dir/cfg.yaml -r
# out.mp4` and corrscope didn't resolve `out.mp4` before passing into
# FFmpegOutputConfig, it would mistakenly write to `dir/out.mp4`.
#
# In the future, relative paths could be allowed by not switching directories to the
# config dir, and finding another way to resolve `corr_cfg.master_audio` based on
# the config dir.
path: Optional[str] path: Optional[str]
args: str = "" args: str = ""

Wyświetl plik

@ -140,15 +140,19 @@ def test_load_yaml_another_dir(mocker, Popen):
# Log execution of CorrScope().play() # Log execution of CorrScope().play()
Wave = mocker.spy(corrscope.channel, "Wave") Wave = mocker.spy(corrscope.channel, "Wave")
# Issue: this test does not use cli.main() to compute output path. # Same function as used in cli.py and gui/__init__.py.
# Possible solution: Call cli.main() via Click runner. output = cfg.get_ffmpeg_cfg(mp4)
output = FFmpegOutputConfig(cli._get_file_name(None, cfg, cli.VIDEO_NAME))
corr = CorrScope(cfg, Arguments(subdir, [output])) corr = CorrScope(cfg, Arguments(subdir, [output]))
corr.play() corr.play()
# Compute absolute paths # The .wav path (specified in Config) should be resolved relative to the config
# file.
wav_abs = abspath(f"{subdir}/{wav}") wav_abs = abspath(f"{subdir}/{wav}")
mp4_abs = abspath(f"{subdir}/{mp4}")
# The output video path (specified in CLI --render) should be resolved relative to
# the shell's working directory.
mp4_abs = abspath(mp4)
# Test `wave_path` # Test `wave_path`
args, kwargs = Wave.call_args args, kwargs = Wave.call_args