kopia lustrzana https://github.com/corrscope/corrscope
[wip] Fix loading YAML config from another directory
Bugs: FFmpeg writes to file named "-"pull/357/head
rodzic
590aad12ba
commit
ba17f64618
|
@ -1,3 +1,4 @@
|
||||||
|
from os.path import abspath
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from ovgenpy.config import register_config, Alias
|
from ovgenpy.config import register_config, Alias
|
||||||
|
@ -42,7 +43,7 @@ class Channel:
|
||||||
|
|
||||||
# Create a Wave object.
|
# Create a Wave object.
|
||||||
wcfg = _WaveConfig(amplification=ovgen_cfg.amplification * cfg.ampl_ratio)
|
wcfg = _WaveConfig(amplification=ovgen_cfg.amplification * cfg.ampl_ratio)
|
||||||
self.wave = Wave(wcfg, cfg.wav_path)
|
self.wave = Wave(wcfg, abspath(cfg.wav_path))
|
||||||
|
|
||||||
# Compute subsampling (array stride).
|
# Compute subsampling (array stride).
|
||||||
tw = coalesce(cfg.trigger_width, ovgen_cfg.trigger_width)
|
tw = coalesce(cfg.trigger_width, ovgen_cfg.trigger_width)
|
||||||
|
|
|
@ -86,6 +86,7 @@ def main(
|
||||||
|
|
||||||
# Create cfg: Config object.
|
# Create cfg: Config object.
|
||||||
cfg: Config = None
|
cfg: Config = None
|
||||||
|
cfg_dir: str = None
|
||||||
|
|
||||||
wav_list: List[Path] = []
|
wav_list: List[Path] = []
|
||||||
for name in files:
|
for name in files:
|
||||||
|
@ -106,6 +107,7 @@ def main(
|
||||||
raise click.ClickException(
|
raise click.ClickException(
|
||||||
f'When supplying config {path}, you cannot supply other files/folders')
|
f'When supplying config {path}, you cannot supply other files/folders')
|
||||||
cfg = yaml.load(path)
|
cfg = yaml.load(path)
|
||||||
|
cfg_dir = path.parent
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -136,6 +138,7 @@ def main(
|
||||||
# amplification...render=default,
|
# amplification...render=default,
|
||||||
outputs=outputs
|
outputs=outputs
|
||||||
)
|
)
|
||||||
|
cfg_dir = '.'
|
||||||
|
|
||||||
if show_gui:
|
if show_gui:
|
||||||
raise OvgenError('GUI not implemented')
|
raise OvgenError('GUI not implemented')
|
||||||
|
@ -171,4 +174,4 @@ def main(
|
||||||
cProfile.runctx('Ovgen(cfg).play()', globals(), locals(), path)
|
cProfile.runctx('Ovgen(cfg).play()', globals(), locals(), path)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
Ovgen(cfg).play()
|
Ovgen(cfg, cfg_dir).play()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from os.path import abspath
|
||||||
from typing import TYPE_CHECKING, Type, List, Union
|
from typing import TYPE_CHECKING, Type, List, Union
|
||||||
|
|
||||||
from ovgenpy.config import register_config
|
from ovgenpy.config import register_config
|
||||||
|
@ -55,7 +56,7 @@ class _FFmpegCommand:
|
||||||
|
|
||||||
self.templates += ffmpeg_input_video(ovgen_cfg) # video
|
self.templates += ffmpeg_input_video(ovgen_cfg) # video
|
||||||
if ovgen_cfg.master_audio:
|
if ovgen_cfg.master_audio:
|
||||||
audio_path = shlex.quote(ovgen_cfg.master_audio)
|
audio_path = shlex.quote(abspath(ovgen_cfg.master_audio))
|
||||||
self.templates.append(f'-ss {ovgen_cfg.begin_time}')
|
self.templates.append(f'-ss {ovgen_cfg.begin_time}')
|
||||||
self.templates += ffmpeg_input_audio(audio_path) # audio
|
self.templates += ffmpeg_input_audio(audio_path) # audio
|
||||||
|
|
||||||
|
@ -141,7 +142,7 @@ class FFmpegOutput(PipeOutput):
|
||||||
ffmpeg = _FFmpegCommand([FFMPEG, '-y'], ovgen_cfg)
|
ffmpeg = _FFmpegCommand([FFMPEG, '-y'], ovgen_cfg)
|
||||||
ffmpeg.add_output(cfg)
|
ffmpeg.add_output(cfg)
|
||||||
ffmpeg.templates.append(cfg.args)
|
ffmpeg.templates.append(cfg.args)
|
||||||
self.open(ffmpeg.popen([cfg.path], self.bufsize))
|
self.open(ffmpeg.popen([abspath(cfg.path)], self.bufsize))
|
||||||
|
|
||||||
|
|
||||||
# FFplayOutput
|
# FFplayOutput
|
||||||
|
|
|
@ -10,6 +10,7 @@ from ovgenpy.channel import Channel, ChannelConfig
|
||||||
from ovgenpy.config import register_config, register_enum, Ignored
|
from ovgenpy.config import register_config, register_enum, Ignored
|
||||||
from ovgenpy.renderer import MatplotlibRenderer, RendererConfig, LayoutConfig
|
from ovgenpy.renderer import MatplotlibRenderer, RendererConfig, LayoutConfig
|
||||||
from ovgenpy.triggers import ITriggerConfig, CorrelationTriggerConfig, Trigger
|
from ovgenpy.triggers import ITriggerConfig, CorrelationTriggerConfig, Trigger
|
||||||
|
from ovgenpy.util import pushd
|
||||||
from ovgenpy.utils import keyword_dataclasses as dc
|
from ovgenpy.utils import keyword_dataclasses as dc
|
||||||
from ovgenpy.utils.keyword_dataclasses import field
|
from ovgenpy.utils.keyword_dataclasses import field
|
||||||
from ovgenpy.wave import Wave
|
from ovgenpy.wave import Wave
|
||||||
|
@ -97,8 +98,9 @@ def default_config(**kwargs):
|
||||||
|
|
||||||
|
|
||||||
class Ovgen:
|
class Ovgen:
|
||||||
def __init__(self, cfg: Config):
|
def __init__(self, cfg: Config, cfg_dir: str):
|
||||||
self.cfg = cfg
|
self.cfg = cfg
|
||||||
|
self.cfg_dir = cfg_dir
|
||||||
self.has_played = False
|
self.has_played = False
|
||||||
|
|
||||||
if len(self.cfg.channels) == 0:
|
if len(self.cfg.channels) == 0:
|
||||||
|
@ -110,19 +112,21 @@ class Ovgen:
|
||||||
nchan: int
|
nchan: int
|
||||||
|
|
||||||
def _load_channels(self):
|
def _load_channels(self):
|
||||||
self.channels = [Channel(ccfg, self.cfg) for ccfg in self.cfg.channels]
|
with pushd(self.cfg_dir):
|
||||||
self.waves = [channel.wave for channel in self.channels]
|
self.channels = [Channel(ccfg, self.cfg) for ccfg in self.cfg.channels]
|
||||||
self.triggers = [channel.trigger for channel in self.channels]
|
self.waves = [channel.wave for channel in self.channels]
|
||||||
self.nchan = len(self.channels)
|
self.triggers = [channel.trigger for channel in self.channels]
|
||||||
|
self.nchan = len(self.channels)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def _load_outputs(self):
|
def _load_outputs(self):
|
||||||
with ExitStack() as stack:
|
with pushd(self.cfg_dir):
|
||||||
self.outputs = [
|
with ExitStack() as stack:
|
||||||
stack.enter_context(output_cfg(self.cfg))
|
self.outputs = [
|
||||||
for output_cfg in self.cfg.outputs
|
stack.enter_context(output_cfg(self.cfg))
|
||||||
]
|
for output_cfg in self.cfg.outputs
|
||||||
yield
|
]
|
||||||
|
yield
|
||||||
|
|
||||||
def _load_renderer(self):
|
def _load_renderer(self):
|
||||||
renderer = MatplotlibRenderer(self.cfg.render, self.cfg.layout, self.nchan)
|
renderer = MatplotlibRenderer(self.cfg.render, self.cfg.layout, self.nchan)
|
||||||
|
|
|
@ -64,7 +64,7 @@ def test_channel_subsampling(
|
||||||
assert trigger._subsampling == channel.trigger_subsampling
|
assert trigger._subsampling == channel.trigger_subsampling
|
||||||
|
|
||||||
# Ensure ovgenpy calls render using channel.window_samp and render_subsampling.
|
# Ensure ovgenpy calls render using channel.window_samp and render_subsampling.
|
||||||
ovgen = Ovgen(cfg)
|
ovgen = Ovgen(cfg, '.')
|
||||||
renderer = mocker.patch.object(Ovgen, '_load_renderer').return_value
|
renderer = mocker.patch.object(Ovgen, '_load_renderer').return_value
|
||||||
ovgen.play()
|
ovgen.play()
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import shlex
|
import shlex
|
||||||
|
from os.path import abspath
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Callable
|
from typing import TYPE_CHECKING, Callable
|
||||||
|
|
||||||
|
@ -13,7 +14,6 @@ from ovgenpy.config import yaml
|
||||||
from ovgenpy.ovgenpy import Config, Ovgen
|
from ovgenpy.ovgenpy import Config, Ovgen
|
||||||
from ovgenpy.util import pushd
|
from ovgenpy.util import pushd
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import pytest_mock
|
import pytest_mock
|
||||||
|
|
||||||
|
@ -49,7 +49,8 @@ def player_sink(mocker) -> Callable:
|
||||||
call_main(argv)
|
call_main(argv)
|
||||||
|
|
||||||
Ovgen.assert_called_once()
|
Ovgen.assert_called_once()
|
||||||
(cfg,), kwargs = Ovgen.call_args
|
args, kwargs = Ovgen.call_args
|
||||||
|
cfg = args[0]
|
||||||
|
|
||||||
assert isinstance(cfg, Config)
|
assert isinstance(cfg, Config)
|
||||||
return cfg,
|
return cfg,
|
||||||
|
@ -105,36 +106,39 @@ def test_write_dir(yaml_sink):
|
||||||
assert outpath.parent / cfg.master_audio == audio_path
|
assert outpath.parent / cfg.master_audio == audio_path
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def Wave(mocker):
|
|
||||||
""" Logs all calls, and returns a real Wave object. """
|
|
||||||
Wave = mocker.spy(ovgenpy.channel, 'Wave')
|
|
||||||
yield Wave
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('Popen')
|
@pytest.mark.usefixtures('Popen')
|
||||||
def test_load_yaml_another_dir(yaml_sink, Popen, Wave):
|
def test_load_yaml_another_dir(yaml_sink, mocker, Popen):
|
||||||
""" Loading `another/dir/YAML` should resolve `master_audio`, `channels[].wav_path`,
|
""" YAML file located in `another/dir` should resolve `master_audio`, `channels[].
|
||||||
and video `path` from `another/dir`. """
|
wav_path`, and video `path` from `another/dir`. """
|
||||||
|
|
||||||
with pushd('tests'):
|
subdir = 'tests'
|
||||||
arg_str = 'sine440.wav -a sine440.wav -o foo.mp4'
|
wav = 'sine440.wav'
|
||||||
|
mp4 = 'foo.mp4'
|
||||||
|
with pushd(subdir):
|
||||||
|
arg_str = f'{wav} -a {wav} -o {mp4}'
|
||||||
cfg, outpath = yaml_sink(arg_str) # type: Config, Path
|
cfg, outpath = yaml_sink(arg_str) # type: Config, Path
|
||||||
|
|
||||||
cfg.begin_time = 100 # To skip all actual rendering
|
cfg.begin_time = 100 # To skip all actual rendering
|
||||||
ovgen = Ovgen(cfg, 'tests')
|
|
||||||
|
# Log execution of Ovgen().play()
|
||||||
|
Wave = mocker.spy(ovgenpy.channel, 'Wave')
|
||||||
|
ovgen = Ovgen(cfg, subdir)
|
||||||
ovgen.play()
|
ovgen.play()
|
||||||
|
|
||||||
|
# Compute absolute paths
|
||||||
|
wav_abs = abspath(f'{subdir}/{wav}')
|
||||||
|
mp4_abs = abspath(f'{subdir}/{mp4}')
|
||||||
|
|
||||||
# Test `wave_path`
|
# Test `wave_path`
|
||||||
args, kwargs = Wave.call_args
|
args, kwargs = Wave.call_args
|
||||||
cfg, wave_path = args
|
cfg, wave_path = args
|
||||||
assert wave_path == 'tests/sine440.wav'
|
assert wave_path == wav_abs
|
||||||
|
|
||||||
# Test output `master_audio` and video `path`
|
# Test output `master_audio` and video `path`
|
||||||
args, kwargs = Popen.call_args
|
args, kwargs = Popen.call_args
|
||||||
argv = args[0]
|
argv = args[0]
|
||||||
assert argv[-1] == 'tests/foo.mp4'
|
assert argv[-1] == mp4_abs
|
||||||
assert '-i tests/sine440.wav' in ' '.join(argv)
|
assert f'-i {wav_abs}' in ' '.join(argv)
|
||||||
|
|
||||||
|
|
||||||
# TODO integration test without --audio
|
# TODO integration test without --audio
|
||||||
|
|
|
@ -67,7 +67,7 @@ def test_ovgen_terminate_ffplay(Popen, mocker: 'pytest_mock.MockFixture'):
|
||||||
master_audio='tests/sine440.wav',
|
master_audio='tests/sine440.wav',
|
||||||
outputs=[FFplayOutputConfig()]
|
outputs=[FFplayOutputConfig()]
|
||||||
)
|
)
|
||||||
ovgen = Ovgen(cfg)
|
ovgen = Ovgen(cfg, '.')
|
||||||
|
|
||||||
render_frame = mocker.patch.object(MatplotlibRenderer, 'render_frame')
|
render_frame = mocker.patch.object(MatplotlibRenderer, 'render_frame')
|
||||||
render_frame.side_effect = DummyException()
|
render_frame.side_effect = DummyException()
|
||||||
|
|
Ładowanie…
Reference in New Issue