Omar-youssef commited on
Commit
4d96a11
·
1 Parent(s): af5a49b

new version

Browse files
Files changed (4) hide show
  1. app.py +180 -43
  2. audio_processor.py +1 -0
  3. link_processor.py +47 -0
  4. video_processor.py +95 -12
app.py CHANGED
@@ -4,6 +4,14 @@ import os
4
  from pathlib import Path
5
  from video_processor import VideoProcessor
6
  from audio_processor import AudioProcessor
 
 
 
 
 
 
 
 
7
 
8
  # Utility functions
9
  def save_uploaded_file(uploaded_file, temp_dir):
@@ -12,124 +20,253 @@ def save_uploaded_file(uploaded_file, temp_dir):
12
  f.write(uploaded_file.read())
13
  return file_path
14
 
 
15
  def download_file_button(file_path, label, file_name, mime_type):
16
  with open(file_path, 'rb') as f:
17
- st.download_button(
18
- label=label,
19
- data=f.read(),
20
- file_name=file_name,
21
- mime=mime_type
22
- )
 
 
 
 
 
 
 
 
23
 
24
  def process_audio(audio_file):
25
  with tempfile.TemporaryDirectory() as temp_dir:
26
- # Save uploaded file
27
  input_path = save_uploaded_file(audio_file, temp_dir)
28
-
29
- # Create output directory
30
  output_dir = Path(temp_dir) / "output"
31
  output_dir.mkdir(exist_ok=True)
32
 
33
  with st.spinner("Processing audio..."):
34
  try:
35
  audio_proc = AudioProcessor(input_path, output_dir)
36
-
37
- # Run Demucs
38
  if not audio_proc.run_demucs():
39
  raise RuntimeError("Demucs processing failed.")
40
 
41
- # Retrieve processed files
42
  vocals_path = audio_proc.get_vocals_path()
43
  no_vocals_path = audio_proc.get_no_vocals_path()
44
 
45
  if not vocals_path or not no_vocals_path:
46
  raise FileNotFoundError("Processed files not found.")
47
 
48
- # Download buttons
49
  col1, col2 = st.columns(2)
50
- download_file_button(vocals_path, "Download Vocals", f"vocals_{audio_file.name.rsplit('.', 1)[0]}.wav", "audio/wav")
51
- # download_file_button(no_vocals_path, "Download Instrumental", f"instrumental_{audio_file.name.rsplit('.', 1)[0]}.wav", "audio/wav")
 
 
 
 
 
52
 
53
  st.success("Audio processing completed!")
 
54
 
55
  except Exception as e:
56
  st.error(f"An error occurred: {str(e)}")
 
 
57
 
58
  def process_video(video_file):
59
  with tempfile.TemporaryDirectory() as temp_dir:
60
- # Save uploaded file
61
  input_path = save_uploaded_file(video_file, temp_dir)
62
-
63
- # Create output directory
64
  output_dir = Path(temp_dir) / "output"
65
  output_dir.mkdir(exist_ok=True)
66
 
67
  with st.spinner("Processing video..."):
68
  try:
69
- # Extract audio from video
70
  video_proc = VideoProcessor(input_path, output_dir)
71
  audio_path = video_proc.extract_audio()
72
  if not audio_path:
73
  raise RuntimeError("Audio extraction from video failed.")
74
 
75
- # Process extracted audio
76
  audio_proc = AudioProcessor(audio_path, output_dir)
77
  if not audio_proc.run_demucs():
78
  raise RuntimeError("Demucs processing failed.")
79
 
80
- # Retrieve processed audio files
81
  vocals_path = audio_proc.get_vocals_path()
82
  no_vocals_path = audio_proc.get_no_vocals_path()
83
  if not vocals_path or not no_vocals_path:
84
  raise FileNotFoundError("Processed audio files not found.")
85
 
86
- # Combine video with vocals
87
  final_video_path = video_proc.combine_video_audio(vocals_path)
88
 
89
- # Download buttons
90
- col1, col2, col3 = st.columns(3)
91
  if final_video_path:
