Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,276 +1,110 @@
|
|
1 |
import gradio as gr
|
2 |
-
import
|
3 |
-
import
|
4 |
-
import subprocess
|
5 |
-
from datetime import datetime
|
6 |
-
from pydub import AudioSegment, silence
|
7 |
-
from pydub.effects import normalize
|
8 |
import noisereduce as nr
|
9 |
import numpy as np
|
10 |
-
import
|
11 |
-
|
12 |
-
import
|
13 |
-
import yt_dlp
|
14 |
-
|
15 |
-
|
16 |
-
# Part 1: Audio Processor
|
17 |
-
# Global variable to store processed audio path from Step 1
|
18 |
-
processed_audio_path_step1 = None
|
19 |
-
|
20 |
-
SILENCE_THRESHOLD_MS = 500 # Example: 500ms
|
21 |
-
|
22 |
-
def generate_unique_filename(base_path, duration, suffix=""):
|
23 |
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
24 |
-
duration_str = f"{duration:.2f}"
|
25 |
-
base_name = os.path.basename(base_path)
|
26 |
-
name, ext = os.path.splitext(base_name)
|
27 |
-
unique_filename = f"Mirchi_{duration_str}_{timestamp}{suffix}{ext}"
|
28 |
-
return unique_filename
|
29 |
-
|
30 |
-
sarcastic_messages = [
|
31 |
-
"Oh wow, that went well... Not.",
|
32 |
-
"You really outdid yourself this time.",
|
33 |
-
"Congratulations, you've hit an error. Again.",
|
34 |
-
"Well, that didn't work. Shocker.",
|
35 |
-
"Surprise, surprise, another error.",
|
36 |
-
"Great job! Another error for your collection."
|
37 |
-
]
|
38 |
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
41 |
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
46 |
|
47 |
-
|
48 |
-
|
|
|
|
|
|
|
|
|
49 |
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
if silence_duration <= silence_threshold_ms * sr / 1000:
|
54 |
-
processed_audio.append(y[last_end:start])
|
55 |
-
|
56 |
-
processed_audio.append(y[start:end])
|
57 |
-
last_end = end
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
processed_audio.append(y[last_end:])
|
62 |
|
63 |
-
|
|
|
64 |
|
65 |
-
return
|
66 |
-
|
67 |
-
def apply_noise_removal(y, sr):
|
68 |
-
reduced_noise = nr.reduce_noise(y=y, sr=sr, prop_decrease=0.6, n_fft=1024, win_length=512, hop_length=256)
|
69 |
-
return reduced_noise
|
70 |
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
sample_width=2,
|
77 |
-
channels=1
|
78 |
-
)
|
79 |
-
return audio_segment
|
80 |
-
|
81 |
-
def process_audio_step1(audio_path, crossfade, normalize_audio, noise_removal):
|
82 |
-
global processed_audio_path_step1
|
83 |
-
try:
|
84 |
-
if not audio_path:
|
85 |
-
return None, random.choice(sarcastic_messages), None
|
86 |
-
|
87 |
-
y, sr = remove_unwanted_sounds(audio_path, SILENCE_THRESHOLD_MS)
|
88 |
|
89 |
-
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
|
92 |
-
|
|
|
|
|
|
|
|
|
93 |
|
94 |
-
|
95 |
-
|
96 |
|
97 |
-
|
98 |
-
|
99 |
-
processed_audio = final_audio[:crossfade_duration]
|
100 |
-
for i in range(crossfade_duration, len(final_audio), crossfade_duration):
|
101 |
-
chunk = final_audio[i:i + crossfade_duration]
|
102 |
-
processed_audio = processed_audio.append(chunk, crossfade=crossfade_duration)
|
103 |
-
else:
|
104 |
-
processed_audio = final_audio
|
105 |
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
except Exception as e:
|
113 |
-
return None, f"{random.choice(sarcastic_messages)} Error: {str(e)}", None
|
114 |
-
|
115 |
-
def process_audio_step2(audio_path, target_duration):
|
116 |
-
try:
|
117 |
-
if not audio_path:
|
118 |
-
return None, random.choice(sarcastic_messages), None
|
119 |
-
|
120 |
-
audio = AudioSegment.from_file(audio_path)
|
121 |
-
original_duration = len(audio) / 1000
|
122 |
-
stretch_ratio = original_duration / target_duration
|
123 |
-
unique_filename = generate_unique_filename(audio_path, original_duration, "_step2")
|
124 |
-
output_path = os.path.join("audios_output_step2", unique_filename)
|
125 |
-
os.makedirs("audios_output_step2", exist_ok=True)
|
126 |
-
command = [
|
127 |
-
'ffmpeg',
|
128 |
-
'-i', audio_path,
|
129 |
-
'-filter:a', f'atempo={stretch_ratio}',
|
130 |
-
output_path
|
131 |
-
]
|
132 |
-
subprocess.run(command, check=True)
|
133 |
-
return output_path, "Done! Your audio is stretched.", None
|
134 |
-
except subprocess.CalledProcessError:
|
135 |
-
return None, f"{random.choice(sarcastic_messages)} FFmpeg error occurred.", None
|
136 |
-
except Exception as e:
|
137 |
-
return None, f"{random.choice(sarcastic_messages)} Error: {str(e)}", None
|
138 |
-
|
139 |
-
def clear_output_folders():
|
140 |
-
folders = ["audios_output_step1", "audios_output_step2"]
|
141 |
-
for folder in folders:
|
142 |
-
if os.path.exists(folder):
|
143 |
-
shutil.rmtree(folder)
|
144 |
-
return "All output folders are cleared."
|
145 |
-
|
146 |
-
def open_output_folder(folder_name):
|
147 |
-
try:
|
148 |
-
if os.name == 'nt': # Windows
|
149 |
-
os.startfile(folder_name)
|
150 |
-
elif os.name == 'posix': # macOS or Linux
|
151 |
-
if os.uname().sysname == 'Darwin': # macOS
|
152 |
-
subprocess.run(['open', folder_name])
|
153 |
-
else: # Linux
|
154 |
-
subprocess.run(['xdg-open', folder_name])
|
155 |
-
return "Opening folder."
|
156 |
-
except Exception as e:
|
157 |
-
return f"{random.choice(sarcastic_messages)} Failed to open folder: {str(e)}"
|
158 |
-
|
159 |
-
# Part 2: YouTube Downloader
|
160 |
-
def download_youtube_video(url, save_path='.', file_format='mp4'):
|
161 |
-
try:
|
162 |
-
if file_format == 'mp4':
|
163 |
-
ydl_opts = {
|
164 |
-
'format': 'bestvideo+bestaudio/best',
|
165 |
-
'outtmpl': os.path.join(save_path, '%(title)s.%(ext)s'),
|
166 |
-
'merge_output_format': 'mp4',
|
167 |
-
'postprocessors': [{
|
168 |
-
'key': 'FFmpegVideoConvertor',
|
169 |
-
'preferedformat': 'mp4',
|
170 |
-
}],
|
171 |
-
}
|
172 |
-
elif file_format == 'mp3':
|
173 |
-
ydl_opts = {
|
174 |
-
'format': 'bestaudio/best',
|
175 |
-
'outtmpl': os.path.join(save_path, '%(title)s.%(ext)s'),
|
176 |
-
'postprocessors': [{
|
177 |
-
'key': 'FFmpegExtractAudio',
|
178 |
-
'preferredcodec': 'mp3',
|
179 |
-
'preferredquality': '192',
|
180 |
-
}],
|
181 |
-
}
|
182 |
-
|
183 |
-
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
184 |
-
info = ydl.extract_info(url, download=True)
|
185 |
-
filename = ydl.prepare_filename(info)
|
186 |
-
file_ext = '.mp4' if file_format == 'mp4' else '.mp3'
|
187 |
-
final_filename = filename.rsplit('.', 1)[0] + file_ext
|
188 |
-
|
189 |
-
if os.path.exists(final_filename):
|
190 |
-
return final_filename, f"Downloaded successfully as {file_format.upper()}!"
|
191 |
-
else:
|
192 |
-
return None, f"Download failed, file not found."
|
193 |
-
|
194 |
-
except Exception as e:
|
195 |
-
return None, f"An error occurred: {str(e)}"
|
196 |
-
|
197 |
-
def download_video_from_ui(url, save_location, file_format):
|
198 |
-
if not url.strip():
|
199 |
-
return None, "Please enter a valid URL."
|
200 |
|
201 |
-
|
202 |
-
save_location = '.' # Use current directory if save path is empty
|
203 |
-
|
204 |
-
file_path, message = download_youtube_video(url, save_location, file_format)
|
205 |
-
return file_path, message
|
206 |
-
|
207 |
-
|
208 |
-
# Gradio interface with tabs
|
209 |
-
with gr.Blocks(theme="soft") as demo:
|
210 |
-
with gr.Tabs():
|
211 |
-
with gr.Tab("Audio Processor"):
|
212 |
-
with gr.Column():
|
213 |
-
gr.Markdown("### Step 1: Cleanup")
|
214 |
-
audio_input_step1 = gr.Audio(label="Upload Audio for Cleanup", type="filepath")
|
215 |
-
with gr.Row():
|
216 |
-
crossfade_option = gr.Checkbox(label="Apply Crossfade")
|
217 |
-
normalize_option = gr.Checkbox(label="Normalize Audio")
|
218 |
-
noise_removal_option = gr.Checkbox(label="Noise Removal")
|
219 |
-
step1_error = gr.Textbox(lines=2, interactive=False)
|
220 |
-
step1_output = gr.Audio(label="Processed Audio", interactive=False)
|
221 |
-
step1_button = gr.Button("Process Step 1")
|
222 |
-
|
223 |
-
gr.Markdown("### Step 2: Time Stretch")
|
224 |
-
step2_audio_input = gr.Audio(label="Upload Audio for Time Stretch", type="filepath")
|
225 |
-
with gr.Row():
|
226 |
-
target_duration_input = gr.Number(label="Target Duration (seconds)", value=60.0)
|
227 |
-
process_step2_button = gr.Button("Process Step 2")
|
228 |
-
step2_error = gr.Textbox(lines=2, interactive=False)
|
229 |
-
step2_output = gr.Audio(label="Processed Audio", interactive=False)
|
230 |
-
|
231 |
-
gr.Markdown("### Manage Outputs")
|
232 |
-
with gr.Row():
|
233 |
-
open_output_button_step1 = gr.Button("Open Cleanup Output Folder")
|
234 |
-
open_output_button_step2 = gr.Button("Open Stretch Output Folder")
|
235 |
-
with gr.Row():
|
236 |
-
clear_button = gr.Button("Clear Output Folders")
|
237 |
-
step1_button.click(fn=process_audio_step1,
|
238 |
-
inputs=[audio_input_step1, crossfade_option, normalize_option, noise_removal_option],
|
239 |
-
outputs=[step1_output, step1_error, step1_output])
|
240 |
-
process_step2_button.click(fn=process_audio_step2,
|
241 |
-
inputs=[step2_audio_input, target_duration_input],
|
242 |
-
outputs=[step2_output, step2_error, step2_output])
|
243 |
-
clear_button.click(fn=clear_output_folders, inputs=None, outputs=None)
|
244 |
-
open_output_button_step1.click(fn=lambda: open_output_folder("audios_output_step1"), inputs=None, outputs=None)
|
245 |
-
open_output_button_step2.click(fn=lambda: open_output_folder("audios_output_step2"), inputs=None, outputs=None)
|
246 |
-
|
247 |
-
with gr.Tab("YouTube Downloader"):
|
248 |
-
gr.Markdown("""
|
249 |
-
<div style="text-align: center;">
|
250 |
-
<h1>YouTube Video Downloader</h1>
|
251 |
-
<p>This application allows you to download YouTube videos or audio tracks.
|
252 |
-
You can choose the format (MP4 for video or MP3 for audio) and specify a save location for the downloaded file.</p>
|
253 |
-
</div>
|
254 |
-
""")
|
255 |
-
with gr.Row():
|
256 |
-
url_input = gr.Textbox(label="YouTube Video URL", placeholder="Enter YouTube video URL here...")
|
257 |
-
format_input = gr.Radio(label="Select Format", choices=['mp4', 'mp3'], value='mp4')
|
258 |
-
save_path_input = gr.Textbox(label="Save Path", placeholder="Enter path to save the video (or leave blank for current directory)")
|
259 |
-
|
260 |
-
download_button = gr.Button("Download Video/Audio")
|
261 |
-
output_text = gr.Textbox(label="Status", interactive=False)
|
262 |
-
file_output = gr.File(label="Download Link")
|
263 |
-
def enable_button(url):
|
264 |
-
if url.strip():
|
265 |
-
return gr.update(interactive=True)
|
266 |
-
else:
|
267 |
-
return gr.update(interactive=False)
|
268 |
-
|
269 |
-
def execute_download(url, save_path, file_format):
|
270 |
-
file_path, result = download_video_from_ui(url, save_path, file_format)
|
271 |
-
return result, file_path
|
272 |
-
|
273 |
-
url_input.change(enable_button, inputs=url_input, outputs=download_button)
|
274 |
-
download_button.click(execute_download, inputs=[url_input, save_path_input, format_input], outputs=[output_text, file_output])
|
275 |
|
276 |
-
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
+
from pydub import AudioSegment
|
3 |
+
import librosa
|
|
|
|
|
|
|
|
|
4 |
import noisereduce as nr
|
5 |
import numpy as np
|
6 |
+
from io import BytesIO
|
7 |
+
import tempfile
|
8 |
+
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
+
# Function to load audio
|
11 |
+
def load_audio(audio_file_path):
|
12 |
+
# Open the file and read its bytes
|
13 |
+
with open(audio_file_path, "rb") as f:
|
14 |
+
audio_bytes = f.read()
|
15 |
|
16 |
+
# Load the audio using pydub from the file bytes
|
17 |
+
audio_segment = AudioSegment.from_file(BytesIO(audio_bytes))
|
18 |
+
audio_array = np.array(audio_segment.get_array_of_samples(), dtype=np.float32)
|
19 |
+
sample_rate = audio_segment.frame_rate
|
20 |
+
return audio_array, sample_rate
|
21 |
+
|
22 |
+
# Function for noise reduction
|
23 |
+
def reduce_noise(audio, sample_rate):
|
24 |
+
reduced_noise_audio = nr.reduce_noise(y=audio, sr=sample_rate)
|
25 |
+
return reduced_noise_audio
|
26 |
+
|
27 |
+
# Function for pitch shifting
|
28 |
+
def pitch_shift(audio, sample_rate, n_steps):
|
29 |
+
shifted_audio = librosa.effects.pitch_shift(audio, sr=sample_rate, n_steps=n_steps)
|
30 |
+
return shifted_audio
|
31 |
+
|
32 |
+
# Function for time-stretching
|
33 |
+
def time_stretch(audio, rate):
|
34 |
+
stretched_audio = librosa.effects.time_stretch(audio, rate)
|
35 |
+
return stretched_audio
|
36 |
+
|
37 |
+
# Function to save audio to a temporary file and return the path
|
38 |
+
def save_audio(audio, sample_rate):
|
39 |
+
# Create a temporary file to save the audio
|
40 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".wav")
|
41 |
+
temp_file_name = temp_file.name
|
42 |
|
43 |
+
# Use pydub to save the audio to the temp file
|
44 |
+
audio_segment = AudioSegment(
|
45 |
+
np.int16(audio).tobytes(), frame_rate=sample_rate, sample_width=2, channels=1
|
46 |
+
)
|
47 |
+
audio_segment.export(temp_file_name, format="wav")
|
48 |
|
49 |
+
temp_file.close()
|
50 |
+
return temp_file_name
|
51 |
+
|
52 |
+
# Main function to apply audio effects
|
53 |
+
def apply_effects(audio_file, noise_reduction, pitch_shift_steps, time_stretch_rate):
|
54 |
+
audio, sr = load_audio(audio_file)
|
55 |
|
56 |
+
if noise_reduction:
|
57 |
+
audio = reduce_noise(audio, sr)
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
if pitch_shift_steps != 0:
|
60 |
+
audio = pitch_shift(audio, sr, pitch_shift_steps)
|
|
|
61 |
|
62 |
+
if time_stretch_rate != 1.0:
|
63 |
+
audio = time_stretch(audio, time_stretch_rate)
|
64 |
|
65 |
+
return save_audio(audio, sr)
|
|
|
|
|
|
|
|
|
66 |
|
67 |
+
# Gradio UI
|
68 |
+
def build_ui():
|
69 |
+
with gr.Blocks() as demo:
|
70 |
+
# Custom Title
|
71 |
+
gr.Markdown("# Shyam's AI Audio Studio")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
|
73 |
+
# Description for the tool
|
74 |
+
gr.Markdown(
|
75 |
+
"""
|
76 |
+
Welcome to **Shyam's AI Audio Studio**!
|
77 |
+
This tool allows you to upload audio files and apply various effects like:
|
78 |
+
- Noise Reduction
|
79 |
+
- Pitch Shifting (up or down by semitones)
|
80 |
+
- Time Stretching (speed up or slow down)
|
81 |
+
|
82 |
+
Experiment with the sliders to fine-tune the effects and get your desired sound!
|
83 |
+
"""
|
84 |
+
)
|
85 |
|
86 |
+
# Input components
|
87 |
+
audio_input = gr.Audio(type="filepath", label="Upload Audio File")
|
88 |
+
noise_reduction = gr.Checkbox(label="Apply Noise Reduction", value=True)
|
89 |
+
pitch_shift_steps = gr.Slider(label="Pitch Shift (in semitones)", minimum=-12, maximum=12, value=0, step=1)
|
90 |
+
time_stretch_rate = gr.Slider(label="Time Stretch Rate", minimum=0.5, maximum=2.0, value=1.0, step=0.1)
|
91 |
|
92 |
+
# Output component
|
93 |
+
audio_output = gr.File(label="Download Edited Audio")
|
94 |
|
95 |
+
# Button to trigger the process
|
96 |
+
edit_button = gr.Button("Apply Effects")
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
|
98 |
+
# Link the button to the effect function
|
99 |
+
edit_button.click(
|
100 |
+
apply_effects,
|
101 |
+
inputs=[audio_input, noise_reduction, pitch_shift_steps, time_stretch_rate],
|
102 |
+
outputs=audio_output
|
103 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
+
return demo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
+
# Launch the Gradio app
|
108 |
+
if __name__ == "__main__":
|
109 |
+
ui = build_ui()
|
110 |
+
ui.launch()
|