This commit is contained in:
bender
2023-11-12 00:10:13 -05:00
parent d6410e4ab0
commit b1d02cff7b
27 changed files with 506 additions and 403 deletions
View File
+28
View File
@@ -0,0 +1,28 @@
'''Align tracks end to end, chop based on chapters list durations, export to final folder, archive originals'''
import sys
import os
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent_dir)
from XML_JSON.json_scripts import load_config
from Chop_Tag_Audio.final_metadata_add import traverse_directory
from Chop_Tag_Audio.exportchapters import split_mp3s
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
config_path = os.path.join(parent_dir, 'config.json')
config = load_config(config_path)
path_to_ffmpeg = config['ffmpeg']
archive = config['archive']
annex = config['annex']
circulation = config['circulation']
bookshelf = config['bookshelf']
def chop_tag_audio():
split_mp3s(annex, circulation)
traverse_directory(circulation, bookshelf)
if __name__ == '__main__':
chop_tag_audio()
+100
View File
@@ -0,0 +1,100 @@
import sys
import random
import os
import shutil
from pydub import AudioSegment
from XML_JSON.json_scripts import load_config
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
config_path = os.path.join(parent_dir, 'config.json')
config = load_config(config_path)
path_to_ffmpeg = config['ffmpeg']
archive = config['archive']
annex = config['annex']
circulation = config['circulation']
AudioSegment.converter = path_to_ffmpeg
AudioSegment.ffmpeg = path_to_ffmpeg
AudioSegment.ffprobe = path_to_ffmpeg
def export_audio_file(combined, start_ms, end_ms, name, circulation, author_name, folder_name):
export = combined[start_ms:end_ms]
export_folder = os.path.join(circulation, author_name, folder_name)
if not os.path.exists(export_folder):
os.makedirs(export_folder)
export_path = os.path.join(export_folder, f"{name}.mp3")
export.export(export_path, format="mp3")
return export_folder # Return the folder path where the file was exported
def split_mp3s(annex, circulation):
update_messages = [
"Getting close...",
"Hang in there...",
"Still running...",
"Almost done...",
"It'll all be over soon...",
"Bear with me..."
]
# Shuffle the list of messages
random.shuffle(update_messages)
# An iterator over the shuffled list of messages
message_cycle = iter(update_messages)
for folder in sorted(os.listdir(annex)):
folder_path = os.path.join(annex, folder)
if os.path.isdir(folder_path):
file_count = 0 # Reset file_count to 0 for each new folder
try:
mp3_files = [file for file in sorted(os.listdir(folder_path)) if file.endswith(".mp3")]
combined = AudioSegment.empty()
for mp3_file in mp3_files:
combined += AudioSegment.from_file(os.path.join(folder_path, mp3_file))
combined = combined.set_channels(1) # Assuming you want mono audio
labels_file = os.path.join(folder_path, "overdrive_chapters_ms_spans.txt")
with open(labels_file, "r") as f:
labels = [line.strip().split("\t") for line in f]
for start, end, name in labels:
if end == "0":
end = str(len(combined)) # Use str to keep it consistent with start
start_ms = int(start)
end_ms = int(end)
author_name, folder_name = folder.split(" - ")
formatted_name = f"{file_count:03}_{name.replace('/', '_')}" # Formatting name with file_count
export_folder = export_audio_file(combined, start_ms, end_ms, formatted_name, circulation, author_name.strip(), folder_name.strip())
artwork_and_metadata(export_folder, folder_path)
file_count += 1 # Increment file_count after each file is exported
message = next(message_cycle, None)
if message is None: # If the end of the list is reached, shuffle and restart
random.shuffle(update_messages)
message_cycle = iter(update_messages)
message = next(message_cycle)
print(message)
except Exception as e:
print(f"Error processing {folder}: {e}")
archive_folder = os.path.join(archive, folder)
if not os.path.exists(archive_folder):
os.makedirs(archive_folder)
shutil.move(folder_path, archive_folder)
def artwork_and_metadata(export_folder, folder_path):
metadata_file = os.path.join(folder_path, "cleaned_metadata.json")
album_art = os.path.join(folder_path, "folder.jpg")
if os.path.exists(album_art):
shutil.copy(album_art, export_folder)
if os.path.exists(metadata_file):
shutil.copy(metadata_file, export_folder)
if __name__ == '__main__':
split_mp3s(annex, circulation)
+118
View File
@@ -0,0 +1,118 @@
'''Adds metadata to the chapterized mp3s'''
import os
import shutil
import json
import re
from mutagen.id3 import ID3, APIC, TIT2, TPE1, TCOM, TCON, TRCK, TALB, ID3NoHeaderError
def process_directories(directory):
'''Iterate through all subfolders in each author's directory & create cleaned_metadata.json'''
for author_name in os.listdir(directory):
author_path = os.path.join(directory, author_name)
if os.path.isdir(author_path):
for book_title in os.listdir(author_path):
book_path = os.path.join(author_path, book_title)
metadata_file = os.path.join(book_path, "cleaned_metadata.json")
if os.path.exists(metadata_file):
metadata = read_metadata(metadata_file)
if metadata:
add_metadata_to_files(book_path, metadata)
else:
print(f"Skipping {book_title} due to json error")
else:
print(f"Metadata file not found in {book_title}")
def read_metadata(metadata_file):
'''Read and return json data from the given metadata file'''
try:
with open(metadata_file, "r") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Error reading metadata file {metadata_file}: {e}")
return None
def get_album_title(metadata):
'''Extract the Book Title from metadata and clean it'''
album_title = metadata.get("Title", "")
return re.sub(r"-s\b", "'s", album_title)
def get_artist(metadata):
'''Extract the Author from metadata'''
return metadata.get("Author", "")
def get_composer(metadata):
'''Extract the Narrator from metadata'''
return metadata.get("Narrator", "")
def get_genre(metadata):
'''Extract the Subjects from metadata'''
return metadata.get("Subjects", "")
def add_metadata_to_files(directory, metadata):
'''Add metadata to all chapterized mp3s in the directory'''
if not metadata:
print(f"No metadata available for directory {directory}. Skipping.")
return
album_title_1 = get_album_title(metadata)
artist = get_artist(metadata)
composer = get_composer(metadata)
genre = get_genre(metadata)
'''Replace all occurrences of -s with "'s" in the album title'''
album_title_2 = re.sub("-s", "'s", album_title_1)
'''Replace all remaining occurrences of - with "'" in the album title'''
album_title = re.sub("-", "'", album_title_2)
'''Get a list of all mp3 files in the current directory'''
mp3_files = [f for f in os.listdir(directory) if f.endswith(".mp3")]
'''Sort the list of mp3 files alphabetically'''
mp3_files.sort()
'''Iterate through the list of mp3 files'''
for i, mp3_file in enumerate(mp3_files, 1):
mp3_file_path = os.path.join(directory, mp3_file)
'''Extract the title of the mp3 file'''
file_name, _ = os.path.splitext(mp3_file)
title = re.sub(r"^\d+_", "", file_name) # Correctly extracts the title from the filename
'''Add the track number & other metadata to the file using ID3'''
try:
audio = ID3(mp3_file_path)
except ID3NoHeaderError:
audio = ID3()
audio.add(TRCK(encoding=3, text=str(i)))
audio.add(TIT2(encoding=3, text=title))
audio.add(TPE1(encoding=3, text=artist))
audio.add(TCOM(encoding=3, text=composer))
audio.add(TCON(encoding=3, text=genre))
audio.add(TALB(encoding=3, text=album_title))
'''Check if folder.jpg exists in the current directory'''
album_art_path = os.path.join(directory, "folder.jpg")
if os.path.exists(album_art_path):
'''Add the album art to the file using ID3'''
with open(album_art_path, "rb") as albumart:
audio.add(APIC(encoding=3, mime='image/jpeg', type=3, desc='Cover', data=albumart.read()))
audio.save(mp3_file_path) # Save the tags to the file
def meta_to_mp3(directory, bookshelf):
for author_name in os.listdir(directory):
author_path = os.path.join(directory, author_name)
if os.path.isdir(author_path):
for book_title in os.listdir(author_path):
book_path = os.path.join(author_path, book_title)
destination_path = os.path.join(bookshelf, author_name, book_title)
os.makedirs(os.path.dirname(destination_path), exist_ok=True)
shutil.move(book_path, destination_path)
print(f"Completed: '{book_title}' by '{author_name}'")
# After moving all books, check if the author directory is empty and remove it
if not os.listdir(author_path):
os.rmdir(author_path)
def traverse_directory(directory, bookshelf):
process_directories(directory)
meta_to_mp3(directory, bookshelf)
@@ -0,0 +1,8 @@
{
"folders": [
{
"path": ".."
}
],
"settings": {}
}