op 2.0
This commit is contained in:
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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": {}
|
||||
}
|
||||
Reference in New Issue
Block a user