Conversion of MP3 Files to TAF Files with Teddycloud and Docker

Hi everyone,

I want to show you how to use Teddycloud and Docker to convert a large number of MP3 files to TAF files. The process involves creating commands in the first script, which are then executed in a second script. It’s important to note that Teddycloud is run on the local machine via Docker. Additionally, the name of the folder will be used as the name of the TAF file.

Script to Create Commands

This script scans a directory for MP3 files, sorts them numerically, and creates the corresponding Teddycloud commands, which are saved in a file. The name of the folder will be used as the name of the TAF file.

import os
import re

def extract_number(name):
    """Extracts the first number from a string."""
    match = re.search(r'\d+', name)
    return int(match.group()) if match else float('inf')

def list_mp3_files(directory):
    """Lists all MP3 files in a directory and sorts them numerically."""
    files = [f for f in os.listdir(directory) if f.endswith('.mp3')]
    files.sort(key=extract_number)  # Sort by numerical value
    return files

def escape_path(path):
    """Escapes paths and filenames to handle spaces and special characters."""
    return path.replace(" ", "\\ ").replace("(", "\\(").replace(")", "\\)").replace("&", "\\&")

def create_teddycloud_command(target_directory, container_base_path):
    """Creates the teddycloud command for a target directory."""
    target_name = os.path.basename(target_directory) + ".taf"
    mp3_files = list_mp3_files(target_directory)
    if not mp3_files:
        print(f"No MP3 files found in directory {target_directory}.")
        return None

    # Correct the path to avoid duplicate segments
    container_target_directory = target_directory.replace(
        '/path/to/your/local/directory',
        container_base_path
    )
    source_files = ' '.join([escape_path(os.path.join(container_target_directory, f)) for f in mp3_files])
    command = f"teddycloud --encode {escape_path(target_name)} {source_files}"
    return command

def main():
    host_base_directory = '/path/to/your/local/directory'
    container_base_path = '/mount/container/directory'
    subdirectories = [os.path.join(host_base_directory, d) for d in os.listdir(host_base_directory) if os.path.isdir(os.path.join(host_base_directory, d))]

    # Sort subdirectories numerically
    subdirectories.sort(key=lambda d: extract_number(os.path.basename(d)))

    with open('teddycloud_commands.txt', 'w') as file:
        for subdir in subdirectories:
            command = create_teddycloud_command(subdir, container_base_path)
            if command:
                file.write(f"{command}\n")  # Newline after each command
                print(f"Command for {subdir}:")
                print(command)
                print()

if __name__ == "__main__":
    main()

Script to Execute the Commands

This script reads the created commands from the file and executes them in a Docker container.

import subprocess
import time

def read_commands(file_path):
    """Reads the commands from the file."""
    with open(file_path, 'r') as file:
        commands = file.read().strip().split('\n')
    return commands

def execute_command_in_container(container_id, command):
    """Executes a command in a Docker container."""
    # Combine the command to change the directory with the actual command
    docker_command = f"docker exec -it {container_id} /bin/bash -c \"cd /mount/container/directory && {command}\""
    print(f"Executing command: {docker_command}")  # Debugging output
    result = subprocess.run(docker_command, shell=True, capture_output=True, text=True)
    if result.returncode == 0:
        print(f"Successfully executed: {command}")
    else:
        print(f"Error executing: {command}")
        print(result.stderr)

def main():
    container_id = 'your_container_id'
    commands_file = 'teddycloud_commands.txt'
    commands = read_commands(commands_file)

    for command in commands:
        if command.strip():  # Check if the command is not empty
            execute_command_in_container(container_id, command)
            # Add a sleep to prevent the container from being overloaded
            time.sleep(5)
        else:
            print("Skipped empty command")

if __name__ == "__main__":
    main()

Notes

  • Replace /path/to/your/local/directory and /mount/container/directory with the actual paths on your local machine and in the Docker container.
  • Replace your_container_id with the actual ID or name of your Docker container.

I hope this guide helps! If you have any questions, feel free to ask.

2 Likes

Hi Florian,

very nice script. Thank you for that!
It helps alot, when you fill your teddycloud the first time with your old mp3 collection.

But there is one problem, i run into during my first trys with this.
The encoder is limited to 99 files for one .taf, so it stops working if you have a directory with more than 99 files in it.
So it would be nice, if the create-commands script would give at least a warning, if you try to encode >99 files to one .taf
An even better solution would be some kind of automated splitting in something like FILENAME_part1.taf, FILENAME_part2.taf, …