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.