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
|
import argparse
|
||||||
|
|
||||||
# multiprocessing stuff
|
# multiprocessing stuff
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool, Value, parent_process
|
||||||
|
|
||||||
# executing some commands
|
# executing some commands
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -38,6 +38,12 @@ import pyloudnorm
|
|||||||
# file copy
|
# file copy
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
# signal handling
|
||||||
|
import signal
|
||||||
|
|
||||||
|
# exiting
|
||||||
|
import sys
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Normalize loudness of all music files in a given directory and its subdirectories.
|
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")
|
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):
|
def loudnorm(inputfile: str, outputfile: str):
|
||||||
"""
|
"""
|
||||||
Normalize audio to EBU R 128 standard using pyloudnorm
|
Normalize audio to EBU R 128 standard using pyloudnorm
|
||||||
@ -59,6 +77,10 @@ def loudnorm(inputfile: str, outputfile: str):
|
|||||||
meter = pyloudnorm.Meter(rate=rate)
|
meter = pyloudnorm.Meter(rate=rate)
|
||||||
loudness = meter.integrated_loudness(data=data)
|
loudness = meter.integrated_loudness(data=data)
|
||||||
|
|
||||||
|
# cleanup check
|
||||||
|
if bool(cleanup_required.value):
|
||||||
|
raise CleanupRequired()
|
||||||
|
|
||||||
# normalize audio
|
# normalize audio
|
||||||
file_normalized = pyloudnorm.normalize.loudness(
|
file_normalized = pyloudnorm.normalize.loudness(
|
||||||
data=data, input_loudness=loudness, target_loudness=-18.0
|
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
|
inputfile (str): Path to input file
|
||||||
outputfile (str): Path to output file
|
outputfile (str): Path to output file
|
||||||
"""
|
"""
|
||||||
|
# cleanup check
|
||||||
|
if bool(cleanup_required.value):
|
||||||
|
raise CleanupRequired()
|
||||||
|
|
||||||
# convert to wav in temporary directory
|
# convert to wav in temporary directory
|
||||||
with tempfile.TemporaryDirectory() as tempdir:
|
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)
|
subprocess.run(ff.cmd, shell=True, capture_output=True)
|
||||||
|
|
||||||
|
# cleanup check
|
||||||
|
if bool(cleanup_required.value):
|
||||||
|
raise CleanupRequired()
|
||||||
|
|
||||||
# normalize loudness
|
# normalize loudness
|
||||||
loudnorm(inputfile=temp_input, outputfile=temp_output)
|
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"
|
outputfile: "-c:a libopus" " " "-b:a 192k" " " "-compression_level 10"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# cleanup check
|
||||||
|
if bool(cleanup_required.value):
|
||||||
|
raise CleanupRequired()
|
||||||
|
|
||||||
ff = ffmpy.FFmpeg(
|
ff = ffmpy.FFmpeg(
|
||||||
inputs={temp_output: None}, outputs=outputcmd, global_options=("-y")
|
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
|
inputfile (str): Path to input file
|
||||||
outputfile (str): Path to output 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
|
# store output file as temporary file. FFMPEG can't work on files in-place
|
||||||
with tempfile.NamedTemporaryFile() as temp_audio:
|
with tempfile.NamedTemporaryFile() as temp_audio:
|
||||||
@ -154,10 +190,13 @@ def main(inputfile: str) -> Optional[list[Any]]:
|
|||||||
Output:
|
Output:
|
||||||
dynamically normalised audio files (list)
|
dynamically normalised audio files (list)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# set output folder to parent path + "normalized"
|
# set output folder to parent path + "normalized"
|
||||||
outputfolder = os.path.join(os.path.dirname(inputfile), "normalized")
|
outputfolder = os.path.join(os.path.dirname(inputfile), "normalized")
|
||||||
|
|
||||||
|
# cleanup check
|
||||||
|
if bool(cleanup_required.value):
|
||||||
|
raise CleanupRequired()
|
||||||
|
|
||||||
# NOTE create output folder
|
# NOTE create output folder
|
||||||
# because multiple parallel processes are at work here,
|
# because multiple parallel processes are at work here,
|
||||||
# there might be conflicts with one trying to create the directory although it already exists
|
# 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
|
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
|
# start time of program
|
||||||
starttime = time.time()
|
starttime = time.time()
|
||||||
@ -265,8 +309,11 @@ if __name__ == "__main__":
|
|||||||
if os.path.getmtime(filepath) >= float(timeprev):
|
if os.path.getmtime(filepath) >= float(timeprev):
|
||||||
musicfiles.append(os.path.join(root, file))
|
musicfiles.append(os.path.join(root, file))
|
||||||
|
|
||||||
|
# process pool
|
||||||
with Pool(cpu) as p:
|
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
|
# write this run's time into file
|
||||||
with open(timefile, "w") as file:
|
with open(timefile, "w") as file:
|
||||||
|
Loading…
Reference in New Issue
Block a user