Last active
August 30, 2024 13:53
-
-
Save henno/8799a8a6da4de5511b54d955a93a7e95 to your computer and use it in GitHub Desktop.
Script renames .opus files based on audio content
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
""" | |
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