Spaces:
Running
Running
Update templates/index.html
Browse files- templates/index.html +127 -48
templates/index.html
CHANGED
@@ -110,39 +110,62 @@
|
|
110 |
</header>
|
111 |
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
|
112 |
<div class="glass-morphism col-span-1 md:col-span-1 rounded-xl shadow-xl p-8 mb-8 h-fit">
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
<
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
<p class="text-sm text-gray-500" id="fileSize"></p>
|
135 |
-
</div>
|
136 |
-
<button id="removeFileBtn"
|
137 |
-
class="text-gray-400 hover:text-red-500 transition-colors duration-300">
|
138 |
-
<i data-feather="x-circle"></i>
|
139 |
</button>
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
|
147 |
<button id="convertBtn"
|
148 |
class="w-full bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-semibold py-3 px-4 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition duration-300 flex items-center justify-center pulse"
|
@@ -284,30 +307,50 @@
|
|
284 |
|
285 |
// Handle convert button click
|
286 |
convertBtn.addEventListener('click', async () => {
|
287 |
-
const
|
288 |
-
|
289 |
-
|
290 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
}
|
292 |
-
|
293 |
// Show loading indicator
|
294 |
loadingIndicator.classList.remove('hidden');
|
295 |
convertBtn.disabled = true;
|
296 |
convertBtn.classList.remove('pulse');
|
297 |
-
|
298 |
-
const formData = new FormData();
|
299 |
-
formData.append('file', file);
|
300 |
-
|
301 |
try {
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
307 |
const result = await response.json();
|
308 |
-
|
309 |
if (response.ok) {
|
310 |
-
lyricsOutput.srcdoc = result.lyrics;
|
311 |
downloadBtn.classList.remove('hidden');
|
312 |
} else {
|
313 |
lyricsOutput.srcdoc = `Error: ${result.error}`;
|
@@ -315,7 +358,6 @@
|
|
315 |
} catch (error) {
|
316 |
lyricsOutput.srcdoc = `Error: ${error.message}`;
|
317 |
} finally {
|
318 |
-
// Hide loading indicator
|
319 |
loadingIndicator.classList.add('hidden');
|
320 |
convertBtn.disabled = false;
|
321 |
}
|
@@ -335,6 +377,43 @@
|
|
335 |
document.body.removeChild(a);
|
336 |
URL.revokeObjectURL(url);
|
337 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
</script>
|
339 |
</body>
|
340 |
|
|
|
110 |
</header>
|
111 |
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
|
112 |
<div class="glass-morphism col-span-1 md:col-span-1 rounded-xl shadow-xl p-8 mb-8 h-fit">
|
113 |
+
<!-- Add tab buttons -->
|
114 |
+
<div class="flex mb-6 bg-gray-100 rounded-lg p-1">
|
115 |
+
<button id="fileTabBtn" class="flex-1 py-2 px-4 rounded-md bg-white shadow-sm text-blue-600 font-medium">
|
116 |
+
<i data-feather="file" class="inline mr-2"></i>File
|
117 |
+
</button>
|
118 |
+
<button id="youtubeTabBtn" class="flex-1 py-2 px-4 rounded-md text-gray-600 font-medium">
|
119 |
+
<i data-feather="youtube" class="inline mr-2"></i>YouTube
|
120 |
+
</button>
|
121 |
+
</div>
|
122 |
+
|
123 |
+
<!-- File upload section -->
|
124 |
+
<div id="fileUploadSection">
|
125 |
+
<div class="dropzone p-8 mb-6 flex flex-col items-center justify-center" id="dropzone">
|
126 |
+
<i data-feather="music" class="text-blue-500 mb-4" style="width: 48px; height: 48px;"></i>
|
127 |
+
<label class="block text-gray-700 font-medium mb-2">Upload Audio File</label>
|
128 |
+
<p class="text-sm text-gray-500 mb-4">Drag & drop your file here or click to browse</p>
|
129 |
+
<input type="file" id="audioFile" accept="audio/*" class="hidden">
|
130 |
+
<button id="browseBtn"
|
131 |
+
class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-6 rounded-full transition duration-300 flex items-center">
|
132 |
+
<i data-feather="upload-cloud" class="mr-2"></i>
|
133 |
+
Browse Files
|
|
|
|
|
|
|
|
|
|
|
134 |
</button>
|
135 |
+
</div>
|
136 |
+
<div class="mb-6">
|
137 |
+
<div class="bg-gray-50 rounded-lg p-4 flex items-center" id="fileInfoContainer"
|
138 |
+
style="display: none;">
|
139 |
+
<div
|
140 |
+
class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mr-3 flex-shrink-0">
|
141 |
+
<i data-feather="file" class="text-blue-600"></i>
|
142 |
+
</div>
|
143 |
+
<div class="flex-grow">
|
144 |
+
<p class="font-medium text-gray-800" id="fileName">No file selected</p>
|
145 |
+
<p class="text-sm text-gray-500" id="fileSize"></p>
|
146 |
+
</div>
|
147 |
+
<button id="removeFileBtn"
|
148 |
+
class="text-gray-400 hover:text-red-500 transition-colors duration-300">
|
149 |
+
<i data-feather="x-circle"></i>
|
150 |
+
</button>
|
151 |
+
</div>
|
152 |
|
153 |
+
<audio id="audioPlayer" controls class="w-full mt-4 rounded-lg" hidden>
|
154 |
+
Your browser does not support the audio element.
|
155 |
+
</audio>
|
156 |
+
</div>
|
157 |
+
</div>
|
158 |
+
|
159 |
+
<!-- YouTube link section -->
|
160 |
+
<div id="youtubeLinkSection" class="hidden">
|
161 |
+
<div class="mb-6">
|
162 |
+
<label class="block text-gray-700 font-medium mb-2">YouTube URL</label>
|
163 |
+
<input type="text" id="youtubeUrl"
|
164 |
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
165 |
+
placeholder="https://www.youtube.com/watch?v=...">
|
166 |
+
<p id="youtubeError" class="text-red-500 text-sm mt-1 hidden">Please enter a valid YouTube URL</p>
|
167 |
+
</div>
|
168 |
+
</div>
|
169 |
|
170 |
<button id="convertBtn"
|
171 |
class="w-full bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-semibold py-3 px-4 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition duration-300 flex items-center justify-center pulse"
|
|
|
307 |
|
308 |
// Handle convert button click
|
309 |
convertBtn.addEventListener('click', async () => {
|
310 |
+
const isYoutubeMode = !youtubeLinkSection.classList.contains('hidden');
|
311 |
+
|
312 |
+
if (isYoutubeMode) {
|
313 |
+
const youtubeLink = youtubeUrl.value;
|
314 |
+
if (!isValidYouTubeUrl(youtubeLink)) {
|
315 |
+
alert('Please enter a valid YouTube URL');
|
316 |
+
return;
|
317 |
+
}
|
318 |
+
} else {
|
319 |
+
const file = audioFile.files[0];
|
320 |
+
if (!file) {
|
321 |
+
alert('Please select an audio file first');
|
322 |
+
return;
|
323 |
+
}
|
324 |
}
|
325 |
+
|
326 |
// Show loading indicator
|
327 |
loadingIndicator.classList.remove('hidden');
|
328 |
convertBtn.disabled = true;
|
329 |
convertBtn.classList.remove('pulse');
|
330 |
+
|
|
|
|
|
|
|
331 |
try {
|
332 |
+
let response;
|
333 |
+
if (isYoutubeMode) {
|
334 |
+
response = await fetch('/convert-youtube', {
|
335 |
+
method: 'POST',
|
336 |
+
headers: {
|
337 |
+
'Content-Type': 'application/json',
|
338 |
+
},
|
339 |
+
body: JSON.stringify({ url: youtubeUrl.value })
|
340 |
+
});
|
341 |
+
} else {
|
342 |
+
const formData = new FormData();
|
343 |
+
formData.append('file', audioFile.files[0]);
|
344 |
+
response = await fetch('/convert', {
|
345 |
+
method: 'POST',
|
346 |
+
body: formData
|
347 |
+
});
|
348 |
+
}
|
349 |
+
|
350 |
const result = await response.json();
|
351 |
+
|
352 |
if (response.ok) {
|
353 |
+
lyricsOutput.srcdoc = result.lyrics;
|
354 |
downloadBtn.classList.remove('hidden');
|
355 |
} else {
|
356 |
lyricsOutput.srcdoc = `Error: ${result.error}`;
|
|
|
358 |
} catch (error) {
|
359 |
lyricsOutput.srcdoc = `Error: ${error.message}`;
|
360 |
} finally {
|
|
|
361 |
loadingIndicator.classList.add('hidden');
|
362 |
convertBtn.disabled = false;
|
363 |
}
|
|
|
377 |
document.body.removeChild(a);
|
378 |
URL.revokeObjectURL(url);
|
379 |
});
|
380 |
+
|
381 |
+
// Add new JavaScript for YouTube functionality
|
382 |
+
const fileTabBtn = document.getElementById('fileTabBtn');
|
383 |
+
const youtubeTabBtn = document.getElementById('youtubeTabBtn');
|
384 |
+
const fileUploadSection = document.getElementById('fileUploadSection');
|
385 |
+
const youtubeLinkSection = document.getElementById('youtubeLinkSection');
|
386 |
+
const youtubeUrl = document.getElementById('youtubeUrl');
|
387 |
+
const youtubeError = document.getElementById('youtubeError');
|
388 |
+
|
389 |
+
fileTabBtn.addEventListener('click', () => {
|
390 |
+
fileTabBtn.classList.add('bg-white', 'shadow-sm', 'text-blue-600');
|
391 |
+
youtubeTabBtn.classList.remove('bg-white', 'shadow-sm', 'text-blue-600');
|
392 |
+
fileUploadSection.classList.remove('hidden');
|
393 |
+
youtubeLinkSection.classList.add('hidden');
|
394 |
+
convertBtn.disabled = !audioFile.files.length;
|
395 |
+
});
|
396 |
+
|
397 |
+
youtubeTabBtn.addEventListener('click', () => {
|
398 |
+
youtubeTabBtn.classList.add('bg-white', 'shadow-sm', 'text-blue-600');
|
399 |
+
fileTabBtn.classList.remove('bg-white', 'shadow-sm', 'text-blue-600');
|
400 |
+
youtubeLinkSection.classList.remove('hidden');
|
401 |
+
fileUploadSection.classList.add('hidden');
|
402 |
+
convertBtn.disabled = !isValidYouTubeUrl(youtubeUrl.value);
|
403 |
+
});
|
404 |
+
|
405 |
+
function isValidYouTubeUrl(url) {
|
406 |
+
const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/;
|
407 |
+
return youtubeRegex.test(url);
|
408 |
+
}
|
409 |
+
|
410 |
+
youtubeUrl.addEventListener('input', () => {
|
411 |
+
const isValid = isValidYouTubeUrl(youtubeUrl.value);
|
412 |
+
youtubeError.classList.toggle('hidden', isValid);
|
413 |
+
convertBtn.disabled = !isValid;
|
414 |
+
if (isValid) convertBtn.classList.add('pulse');
|
415 |
+
else convertBtn.classList.remove('pulse');
|
416 |
+
});
|
417 |
</script>
|
418 |
</body>
|
419 |
|