From f03a3a686c7304588dd434322c73506531e53595 Mon Sep 17 00:00:00 2001 From: Brianna Rainey Date: Thu, 12 Feb 2026 15:38:54 -0500 Subject: v2.2.4 - Quiet FFmpeg; add "invert" option to Classic Vis; fix CLI parsing for Image component (#96) * change noisiness of terminal output ffmpeg no longer prints everything into the terminal unless we're in `--verbose` mode. percentage progress text stays on one line while not in verbose mode. * Added hint to run `avp --verbose` if `avp --log` is run with no avp_debug.log file present * Classic Visualizer: add invert option * Image component: fix path commandline option * Image component: restrict file formats in CLI to match GUI * Color component: add tooltip to color2 picker (second color of gradients) * change tests to work with pytest-xdist avp core stores its config (location of `settings.ini`) in temp directories if using multiple workers to run tests, so they don't interfere with each other. when using a single worker, the `tests/data/config` directory is still used * check alt comp names when parsing cmdline * rename `original.py` to `classic.py` * move `component.py` into subpackage * rename comp_original to comp_classic * show traceback if renderFrame() raises exception * do not try to insert non-existent components from project files * add "composite" property for components if a component returns "composite" then it will receive a frame to draw on during calls to previewRender and frameRender * more tests of projects, actions, waveform, spectrum, image, color, classic * do not change presetDir to "projects" within PresetManager--- src/avp/video_thread.py | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) (limited to 'src/avp/video_thread.py') diff --git a/src/avp/video_thread.py b/src/avp/video_thread.py index 967d2fe..ecd8c4c 100644 --- a/src/avp/video_thread.py +++ b/src/avp/video_thread.py @@ -12,16 +12,16 @@ from PyQt6 import QtCore, QtGui from PyQt6.QtCore import pyqtSignal, pyqtSlot from PIL import Image from PIL.ImageQt import ImageQt + import numpy import subprocess as sp import sys import os -import time import signal import logging -from .component import ComponentError -from .toolkit.frame import Checkerboard +from .libcomponent import ComponentError +from .toolkit import formatTraceback from .toolkit.ffmpeg import ( openPipe, readAudioFile, @@ -61,7 +61,11 @@ class Worker(QtCore.QObject): def createFfmpegCommand(self, duration): try: ffmpegCommand = createFfmpegCommand( - self.inputFile, self.outputFile, self.components, duration + self.inputFile, + self.outputFile, + self.components, + duration, + "info" if log.getEffectiveLevel() < logging.WARNING else "error", ) except sp.CalledProcessError as e: # FIXME video_thread should own this error signal, not components @@ -111,6 +115,7 @@ class Worker(QtCore.QObject): Also prerenders "static" components like text and merges them if possible """ self.staticComponents = {} + self.compositeComponents = set() # Call preFrameRender on each component canceledByComponent = False @@ -160,6 +165,8 @@ class Worker(QtCore.QObject): if "static" in compProps: log.info("Saving static frame from #%s %s", compNo, comp) self.staticComponents[compNo] = comp.frameRender(0).copy() + elif compNo > 0 and "composite" in compProps: + self.compositeComponents.add(compNo) # Check if any errors occured log.debug("Checking if a component wishes to cancel the export...") @@ -208,9 +215,11 @@ class Worker(QtCore.QObject): self.closePipe() self.cancelExport() self.error = True - msg = "A call to renderFrame in the video thread failed critically." - log.critical(msg) - comp._error.emit(msg, str(e)) + msg = f"{comp.name} renderFrame({int(audioI / self.sampleSize)}) raised an exception." + tb = formatTraceback() + details = f"{e.__class__.__name__}: {str(e)}\n\n{tb}" + log.critical(f"{msg}\n{details}") + comp._error.emit(msg, details) bgI = int(audioI / self.sampleSize) frame = None @@ -230,6 +239,9 @@ class Worker(QtCore.QObject): frame, self.staticComponents[layerNo] ) + elif layerNo in self.compositeComponents: + # component that uses previous frame to draw + frame = Image.alpha_composite(frame, comp.frameRender(bgI, frame)) else: # animated component if frame is None: # bottom-most layer @@ -309,9 +321,9 @@ class Worker(QtCore.QObject): log.critical("Out_Pipe to FFmpeg couldn't be created!", exc_info=True) raise - # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ + # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ # START CREATING THE VIDEO - # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ + # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ progressBarValue = 0 self.progressBarUpdate.emit(progressBarValue) # Begin piping into ffmpeg! @@ -335,16 +347,13 @@ class Worker(QtCore.QObject): completion = (audioI / self.audioArrayLen) * 100 if progressBarValue + 1 <= completion: progressBarValue = numpy.floor(completion).astype(int) + msg = "Exporting video: %s%%" % str(int(progressBarValue)) self.progressBarUpdate.emit(progressBarValue) - self.progressBarSetText.emit( - "Exporting video: %s%%" % str(int(progressBarValue)) - ) + self.progressBarSetText.emit(msg) - # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ + # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ # Finished creating the video! - # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ - - numpy.seterr(all="print") + # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ self.closePipe() @@ -363,7 +372,7 @@ class Worker(QtCore.QObject): if self.error: self.failExport() else: - print("Export Complete") + print("\nExport Complete") self.progressBarUpdate.emit(100) self.progressBarSetText.emit("Export Complete") -- cgit v1.2.3