92
- download_file_button(final_video_path, "Download Processed Video", f"processed_{video_file.name}", "video/mp4")
93
- download_file_button(vocals_path, "Download Vocals", f"vocals_{video_file.name.rsplit('.', 1)[0]}.wav", "audio/wav")
94
- #download_file_button(no_vocals_path, "Download Instrumental", f"instrumental_{video_file.name.rsplit('.', 1)[0]}.wav", "audio/wav")
 
 
 
 
 
 
 
 
 
 
 
95
 
96
  st.success("Video processing completed!")
 
97
 
98
  except Exception as e:
99
  st.error(f"An error occurred: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- # Streamlit App Layout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  st.title("🎵 Audio/Video Music Separator")
103
 
104
  # Tabs for different processing types
105
- tab1, tab2 = st.tabs(["Audio Processing 🎧", "Video Processing 🎥"])
106
 
107
  with tab1:
108
- st.header("Process Your Audio File")
109
  with st.expander("Instructions", expanded=True):
110
  st.markdown("""
111
- 1. Upload an audio file in MP3, WAV, or OGG format.
112
- 2. Click the **Process Audio** button.
113
- 3. Download the separated vocals.
114
  """)
115
- audio_file = st.file_uploader("Upload your audio file", type=['mp3', 'wav', 'ogg'])
116
- if audio_file and st.button("Process Audio"):
117
- process_audio(audio_file)
 
 
118
 
119
  with tab2:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  st.header("Process Your Video File")
121
  with st.expander("Instructions", expanded=True):
122
  st.markdown("""
123
- 1. Upload a video file in MP4, MKV, or AVI format.
124
- 2. Click the **Process Video** button.
125
- 3. Download the processed video or vocals.
126
  """)
127
- video_file = st.file_uploader("Upload your video file", type=['mp4', 'mkv', 'avi'])
128
- if video_file and st.button("Process Video"):
129
- process_video(video_file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
  # Footer
132
  st.markdown("""
133
  ---
134
  Made with ❤️ using [Streamlit](https://streamlit.io)
135
- """)
 
4
  from pathlib import Path
5
  from video_processor import VideoProcessor
6
  from audio_processor import AudioProcessor
7
+ from link_processor import LinkDownloader
8
+
9
+ # Initialize session state for tracking processing status
10
+ if 'processing_complete' not in st.session_state:
11
+ st.session_state.processing_complete = False
12
+ if 'current_tab' not in st.session_state:
13
+ st.session_state.current_tab = "URL Processing 🔗"
14
+
15
 
16
  # Utility functions
17
  def save_uploaded_file(uploaded_file, temp_dir):
 
20
  f.write(uploaded_file.read())
21
  return file_path
22
 
23
+
24
  def download_file_button(file_path, label, file_name, mime_type):
25
  with open(file_path, 'rb') as f:
26
+ if st.download_button(
27
+ label=label,
28
+ data=f.read(),
29
+ file_name=file_name,
30
+ mime=mime_type
31
+ ):
32
+ # Reset the processing state after download
33
+ st.session_state.processing_complete = False
34
+ st.rerun()
35
+
36
+
37
+ def reset_processing_state():
38
+ st.session_state.processing_complete = False
39
+
40
 
41
  def process_audio(audio_file):
42
  with tempfile.TemporaryDirectory() as temp_dir:
 
43
  input_path = save_uploaded_file(audio_file, temp_dir)
 
 
44
  output_dir = Path(temp_dir) / "output"
45
  output_dir.mkdir(exist_ok=True)
46
 
47
  with st.spinner("Processing audio..."):
48
  try:
49
  audio_proc = AudioProcessor(input_path, output_dir)
 
 
50
  if not audio_proc.run_demucs():
51
  raise RuntimeError("Demucs processing failed.")
52
 
 
53
  vocals_path = audio_proc.get_vocals_path()
54
  no_vocals_path = audio_proc.get_no_vocals_path()
55
 
56
  if not vocals_path or not no_vocals_path:
57
  raise FileNotFoundError("Processed files not found.")
58
 
 
59
  col1, col2 = st.columns(2)
60
+ with col1:
61
+ download_file_button(
62
+ vocals_path,
63
+ "Download Vocals",
64
+ f"vocals_{audio_file.name.rsplit('.', 1)[0]}.wav",
65
+ "audio/wav"
66
+ )
67
 
68
  st.success("Audio processing completed!")
69
+ st.session_state.processing_complete = True
70
 
71
  except Exception as e:
72
  st.error(f"An error occurred: {str(e)}")
73
+ reset_processing_state()
74
+
75
 
76
  def process_video(video_file):
77
  with tempfile.TemporaryDirectory() as temp_dir:
 
78
  input_path = save_uploaded_file(video_file, temp_dir)
 
 
79
  output_dir = Path(temp_dir) / "output"
80
  output_dir.mkdir(exist_ok=True)
81
 
82
  with st.spinner("Processing video..."):
83
  try:
 
84
  video_proc = VideoProcessor(input_path, output_dir)
85
  audio_path = video_proc.extract_audio()
86
  if not audio_path:
87
  raise RuntimeError("Audio extraction from video failed.")
88
 
 
89
  audio_proc = AudioProcessor(audio_path, output_dir)
90
  if not audio_proc.run_demucs():
91
  raise RuntimeError("Demucs processing failed.")
92
 
 
93
  vocals_path = audio_proc.get_vocals_path()
94
  no_vocals_path = audio_proc.get_no_vocals_path()
95
  if not vocals_path or not no_vocals_path:
96
  raise FileNotFoundError("Processed audio files not found.")
97
 
 
98
  final_video_path = video_proc.combine_video_audio(vocals_path)
99
 
100
+ col1, col2 = st.columns(2)
 
101
  if final_video_path:
102
+ with col1:
103
+ download_file_button(
104
+ final_video_path,
105
+ "Download Processed Video",
106
+ f"processed_{video_file.name}",
107
+ "video/mp4"
108
+ )
109
+ with col2:
110
+ download_file_button(
111
+ vocals_path,
112
+ "Download Vocals",
113
+ f"vocals_{video_file.name.rsplit('.', 1)[0]}.wav",
114
+ "audio/wav"
115
+ )
116
 
117
  st.success("Video processing completed!")
118
+ st.session_state.processing_complete = True
119
 
120
  except Exception as e:
121
  st.error(f"An error occurred: {str(e)}")
122
+ reset_processing_state()
123
+
124
+
125
+ def process_url(url):
126
+ with tempfile.TemporaryDirectory() as temp_dir:
127
+ try:
128
+ output_dir = Path(temp_dir) / "downloaded"
129
+ output_dir.mkdir(exist_ok=True)
130
+
131
+ with st.spinner("Downloading video..."):
132
+ downloader = LinkDownloader(output_dir)
133
+ video_path = downloader.download_from_url(url)
134
+
135
+ if not video_path:
136
+ raise RuntimeError("Video download failed")
137
+
138
+ st.info("Processing video...")
139
+ process_dir = Path(temp_dir) / "output"
140
+ process_dir.mkdir(exist_ok=True)
141
+
142
+ video_proc = VideoProcessor(video_path, process_dir)
143
+ audio_path = video_proc.extract_audio()
144
+ if not audio_path:
145
+ raise RuntimeError("Audio extraction failed")
146
+
147
+ st.info("Separating vocals...")
148
+ audio_proc = AudioProcessor(audio_path, process_dir)
149
+ if not audio_proc.run_demucs():
150
+ raise RuntimeError("Demucs processing failed")
151
+
152
+ vocals_path = audio_proc.get_vocals_path()
153
+ no_vocals_path = audio_proc.get_no_vocals_path()
154
+
155
+ if not vocals_path or not no_vocals_path:
156
+ raise FileNotFoundError("Processed audio files not found")
157
 
158
+ st.info("Creating final video...")
159
+ final_video_path = video_proc.combine_video_audio(vocals_path)
160
+
161
+ if not final_video_path:
162
+ raise RuntimeError("Final video creation failed")
163
+
164
+ col1, col2 = st.columns(2)
165
+ if final_video_path:
166
+ with col1:
167
+ download_file_button(
168
+ final_video_path,
169
+ "Download Processed Video",
170
+ "processed_video.mp4",
171
+ "video/mp4"
172
+ )
173
+
174
+ if vocals_path:
175
+ with col2:
176
+ download_file_button(
177
+ vocals_path,
178
+ "Download Vocals",
179
+ "vocals.wav",
180
+ "audio/wav"
181
+ )
182
+
183
+ st.success("Video processing completed!")
184
+ st.session_state.processing_complete = True
185
+
186
+ except Exception as e:
187
+ st.error(f"An error occurred: {str(e)}")
188
+ reset_processing_state()
189
+ raise
190
+
191
+
192
+ # Page config
193
+ st.set_page_config(
194
+ page_title="🎵 Audio/Video Music Separator",
195
+ page_icon="🎵",
196
+ layout="wide"
197
+ )
198
+
199
+ # Main App Layout
200
  st.title("🎵 Audio/Video Music Separator")
201
 
202
  # Tabs for different processing types
203
+ tab1, tab2, tab3 = st.tabs(["URL Processing 🔗", "Audio Processing 🎧", "Video Processing 🎥"])
204
 
205
  with tab1:
206
+ st.header("Process Video from URL")
207
  with st.expander("Instructions", expanded=True):
208
  st.markdown("""
209
+ 1. Paste a video URL from YouTube, Facebook, or other supported platforms
210
+ 2. Click the **Process URL** button
211
+ 3. Download the processed video or vocals
212
  """)
213
+
214
+ if not st.session_state.processing_complete:
215
+ url = st.text_input("Enter video URL")
216
+ if url and st.button("Process URL"):
217
+ process_url(url)
218
 
219
  with tab2:
220
+ st.header("Process Your Audio File")
221
+ with st.expander("Instructions", expanded=True):
222
+ st.markdown("""
223
+ 1. Upload an audio file in MP3, WAV, or OGG format
224
+ 2. Click the **Process Audio** button
225
+ 3. Download the separated vocals
226
+ """)
227
+
228
+ if not st.session_state.processing_complete:
229
+ audio_file = st.file_uploader("Upload your audio file", type=['mp3', 'wav', 'ogg'])
230
+ if audio_file and st.button("Process Audio"):
231
+ process_audio(audio_file)
232
+
233
+ with tab3:
234
  st.header("Process Your Video File")
235
  with st.expander("Instructions", expanded=True):
236
  st.markdown("""
237
+ 1. Upload a video file in MP4, MKV, or AVI format
238
+ 2. Click the **Process Video** button
239
+ 3. Download the processed video or vocals
240
  """)
241
+
242
+ if not st.session_state.processing_complete:
243
+ video_file = st.file_uploader("Upload your video file", type=['mp4', 'mkv', 'avi'])
244
+ if video_file and st.button("Process Video"):
245
+ process_video(video_file)
246
+
247
+ # Add a reset button when processing is complete
248
+ if st.session_state.processing_complete:
249
+ if st.button("Process Another File"):
250
+ reset_processing_state()
251
+ st.rerun()
252
+
253
+ # Sidebar
254
+ with st.sidebar:
255
+ st.header("About")
256
+ st.markdown("""
257
+ This app helps you separate vocals from music in your audio and video files.
258
+
259
+ ### Features:
260
+ - Process videos from URLs
261
+ - Upload and process audio files
262
+ - Upload and process video files
263
+ - Extract vocals
264
+ - Download processed files
265
+ - Click process another file
266
+ """)
267
 
268
  # Footer
269
  st.markdown("""
270
  ---
271
  Made with ❤️ using [Streamlit](https://streamlit.io)
272
+ """)
audio_processor.py CHANGED
@@ -14,6 +14,7 @@ class AudioProcessor:
14
  subprocess.run([
15
  "demucs",
16
  "--two-stems=vocals",
 
17
  "-o", str(self.output_dir),
18
  str(self.input_audio)
19
  ], check=True)
 
14
  subprocess.run([
15
  "demucs",
16
  "--two-stems=vocals",
17
+ # "-n", "mdx_extra_q",
18
  "-o", str(self.output_dir),
19
  str(self.input_audio)
20
  ], check=True)
link_processor.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import yt_dlp
3
+ import os
4
+ from concurrent.futures import ThreadPoolExecutor
5
+ import streamlit as st
6
+
7
+
8
+ class LinkDownloader:
9
+ def __init__(self, output_dir):
10
+ self.output_dir = Path(output_dir)
11
+ self.output_dir.mkdir(parents=True, exist_ok=True)
12
+ self.executor = ThreadPoolExecutor(max_workers=2)
13
+
14
+ def download_from_url(self, url):
15
+ try:
16
+ temp_filepath = str(self.output_dir / 'downloaded_video.%(ext)s')
17
+
18
+ ydl_opts = {
19
+ 'format': 'best[height<=1080]',
20
+ 'outtmpl': temp_filepath,
21
+ 'quiet': True,
22
+ 'no_warnings': True,
23
+ 'extract_audio': False,
24
+ 'concurrent_fragment_downloads': 3,
25
+ 'file_access_retries': 3,
26
+ 'retries': 3,
27
+ 'socket_timeout': 30,
28
+ 'nocheckcertificate': True,
29
+ }
30
+
31
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
32
+ info = ydl.extract_info(url, download=False)
33
+ ext = info.get('ext', 'mp4')
34
+
35
+ # Download in a separate thread
36
+ future = self.executor.submit(ydl.download, [url])
37
+ future.result()
38
+
39
+ actual_filepath = str(self.output_dir / f"downloaded_video.{ext}")
40
+ return actual_filepath if os.path.exists(actual_filepath) else None
41
+
42
+ except Exception as e:
43
+ print(f"Download error: {e}")
44
+ return None
45
+
46
+ def __del__(self):
47
+ self.executor.shutdown(wait=False)
video_processor.py CHANGED
@@ -1,5 +1,7 @@
1
  from pathlib import Path
2
  import subprocess
 
 
3
 
4
  class VideoProcessor:
5
  def __init__(self, input_video, output_dir):
@@ -12,31 +14,112 @@ class VideoProcessor:
12
  def extract_audio(self):
13
  """Extract audio from video file"""
14
  try:
15
- subprocess.run([
16
- "ffmpeg", "-i", str(self.input_video),
17
- "-vn", "-acodec", "pcm_s16le", "-ar", "44100",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  str(self.temp_audio)
19
- ], check=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  print(f"Audio extracted successfully to {self.temp_audio}")
21
  return str(self.temp_audio)
22
- except subprocess.CalledProcessError as e:
23
- print(f"Error extracting audio: {e}")
 
 
24
  return None
25
 
26
  def combine_video_audio(self, vocals_path):
27
  """Combine original video with vocals only"""
28
  try:
29
- subprocess.run([
 
 
 
 
 
 
 
 
 
30
  "ffmpeg",
 
31
  "-i", str(self.input_video),
32
  "-i", vocals_path,
33
- "-c:v", "copy",
34
- "-c:a", "aac",
 
35
  "-map", "0:v:0",
36
  "-map", "1:a:0",
 
37
  str(self.final_video)
38
- ], check=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  return str(self.final_video)
40
- except subprocess.CalledProcessError as e:
41
- print(f"Error combining video and audio: {e}")
 
 
42
  return None
 
1
  from pathlib import Path
2
  import subprocess
3
+ import os
4
+ import streamlit as st
5
 
6
  class VideoProcessor:
7
  def __init__(self, input_video, output_dir):
 
14
  def extract_audio(self):
15
  """Extract audio from video file"""
16
  try:
17
+ # First, verify the input video file exists and is readable
18
+ if not self.input_video.exists():
19
+ st.error(f"Input video file not found: {self.input_video}")
20
+ return None
21
+
22
+ # Print file size and check if it's not empty
23
+ file_size = os.path.getsize(self.input_video)
24
+ print(f"Input video file size: {file_size} bytes")
25
+ if file_size == 0:
26
+ st.error("Input video file is empty")
27
+ return None
28
+
29
+ # Construct FFmpeg command with detailed logging
30
+ command = [
31
+ "ffmpeg",
32
+ "-v", "verbose", # Enable verbose logging
33
+ "-i", str(self.input_video),
34
+ "-vn", # No video
35
+ "-acodec", "pcm_s16le",
36
+ "-ar", "44100",
37
+ "-y", # Overwrite output file if it exists
38
  str(self.temp_audio)
39
+ ]
40
+
41
+ # Run FFmpeg command and capture output
42
+ process = subprocess.run(
43
+ command,
44
+ capture_output=True,
45
+ text=True,
46
+ check=False # Don't raise exception immediately
47
+ )
48
+
49
+ # Print FFmpeg output for debugging
50
+ print("FFmpeg stdout:", process.stdout)
51
+ print("FFmpeg stderr:", process.stderr)
52
+
53
+ # Check if the command was successful
54
+ if process.returncode != 0:
55
+ st.error(f"FFmpeg error: {process.stderr}")
56
+ return None
57
+
58
+ # Verify the output audio file exists and is not empty
59
+ if not self.temp_audio.exists() or os.path.getsize(self.temp_audio) == 0:
60
+ st.error("Audio extraction failed: Output file is missing or empty")
61
+ return None
62
+
63
  print(f"Audio extracted successfully to {self.temp_audio}")
64
  return str(self.temp_audio)
65
+
66
+ except Exception as e:
67
+ st.error(f"Error during audio extraction: {str(e)}")
68
+ print(f"Exception details: {str(e)}")
69
  return None
70
 
71
  def combine_video_audio(self, vocals_path):
72
  """Combine original video with vocals only"""
73
  try:
74
+ # Verify input files exist
75
+ if not self.input_video.exists():
76
+ st.error(f"Original video not found: {self.input_video}")
77
+ return None
78
+ if not Path(vocals_path).exists():
79
+ st.error(f"Vocals audio not found: {vocals_path}")
80
+ return None
81
+
82
+ # Construct FFmpeg command
83
+ command = [
84
  "ffmpeg",
85
+ "-v", "verbose", # Enable verbose logging
86
  "-i", str(self.input_video),
87
  "-i", vocals_path,
88
+ "-c:v", "copy", # Copy video stream without re-encoding
89
+ "-c:a", "aac", # Use AAC codec for audio
90
+ "-b:a", "192k", # Set audio bitrate
91
  "-map", "0:v:0",
92
  "-map", "1:a:0",
93
+ "-y", # Overwrite output file if it exists
94
  str(self.final_video)
95
+ ]
96
+
97
+ # Run FFmpeg command and capture output
98
+ process = subprocess.run(
99
+ command,
100
+ capture_output=True,
101
+ text=True,
102
+ check=False
103
+ )
104
+
105
+ # Print FFmpeg output for debugging
106
+ print("FFmpeg stdout:", process.stdout)
107
+ print("FFmpeg stderr:", process.stderr)
108
+
109
+ # Check if the command was successful
110
+ if process.returncode != 0:
111
+ st.error(f"FFmpeg error during video combination: {process.stderr}")
112
+ return None
113
+
114
+ # Verify the output video file exists and is not empty
115
+ if not self.final_video.exists() or os.path.getsize(self.final_video) == 0:
116
+ st.error("Video combination failed: Output file is missing or empty")
117
+ return None
118
+
119
+ print(f"Video combined successfully: {self.final_video}")
120
  return str(self.final_video)
121
+
122
+ except Exception as e:
123
+ st.error(f"Error during video combination: {str(e)}")
124
+ print(f"Exception details: {str(e)}")
125
  return None