Skip to content

Instantly share code, notes, and snippets.

@henno
Last active August 30, 2024 13:53
Show Gist options
  • Save henno/8799a8a6da4de5511b54d955a93a7e95 to your computer and use it in GitHub Desktop.
Save henno/8799a8a6da4de5511b54d955a93a7e95 to your computer and use it in GitHub Desktop.
Script renames .opus files based on audio content
"""
Script: rename_opus.py
This Python script is designed to search through a specified directory and its subdirectories
to locate all `.opus` audio files, process them, and rename them based on their contents.
It primarily performs the following operations:
1. **Directory Traversal**:
- The script begins by accepting a directory path as a command-line argument.
- It checks if the directory exists, then recursively searches through the directory
and its subdirectories for `.opus` files.
2. **File Conversion and Audio Processing**:
- For each `.opus` file found, the script converts it to a `.wav` format using the `pydub` library.
- It trims the audio to the first 10 seconds to focus on a short snippet for speech recognition.
- The converted `.wav` file is then fed into the Google Speech Recognition API (through the `speech_recognition` library) to transcribe the spoken content into text.
3. **File Renaming**:
- The script extracts a portion of the recognized text (up to 30 characters, ensuring no words are cut off mid-way) and uses this text to rename the original `.opus` file.
- The new filename is formatted to replace spaces with underscores and avoid problematic characters like slashes.
4. **Metadata Insertion**:
- The script utilizes `ffmpeg` to write the original filename into the metadata of the `.opus` file under the description tag.
- A temporary file is created during this process, which replaces the original `.opus` file after successful metadata insertion.
5. **Cleanup**:
- After processing each file, the script deletes the temporary `.wav` files that were created during the conversion process.
- The script is designed to process all `.opus` files found, but currently stops after processing the first file to allow for verification of the changes.
6. **Error Handling**:
- The script includes error handling for potential issues during file conversion, speech recognition, and file renaming, ensuring that processing continues even if an error occurs with a particular file.
7. **Output**:
- Throughout the process, the script provides real-time feedback on its operations using colored terminal output (via the `colorama` library), making it easier to follow what the script is doing.
**Usage**:
`python rename_opus.py <directory_path>`
**Dependencies**:
- `os`: For file and directory operations.
- `sys`: To handle command-line arguments and system operations.
- `uuid`: For generating unique identifiers, particularly for temporary files.
- `time`: To introduce delays, ensuring files are properly closed before deletion.
- `pydub`: For audio file conversion and manipulation.
- `speech_recognition`: For speech-to-text transcription using Google Speech Recognition.
- `colorama`: For colored terminal output to enhance user feedback.
- `ffmpeg`: A command-line tool used here to modify audio file metadata.
This script is particularly useful for renaming audio files based on their content,
which can be beneficial for organizing recordings, such as in voice note collections
or transcription projects where filenames reflect the content.
"""
import os
import sys
import uuid
import time
from pydub import AudioSegment
import speech_recognition as sr
from colorama import Fore, Style, init
# Initialize colorama
init(autoreset=True)
def find_opus_files(directory):
opus_files = []
print(Fore.BLUE + f"Searching for .opus files in directory: {directory}")
for root, _, files in os.walk(directory):
print(Fore.BLUE + f"Checking directory: {root}")
for file in files:
if file.endswith('.opus'):
file_path = os.path.join(root, file)
print(Fore.BLUE + f"Found .opus file: {file_path}")
opus_files.append(file_path)
else:
print(Fore.BLUE + f"Skipped file (not .opus): {file}")
print(Fore.BLUE + f"Total .opus files found: {len(opus_files)}")
return opus_files
def process_opus_files(opus_files):
recognizer = sr.Recognizer()
for opus_path in opus_files:
print(Fore.BLUE + f"Processing file: {opus_path}")
original_filename = os.path.basename(opus_path)
track_number = original_filename.split(' ')[0] # Assuming the track number is the first part of the file name
wav_path = opus_path.replace('.opus', '.wav')
# Convert .opus to .wav
print(Fore.BLUE + f"Converting .opus to .wav: {wav_path}")
try:
audio = AudioSegment.from_file(opus_path)
# Trim to the first 10 seconds
trimmed_audio = audio[:10000] # 10000 milliseconds = 10 seconds
trimmed_audio.export(wav_path, format="wav")
except Exception as e:
print(Fore.RED + f"Error converting {opus_path} to .wav: {e}")
continue
# Load the .wav file for transcription
try:
with sr.AudioFile(wav_path) as source:
audio_data = recognizer.record(source)
print(Fore.BLUE + f"Recognizing speech in: {wav_path}")
text = recognizer.recognize_google(audio_data, language='ru-RU')
print(Fore.BLUE + f"Recognized text: {text}")
# Limit to the first 30 characters and ensure it doesn't cut off in the middle of a word
max_chars = 30
if len(text) > max_chars:
text = text[:max_chars]
if ' ' in text:
text = text[:text.rfind(' ')]
new_filename = f"{track_number} {text.replace(' ', '_').replace('/', '_')}.opus"
new_file_path = os.path.join(os.path.dirname(opus_path), new_filename)
# Renaming file
print(Fore.BLUE + f"Renaming file: {opus_path} to {new_file_path}")
os.rename(opus_path, new_file_path)
# Generate a unique temporary filename
temp_file_path = f"{new_file_path}.{uuid.uuid4().hex}.tmp.opus"
# Write original filename to the metadata description using FFmpeg
print(Fore.BLUE + f"Writing original filename to metadata: {original_filename}")
os.system(f'ffmpeg -i "{new_file_path}" -c copy -metadata description="{original_filename}" "{temp_file_path}"')
# Remove the old file and replace it with the new file
if os.path.exists(new_file_path):
os.remove(new_file_path)
os.rename(temp_file_path, new_file_path)
# Ensure the .wav file is no longer in use and delete it
time.sleep(1) # Wait for a moment to ensure the file is closed
print(Fore.BLUE + f"Removing temporary .wav file: {wav_path}")
if os.path.exists(wav_path):
os.remove(wav_path)
# Stop after processing the first file for verification
print(Fore.BLUE + "Processing complete for the first file. Stopping for verification.")
# os._exit(0) # Force exit the script after the first file is processed
except Exception as e:
print(Fore.RED + f"Error processing {opus_path}: {e}")
continue
if __name__ == "__main__":
print(Fore.BLUE + "Starting script...")
if len(sys.argv) != 2:
print(Fore.RED + "Usage: python rename_opus.py <directory_path>")
sys.exit(1)
directory = sys.argv[1]
print(Fore.BLUE + f"Received directory argument: {directory}")
# Check if the directory exists
if not os.path.exists(directory):
print(Fore.RED + f"Directory '{directory}' does not exist.")
sys.exit(1)
else:
print(Fore.BLUE + f"Directory exists: {directory}")
# Find all .opus files in the directory and its subdirectories
opus_files = find_opus_files(directory)
if not opus_files:
print(Fore.RED + f"No .opus files found in '{directory}'.")
sys.exit(0)
else:
print(Fore.BLUE + f"Proceeding to process {len(opus_files)} files...")
# Process and rename the found .opus files
process_opus_files(opus_files)
print(Fore.BLUE + "Script completed.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment