Implement handling of KeyboardInterrupt
This commit adds handling of KeyboardInterrupts. For this purpose, a signal handler is installed for all processes and a global variable that calls further cleanup steps. Because processes are used, each process receives the SIGINT. To properly stop and not just wait for something to handle the SIGINT, each process raises a custom `CleanupRequired` error and exits.
This commit is contained in:
parent
8471db3ea1
commit
6716d5003b
@ -10,7 +10,7 @@ import ffmpy
|
||||
import argparse
|
||||
|
||||
# multiprocessing stuff
|
||||
from multiprocessing import Pool
|
||||
from multiprocessing import Pool, Value, parent_process
|
||||
|
||||
# executing some commands
|
||||
import subprocess
|
||||
@ -38,6 +38,12 @@ import pyloudnorm
|
||||
# file copy
|
||||
import shutil
|
||||
|
||||
# signal handling
|
||||
import signal
|
||||
|
||||
# exiting
|
||||
import sys
|
||||
|
||||
"""
|
||||
Normalize loudness of all music files in a given directory and its subdirectories.
|
||||
"""
|
||||
@ -45,6 +51,18 @@ Normalize loudness of all music files in a given directory and its subdirectorie
|
||||
musicfile_extensions = (".flac", ".wav", ".mp3", ".m4a", ".aac", ".opus")
|
||||
|
||||
|
||||
class CleanupRequired(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def sigint_handler(signum, frame):
|
||||
# set workers to clean up
|
||||
cleanup_required.value = 1
|
||||
# Output only once
|
||||
if parent_process() is None:
|
||||
print("\nReceived KeyboardInterrupt. Process cleaning up and stopping...")
|
||||
|
||||
|
||||
def loudnorm(inputfile: str, outputfile: str):
|
||||
"""
|
||||
Normalize audio to EBU R 128 standard using pyloudnorm
|
||||
@ -59,6 +77,10 @@ def loudnorm(inputfile: str, outputfile: str):
|
||||
meter = pyloudnorm.Meter(rate=rate)
|
||||
loudness = meter.integrated_loudness(data=data)
|
||||
|
||||
# cleanup check
|
||||
if bool(cleanup_required.value):
|
||||
raise CleanupRequired()
|
||||
|
||||
# normalize audio
|
||||
file_normalized = pyloudnorm.normalize.loudness(
|
||||
data=data, input_loudness=loudness, target_loudness=-18.0
|
||||
@ -76,6 +98,9 @@ def ffmpeg_to_wav(inputfile: str, outputfile: str):
|
||||
inputfile (str): Path to input file
|
||||
outputfile (str): Path to output file
|
||||
"""
|
||||
# cleanup check
|
||||
if bool(cleanup_required.value):
|
||||
raise CleanupRequired()
|
||||
|
||||
# convert to wav in temporary directory
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
@ -99,6 +124,10 @@ def ffmpeg_to_wav(inputfile: str, outputfile: str):
|
||||
|
||||
subprocess.run(ff.cmd, shell=True, capture_output=True)
|
||||
|
||||
# cleanup check
|
||||
if bool(cleanup_required.value):
|
||||
raise CleanupRequired()
|
||||
|
||||
# normalize loudness
|
||||
loudnorm(inputfile=temp_input, outputfile=temp_output)
|
||||
|
||||
@ -107,6 +136,10 @@ def ffmpeg_to_wav(inputfile: str, outputfile: str):
|
||||
outputfile: "-c:a libopus" " " "-b:a 192k" " " "-compression_level 10"
|
||||
}
|
||||
|
||||
# cleanup check
|
||||
if bool(cleanup_required.value):
|
||||
raise CleanupRequired()
|
||||
|
||||
ff = ffmpy.FFmpeg(
|
||||
inputs={temp_output: None}, outputs=outputcmd, global_options=("-y")
|
||||
)
|
||||
@ -123,6 +156,9 @@ def ffmpeg_copy_metadata(inputfile: str, outputfile: str):
|
||||
inputfile (str): Path to input file
|
||||
outputfile (str): Path to output file
|
||||
"""
|
||||
# cleanup check
|
||||
if bool(cleanup_required.value):
|
||||
raise CleanupRequired()
|
||||
|
||||
# store output file as temporary file. FFMPEG can't work on files in-place
|
||||
with tempfile.NamedTemporaryFile() as temp_audio:
|
||||
@ -154,10 +190,13 @@ def main(inputfile: str) -> Optional[list[Any]]:
|
||||
Output:
|
||||
dynamically normalised audio files (list)
|
||||
"""
|
||||
|
||||
# set output folder to parent path + "normalized"
|
||||
outputfolder = os.path.join(os.path.dirname(inputfile), "normalized")
|
||||
|
||||
# cleanup check
|
||||
if bool(cleanup_required.value):
|
||||
raise CleanupRequired()
|
||||
|
||||
# NOTE create output folder
|
||||
# because multiple parallel processes are at work here,
|
||||
# there might be conflicts with one trying to create the directory although it already exists
|
||||
@ -201,6 +240,11 @@ if __name__ == "__main__":
|
||||
"""
|
||||
Handle arguments and other details for interactive usage
|
||||
"""
|
||||
# global cleanup variable
|
||||
cleanup_required = Value("i", 0)
|
||||
|
||||
# handle KeyboardInterrupt
|
||||
signal.signal(signal.SIGINT, sigint_handler)
|
||||
|
||||
# start time of program
|
||||
starttime = time.time()
|
||||
@ -265,8 +309,11 @@ if __name__ == "__main__":
|
||||
if os.path.getmtime(filepath) >= float(timeprev):
|
||||
musicfiles.append(os.path.join(root, file))
|
||||
|
||||
# process pool
|
||||
with Pool(cpu) as p:
|
||||
nonlinear_all: Optional[list[Any]] = p.map(main, musicfiles)
|
||||
result = p.map_async(main, musicfiles)
|
||||
# wait for all processes to finish
|
||||
result.wait()
|
||||
|
||||
# write this run's time into file
|
||||
with open(timefile, "w") as file:
|
||||
|
Loading…
Reference in New Issue
Block a user