Created
June 21, 2024 10:59
-
-
Save patrickwolf/ccb2ae7cf93999cb93c56b378706e2e6 to your computer and use it in GitHub Desktop.
Post processing for AAX Audio Converter to keep JSON files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
AAX Audio Converter PostJob v0.1 | |
Post processing for AAX Audio Converter to keep JSON files. | |
This script maps and copies JSON files based on AAX filenames to their respective destinations. | |
Authors: Patrick Wolf, ChatGPT | |
Year: 2024 | |
License: Open source (all rights allowed) | |
# Setup: | |
Settings > General > Copy .aax to second folder with (flat) \ <book> - <author> | |
- Set Folder to AAXTemp | |
Settings > Folder Structure > Flat Folder Structure and Naming : <book> - <author> | |
This makes AAX create these files | |
AAXTemp/5 Ways to Improve Your Sleep - Alexandra Hayes_B088GNTWH5.aax | |
target/5 Ways to Improve Your Sleep - Alexandra Hayes/5 Ways to Improve Your Sleep - Alexandra Hayes.m4b" | |
And then this script will generate | |
target/5 Ways to Improve Your Sleep - Alexandra Hayes/5 Ways to Improve Your Sleep - Alexandra Hayes.json | |
target/5 Ways to Improve Your Sleep - Alexandra Hayes/5 Ways to Improve Your Sleep - Alexandra Hayes_content.json | |
# Command line: | |
python aax_audio_converter_keep_json.py /path/to/source /path/to/destination --dry_run | |
""" | |
import os | |
import shutil | |
from typing import List, Tuple | |
import argparse | |
import unittest | |
class FileMapper: | |
def __init__(self, srcDirList: List[str], dstDirList: List[str]): | |
# Normalize paths to use forward slashes | |
self.srcDirList = [s.replace('\\', '/') for s in srcDirList] | |
self.dstDirList = [d.replace('\\', '/') for d in dstDirList] | |
def determineActions(self, aax_filename: str) -> List[Tuple[str, str]]: | |
"""Determine the actions needed to copy JSON files based on the AAX filename.""" | |
actions = [] | |
# Normalize path to use forward slashes | |
aax_filename = aax_filename.replace('\\', '/') | |
base_name = os.path.basename(aax_filename) | |
new_name, asin = base_name.rsplit('_', 1) | |
asin = asin.replace('.aax', '') | |
json_file = f"{asin}.json" | |
metadata_file = f"content_metadata_{asin}.json" | |
for dst in self.dstDirList: | |
if new_name in dst: | |
folder_name = os.path.basename(dst) | |
json_dst = f"{dst}/{folder_name}.json" | |
metadata_dst = f"{dst}/{folder_name}_metadata.json" | |
json_src = json_file | |
metadata_src = metadata_file | |
if json_src in self.srcDirList: | |
actions.append((json_src, json_dst)) | |
if metadata_src in self.srcDirList: | |
actions.append((metadata_src, metadata_dst)) | |
break | |
return actions | |
def executeActions(self, actions: List[Tuple[str, str]], source_path: str, | |
destination_path: str, dry_run: bool): | |
"""Execute the determined actions or print them if in dry run mode.""" | |
for src, dst in actions: | |
src_full = os.path.normpath(os.path.join(source_path, src)) | |
dst_full = os.path.normpath(os.path.join(destination_path, dst)) | |
try: | |
action = "" | |
if dry_run: | |
action = "Copy (dry)" | |
else: | |
if os.path.exists(dst_full): | |
action = "Skip" | |
else: | |
shutil.copy(src_full, dst_full) | |
action = "Copy" | |
print(f"\n{action}\nFrom {src_full}\nTo {dst_full}") | |
except FileNotFoundError as e: | |
print(f"Error: {e}\nFile not found: {src_full}") | |
except PermissionError as e: | |
print(f"Error: {e}\nPermission denied: {dst_full}") | |
except Exception as e: | |
print( | |
f"Error: {e}\nUnexpected error while copying from {src_full} to {dst_full}" | |
) | |
def main(source_path: str, destination_path: str, dry_run: bool): | |
srcDirList = os.listdir(source_path) | |
dstDirList = os.listdir(destination_path) | |
file_mapper = FileMapper(srcDirList, dstDirList) | |
for file in [f for f in srcDirList if f.endswith(".aax")]: | |
actions = file_mapper.determineActions(file) | |
file_mapper.executeActions(actions, source_path, destination_path, | |
dry_run) | |
class TestFileMapper(unittest.TestCase): | |
def setUp(self): | |
self.srcDirList = [ | |
"B00HU7T81S.json", "content_metadata_B00HU7T81S.json", | |
"B0193MJLV4.json", "content_metadata_B0193MJLV4.json", | |
"B123456789.json", "content_metadata_B123456789.json" | |
] | |
self.dstDirList = [ | |
"/Complete/12 Essential Scientific Concepts - The Great Courses; Indre Viskontas", | |
"/Complete/5 Ways to Improve Your Sleep - Alexandra Hayes", | |
"/Complete/Some Book Title_with_multiple_underscores" | |
] | |
def test_determineActions(self): | |
aax_filename = "/Data/12 Essential Scientific Concepts - The Great Courses; Indre Viskontas_B00HU7T81S.aax" | |
expected_actions = [ | |
("B00HU7T81S.json", | |
"/Complete/12 Essential Scientific Concepts - The Great Courses; Indre Viskontas/12 Essential Scientific Concepts - The Great Courses; Indre Viskontas.json" | |
), | |
("content_metadata_B00HU7T81S.json", | |
"/Complete/12 Essential Scientific Concepts - The Great Courses; Indre Viskontas/12 Essential Scientific Concepts - The Great Courses; Indre Viskontas_metadata.json" | |
) | |
] | |
file_mapper = FileMapper(self.srcDirList, self.dstDirList) | |
actions = file_mapper.determineActions(aax_filename) | |
self.assertEqual(actions, expected_actions) | |
def test_determineActions_multiple_underscores(self): | |
aax_filename = "/Data/Some Book Title_with_multiple_underscores_B123456789.aax" | |
expected_actions = [ | |
("B123456789.json", | |
"/Complete/Some Book Title_with_multiple_underscores/Some Book Title_with_multiple_underscores.json" | |
), | |
("content_metadata_B123456789.json", | |
"/Complete/Some Book Title_with_multiple_underscores/Some Book Title_with_multiple_underscores_metadata.json" | |
) | |
] | |
file_mapper = FileMapper(self.srcDirList, self.dstDirList) | |
actions = file_mapper.determineActions(aax_filename) | |
self.assertEqual(actions, expected_actions) | |
if __name__ == "__main__": | |
print("AAX Audio Converter PostJob v0.1") | |
default_source_path = r"" | |
default_destination_path = r"" | |
default_dry_run = False | |
# Define default values for testing | |
if False: | |
default_source_path = r"/path/to/aaxTemp" | |
default_destination_path = r"/path/to/completeFiles" | |
default_dry_run = False | |
parser = argparse.ArgumentParser( | |
description="Map and copy JSON files based on AAX file names.") | |
parser.add_argument("source_path", | |
type=str, | |
nargs='?', | |
default=default_source_path, | |
help="Path to the source directory") | |
parser.add_argument("destination_path", | |
type=str, | |
nargs='?', | |
default=default_destination_path, | |
help="Path to the destination directory") | |
parser.add_argument("--dry_run", | |
action="store_true", | |
default=default_dry_run, | |
help="Print actions without executing") | |
args = parser.parse_args() | |
if args.source_path and args.destination_path: | |
main(args.source_path, args.destination_path, args.dry_run) | |
else: | |
print("No parameters given. Running unit tests.") | |
unittest.main(argv=[''], exit=False) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment