File size: 4,988 Bytes
161ca80
 
1d72226
273d546
11fa35c
 
624ca3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11fa35c
624ca3b
 
 
 
 
 
 
 
 
 
 
 
 
 
11fa35c
624ca3b
 
 
 
11fa35c
624ca3b
 
11fa35c
161ca80
273d546
565ad30
 
 
624ca3b
 
 
 
 
565ad30
 
 
624ca3b
565ad30
 
 
 
 
 
 
 
11fa35c
565ad30
 
624ca3b
565ad30
624ca3b
565ad30
 
11fa35c
565ad30
624ca3b
 
11fa35c
565ad30
 
624ca3b
 
 
565ad30
 
11fa35c
565ad30
 
6ff1427
565ad30
 
11fa35c
 
565ad30
 
 
 
 
 
 
a66a1eb
565ad30
 
 
 
 
 
 
 
 
11fa35c
565ad30
 
11fa35c
565ad30
 
a66a1eb
 
565ad30
 
a66a1eb
 
565ad30
 
a66a1eb
 
565ad30
 
 
 
 
 
 
 
 
 
 
 
 
 
11fa35c
624ca3b
11fa35c
 
 
161ca80
565ad30
 
 
 
 
 
624ca3b
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import os
import gradio as gr
from audio_separator.separator import Separator
import shutil
import threading
import time
import uuid

# Configuration
# Directory where files will be stored temporarily
UPLOAD_DIR = "output_storage"
MAX_FILE_AGE = 1800 

# Ensure the storage directory exists
os.makedirs(UPLOAD_DIR, exist_ok=True)

def cleanup_scheduler():
    """
    Background task that runs forever. 
    It scans the UPLOAD_DIR every minute and deletes folders older than MAX_FILE_AGE.
    """
    while True:
        try:
            current_time = time.time()
            # Iterate over all items in the storage directory
            for filename in os.listdir(UPLOAD_DIR):
                file_path = os.path.join(UPLOAD_DIR, filename)
                
                # Check if it's a directory (we create one folder per request)
                if os.path.isdir(file_path):
                    # Get the folder's creation/modification time
                    file_age = current_time - os.path.getmtime(file_path)
                    
                    if file_age > MAX_FILE_AGE:
                        print(f"🧹 Cleanup: Removing old folder {filename} ({int(file_age)}s old)")
                        shutil.rmtree(file_path)
                        
        except Exception as e:
            print(f"Error during cleanup: {e}")
        
        # Wait for 60 seconds before checking again
        time.sleep(60)

# Start the cleanup thread immediately (Daemon means it dies when the main app dies)
threading.Thread(target=cleanup_scheduler, daemon=True).start()


def separate_audio(input_file, model_name):
    if input_file is None:
        return (None, None, None, None, None, None)

    # Create a unique subfolder for this specific request inside our managed directory
    # Using UUID ensures no filename collisions between users
    session_id = str(uuid.uuid4())
    session_dir = os.path.join(UPLOAD_DIR, session_id)
    os.makedirs(session_dir, exist_ok=True)

    try:
        separator = Separator(
            output_dir=session_dir,
            output_format="MP3",
            output_bitrate="128k",
        )

        separator.load_model(model_filename=model_name)
        output_files = separator.separate(input_file)

        stems = ["Vocals", "Drums", "Bass", "Other", "Guitar", "Piano"]
        results = []

        for stem in stems:
            # Find the file that matches the stem name
            match = next(
                (os.path.join(session_dir, f) for f in output_files if stem in f),
                None,
            )
            results.append(match)

        # Note: We do NOT call a delete function here anymore. 
        # The background scheduler will handle it later.
        return tuple(results)

    except Exception as e:
        # If the actual separation fails, clean up this specific failed folder immediately
        if os.path.exists(session_dir):
            shutil.rmtree(session_dir)
        raise RuntimeError(f"Separation failed: {e}") from e


# ------------------ UI ------------------

with gr.Blocks() as demo:
    gr.Markdown(
        """
        # 🎵 AI 6-Stem Audio Separator
        Split your music into high-quality individual tracks using **Demucs 6s**.
        """
    )

    with gr.Row():
        with gr.Column(scale=1):
            input_audio = gr.Audio(
                label="Upload Audio File",
                type="filepath",
                interactive=True,
            )

            model_dropdown = gr.Dropdown(
                choices=["htdemucs_6s.yaml"],
                value="htdemucs_6s.yaml",
                label="Select Model",
            )

            separate_btn = gr.Button("🚀 Separate Stems", variant="primary")

        with gr.Column(scale=2):
            gr.Markdown("### 🔊 Separation Results")

            with gr.Row():
                output_vocals = gr.Audio(label="🎤 Vocals", type="filepath")
                output_drums = gr.Audio(label="🥁 Drums", type="filepath")

            with gr.Row():
                output_bass = gr.Audio(label="🎸 Bass", type="filepath")
                output_guitar = gr.Audio(label="🎸 Guitar", type="filepath")

            with gr.Row():
                output_piano = gr.Audio(label="🎹 Piano", type="filepath")
                output_other = gr.Audio(label="🎼 Other", type="filepath")

    separate_btn.click(
        fn=separate_audio,
        inputs=[input_audio, model_dropdown],
        outputs=[
            output_vocals,
            output_drums,
            output_bass,
            output_other,
            output_guitar,
            output_piano,
        ],
    )

    gr.Markdown(
        "⚠️ **Note:** Processing may take 1–2 minutes. Files are automatically cleaned up after 1 hour."
    )


if __name__ == "__main__":
    demo.launch(
        ssr_mode=False,
        theme=gr.themes.Soft(
            primary_hue="indigo",
            secondary_hue="blue",
        ),
    )