Spaces:
Running
Running
File size: 6,917 Bytes
1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f 1190db4 d48101f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
from pydub import AudioSegment
import os
def merge_mp3_files(file_paths, output_filename, pause_ms=500):
"""
Merges multiple MP3 files into a single MP3 file with a specified pause
between each segment. Skips missing or empty files.
Args:
file_paths (list): A list of paths to MP3 files to merge.
Can contain None entries for files that failed synthesis; these will be skipped.
output_filename (str): The path to save the merged MP3 file.
pause_ms (int): Duration of silence in milliseconds to add between segments.
Returns:
str: The path to the merged MP3 file if successful, None otherwise.
"""
if not file_paths:
print("Warning: No file paths provided for merging.")
return None
valid_segments = []
for file_path in file_paths:
if file_path and os.path.exists(file_path) and os.path.getsize(file_path) > 0:
try:
segment = AudioSegment.from_mp3(file_path)
valid_segments.append(segment)
except Exception as e:
print(f"Error loading audio segment from {file_path}: {e}. Skipping this file.")
elif file_path: # File path provided but file is missing or empty
print(f"Warning: File {file_path} is missing or empty. Skipping.")
# If file_path is None, it's silently skipped (already handled upstream)
if not valid_segments:
print("No valid audio segments found to merge.")
return None
# Start with the first valid segment
combined_audio = valid_segments[0]
# Add subsequent segments with pauses
if len(valid_segments) > 1:
pause_segment = AudioSegment.silent(duration=max(0, pause_ms)) # Ensure pause_ms is not negative
for segment in valid_segments[1:]:
combined_audio += pause_segment
combined_audio += segment
try:
# Export the combined audio to MP3 format
# May require ffmpeg/libav to be installed and accessible in PATH
combined_audio.export(output_filename, format="mp3")
if os.path.exists(output_filename) and os.path.getsize(output_filename) > 0:
return output_filename
else:
print(f"Error: Merged file {output_filename} was not created or is empty after export.")
return None
except Exception as e:
print(f"Error exporting merged MP3 to {output_filename}: {e}")
return None
# Helper function to create dummy MP3 files for testing (requires pydub and ffmpeg)
def _create_dummy_mp3(filename, duration_ms=1000, text_for_log="dummy"):
try:
# Create a silent audio segment
silence = AudioSegment.silent(duration=duration_ms)
# Export it as an MP3 file
silence.export(filename, format="mp3")
print(f"Successfully created dummy MP3: {filename} (duration: {duration_ms}ms) for '{text_for_log}'")
return True
except Exception as e:
print(f"Could not create dummy MP3 '{filename}'. Ensure ffmpeg is installed and accessible. Error: {e}")
return False
if __name__ == '__main__':
print("--- Testing merge_mp3_files ---")
test_output_dir = "test_audio_merge_output"
os.makedirs(test_output_dir, exist_ok=True)
dummy_files = []
# Create some dummy MP3 files for the test
if _create_dummy_mp3(os.path.join(test_output_dir, "dummy1.mp3"), 1000, "Segment 1"):
dummy_files.append(os.path.join(test_output_dir, "dummy1.mp3"))
if _create_dummy_mp3(os.path.join(test_output_dir, "dummy2.mp3"), 1500, "Segment 2"):
dummy_files.append(os.path.join(test_output_dir, "dummy2.mp3"))
# Test case 1: Merge existing files
if len(dummy_files) == 2:
output_merged_1 = os.path.join(test_output_dir, "merged_test1.mp3")
print(f"\nAttempting to merge: {dummy_files} with 300ms pause into {output_merged_1}")
result_path_1 = merge_mp3_files(dummy_files, output_merged_1, pause_ms=300)
if result_path_1 and os.path.exists(result_path_1):
print(f"SUCCESS: Merged audio created at: {result_path_1} (Size: {os.path.getsize(result_path_1)} bytes)")
else:
print(f"FAILURE: Merging test case 1 failed.")
else:
print("\nSkipping merge test case 1 due to failure in creating dummy files.")
# Test case 2: Include a non-existent file and a None entry
files_with_issues = [
dummy_files[0] if dummy_files else None,
os.path.join(test_output_dir, "non_existent_file.mp3"),
None, # Representing a failed synthesis
dummy_files[1] if len(dummy_files) > 1 else None
]
# Filter out None from the list if dummy files weren't created
files_with_issues_filtered = [f for f in files_with_issues if f is not None or isinstance(f, str)]
if any(f for f in files_with_issues_filtered if f and os.path.exists(f)): # Proceed if at least one valid dummy file exists
output_merged_2 = os.path.join(test_output_dir, "merged_test2_with_issues.mp3")
print(f"\nAttempting to merge: {files_with_issues_filtered} with 500ms pause into {output_merged_2}")
result_path_2 = merge_mp3_files(files_with_issues_filtered, output_merged_2, pause_ms=500)
if result_path_2 and os.path.exists(result_path_2):
print(f"SUCCESS (with skips): Merged audio created at: {result_path_2} (Size: {os.path.getsize(result_path_2)} bytes)")
else:
print(f"NOTE: Merging test case 2 might result in fewer segments or failure if no valid files remained.")
else:
print("\nSkipping merge test case 2 as no valid dummy files were available.")
# Test case 3: Empty list of files
output_merged_3 = os.path.join(test_output_dir, "merged_test3_empty.mp3")
print(f"\nAttempting to merge an empty list of files into {output_merged_3}")
result_path_3 = merge_mp3_files([], output_merged_3, pause_ms=100)
if result_path_3 is None:
print("SUCCESS: Correctly handled empty file list (returned None).")
else:
print(f"FAILURE: Expected None for empty file list, got {result_path_3}")
# Test case 4: List with only None or invalid paths
output_merged_4 = os.path.join(test_output_dir, "merged_test4_all_invalid.mp3")
print(f"\nAttempting to merge list with only invalid/None files into {output_merged_4}")
result_path_4 = merge_mp3_files([None, "non_existent.mp3"], output_merged_4, pause_ms=100)
if result_path_4 is None:
print("SUCCESS: Correctly handled list with only invalid/None files (returned None).")
else:
print(f"FAILURE: Expected None for all-invalid list, got {result_path_4}")
print(f"\nTest finished. Check ./{test_output_dir}/ for any generated files.")
# You might want to add shutil.rmtree(test_output_dir) here for cleanup after visual inspection. |