anycoder-c88349ef / index.html
Mudrock10's picture
Upload folder using huggingface_hub
92d0b01 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Audio Similarity Matcher</title>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #4a6bff;
--secondary-color: #f5f7ff;
--accent-color: #ff6b6b;
--text-color: #2c3e50;
--light-text: #7f8c8d;
--success-color: #2ecc71;
--warning-color: #f39c12;
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--border-radius: 12px;
--transition: all 0.3s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
padding: 20px;
color: var(--text-color);
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
}
.logo {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin-bottom: 10px;
}
.logo i {
font-size: 2rem;
color: var(--primary-color);
}
h1 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 10px;
background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.subtitle {
color: var(--light-text);
font-size: 1.1rem;
}
.built-with {
position: absolute;
top: 20px;
right: 20px;
font-size: 0.9rem;
color: var(--light-text);
}
.built-with a {
color: var(--primary-color);
text-decoration: none;
font-weight: 600;
}
.card {
background: white;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
padding: 30px;
margin-bottom: 20px;
transition: var(--transition);
}
.card:hover {
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.input-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
@media (max-width: 768px) {
.input-section {
grid-template-columns: 1fr;
}
}
.input-group {
display: flex;
flex-direction: column;
gap: 10px;
}
label {
font-weight: 600;
color: var(--text-color);
font-size: 0.95rem;
}
.file-upload {
border: 2px dashed var(--primary-color);
border-radius: var(--border-radius);
padding: 20px;
text-align: center;
cursor: pointer;
transition: var(--transition);
background-color: var(--secondary-color);
}
.file-upload:hover {
background-color: rgba(74, 107, 255, 0.1);
border-color: var(--accent-color);
}
.file-upload i {
font-size: 2rem;
color: var(--primary-color);
margin-bottom: 10px;
}
.file-upload p {
color: var(--light-text);
font-size: 0.9rem;
}
input[type="file"] {
display: none;
}
.url-input {
display: flex;
gap: 10px;
}
input[type="text"] {
flex: 1;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: var(--border-radius);
font-size: 1rem;
transition: var(--transition);
}
input[type="text"]:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(74, 107, 255, 0.1);
}
.btn {
padding: 12px 25px;
border: none;
border-radius: var(--border-radius);
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
background-color: #3a5bef;
transform: translateY(-2px);
}
.btn-secondary {
background-color: var(--secondary-color);
color: var(--primary-color);
border: 1px solid var(--primary-color);
}
.btn-secondary:hover {
background-color: rgba(74, 107, 255, 0.1);
}
.btn-add {
background-color: var(--success-color);
color: white;
}
.btn-add:hover {
background-color: #27ae60;
}
.btn-remove {
background-color: var(--accent-color);
color: white;
}
.btn-remove:hover {
background-color: #e74c3c;
}
.audio-preview {
display: flex;
align-items: center;
gap: 15px;
margin-top: 15px;
padding: 10px;
background-color: var(--secondary-color);
border-radius: var(--border-radius);
}
.audio-info {
flex: 1;
}
.audio-name {
font-weight: 600;
margin-bottom: 5px;
color: var(--text-color);
}
.audio-duration {
font-size: 0.85rem;
color: var(--light-text);
}
audio {
width: 100%;
margin-top: 10px;
}
.progress-container {
margin: 20px 0;
}
.progress-bar {
height: 6px;
background-color: #ecf0f1;
border-radius: 3px;
overflow: hidden;
}
.progress {
height: 100%;
background-color: var(--primary-color);
width: 0%;
transition: width 0.3s ease;
}
.status {
text-align: center;
margin-top: 10px;
font-size: 0.9rem;
color: var(--light-text);
}
.results {
display: none;
margin-top: 30px;
}
.results-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.results-title {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-color);
}
.match-percentage {
font-size: 1.2rem;
font-weight: 600;
color: var(--success-color);
}
.timestamps {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 15px;
}
.timestamp-card {
background-color: var(--secondary-color);
border-radius: var(--border-radius);
padding: 15px;
transition: var(--transition);
}
.timestamp-card:hover {
transform: translateY(-3px);
box-shadow: var(--shadow);
}
.timestamp-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.timestamp-time {
font-weight: 600;
color: var(--primary-color);
font-size: 1.1rem;
}
.similarity-score {
background-color: var(--success-color);
color: white;
padding: 3px 8px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
}
.timestamp-details {
font-size: 0.9rem;
color: var(--light-text);
margin-bottom: 10px;
}
.waveform-container {
height: 80px;
background-color: white;
border-radius: 8px;
overflow: hidden;
}
.waveform {
width: 100%;
height: 100%;
}
.no-results {
text-align: center;
padding: 40px;
color: var(--light-text);
}
.no-results i {
font-size: 3rem;
margin-bottom: 15px;
color: var(--primary-color);
opacity: 0.5;
}
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.9);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-content {
text-align: center;
}
.spinner {
width: 60px;
height: 60px;
border: 4px solid rgba(74, 107, 255, 0.2);
border-radius: 50%;
border-top-color: var(--primary-color);
animation: spin 1s ease-in-out infinite;
margin: 0 auto 20px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
font-size: 1.2rem;
font-weight: 600;
color: var(--text-color);
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 15px;
border-radius: var(--border-radius);
margin: 15px 0;
display: none;
}
.error-message.show {
display: block;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 8px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
font-size: 0.8rem;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
</style>
</head>
<body>
<div class="built-with">
Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
</div>
<div class="container">
<header>
<div class="logo">
<i class="fas fa-wave-square"></i>
<h1>Audio Similarity Matcher</h1>
</div>
<p class="subtitle">Find exact timestamps where audio files match</p>
</header>
<div class="card">
<div class="input-section">
<div class="input-group">
<label for="audioUpload">Upload Audio File</label>
<div class="file-upload" id="fileUploadArea">
<i class="fas fa-cloud-upload-alt"></i>
<p>Click to upload or drag and drop</p>
<p class="file-name" id="fileName">No file selected</p>
</div>
<input type="file" id="audioUpload" accept="audio/*">
</div>
<div class="input-group">
<label>Or Enter Audio URLs</label>
<div class="url-input">
<input type="text" id="audioUrl" placeholder="https://example.com/audio.mp3">
<button class="btn btn-add" id="addUrlBtn">
<i class="fas fa-plus"></i> Add
</button>
</div>
<div id="urlList" class="url-list"></div>
</div>
</div>
<div class="progress-container">
<div class="progress-bar">
<div class="progress" id="progressBar"></div>
</div>
<div class="status" id="statusText">Ready to analyze</div>
</div>
<div class="error-message" id="errorMessage">
<i class="fas fa-exclamation-triangle"></i>
<span id="errorText"></span>
</div>
<button class="btn btn-primary" id="analyzeBtn">
<i class="fas fa-search"></i> Analyze Similarity
</button>
</div>
<div class="card results" id="resultsSection">
<div class="results-header">
<h2 class="results-title">Similarity Results</h2>
<div class="match-percentage" id="matchPercentage">0%</div>
</div>
<div class="timestamps" id="timestampsContainer">
<div class="no-results">
<i class="fas fa-music"></i>
<p>No similarity results yet. Upload audio files and click analyze to find matches.</p>
</div>
</div>
</div>
</div>
<div class="loading-overlay" id="loadingOverlay">
<div class="loading-content">
<div class="spinner"></div>
<div class="loading-text">Analyzing audio similarity...</div>
<div class="loading-subtext" id="loadingSubtext">Processing audio files</div>
</div>
</div>
<script>
// DOM Elements
const audioUpload = document.getElementById('audioUpload');
const fileUploadArea = document.getElementById('fileUploadArea');
const fileName = document.getElementById('fileName');
const audioUrl = document.getElementById('audioUrl');
const addUrlBtn = document.getElementById('addUrlBtn');
const urlList = document.getElementById('urlList');
const analyzeBtn = document.getElementById('analyzeBtn');
const progressBar = document.getElementById('progressBar');
const statusText = document.getElementById('statusText');
const errorMessage = document.getElementById('errorMessage');
const errorText = document.getElementById('errorText');
const resultsSection = document.getElementById('resultsSection');
const timestampsContainer = document.getElementById('timestampsContainer');
const matchPercentage = document.getElementById('matchPercentage');
const loadingOverlay = document.getElementById('loadingOverlay');
const loadingSubtext = document.getElementById('loadingSubtext');
// State
let uploadedFiles = [];
let audioUrls = [];
let audioContext = null;
let isProcessing = false;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Set up event listeners
audioUpload.addEventListener('change', handleFileUpload);
fileUploadArea.addEventListener('click', () => audioUpload.click());
fileUploadArea.addEventListener('dragover', handleDragOver);
fileUploadArea.addEventListener('drop', handleDrop);
addUrlBtn.addEventListener('click', addAudioUrl);
analyzeBtn.addEventListener('click', analyzeSimilarity);
// Initialize audio context
audioContext = new (window.AudioContext || window.webkitAudioContext)();
// Load any saved state
loadState();
});
// File upload handlers
function handleFileUpload(e) {
const files = e.target.files;
if (files.length > 0) {
const file = files[0];
uploadedFiles = [file];
fileName.textContent = file.name;
updateStatus('File ready for analysis');
}
}
function handleDragOver(e) {
e.preventDefault();
fileUploadArea.style.borderColor = '#2ecc71';
}
function handleDrop(e) {
e.preventDefault();
fileUploadArea.style.borderColor = 'var(--primary-color)';
const files = e.dataTransfer.files;
if (files.length > 0) {
const file = files[0];
if (file.type.startsWith('audio/')) {
uploadedFiles = [file];
fileName.textContent = file.name;
updateStatus('File ready for analysis');
} else {
showError('Please drop an audio file');
}
}
}
// URL handlers
function addAudioUrl() {
const url = audioUrl.value.trim();
if (url) {
if (!isValidUrl(url)) {
showError('Please enter a valid URL');
return;
}
audioUrls.push(url);
updateUrlList();
audioUrl.value = '';
updateStatus('URL added for analysis');
}
}
function isValidUrl(url) {
try {
new URL(url);
return true;
} catch (e) {
return false;
}
}
function updateUrlList() {
urlList.innerHTML = '';
if (audioUrls.length === 0) return;
const list = document.createElement('div');
list.className = 'url-items';
audioUrls.forEach((url, index) => {
const urlItem = document.createElement('div');
urlItem.className = 'audio-preview';
const urlInfo = document.createElement('div');
urlInfo.className = 'audio-info';
const urlName = document.createElement('div');
urlName.className = 'audio-name';
urlName.textContent = truncateUrl(url);
const urlIndex = document.createElement('div');
urlIndex.className = 'audio-duration';
urlIndex.textContent = `URL ${index + 1}`;
urlInfo.appendChild(urlName);
urlInfo.appendChild(urlIndex);
const removeBtn = document.createElement('button');
removeBtn.className = 'btn btn-remove btn-small';
removeBtn.innerHTML = '<i class="fas fa-trash"></i>';
removeBtn.onclick = () => removeUrl(index);
urlItem.appendChild(urlInfo);
urlItem.appendChild(removeBtn);
list.appendChild(urlItem);
});
urlList.appendChild(list);
}
function truncateUrl(url) {
if (url.length <= 40) return url;
return url.substring(0, 20) + '...' + url.substring(url.length - 15);
}
function removeUrl(index) {
audioUrls.splice(index, 1);
updateUrlList();
updateStatus('URL removed');
}
// Analysis functions
async function analyzeSimilarity() {
if (isProcessing) return;
// Validate inputs
if (uploadedFiles.length === 0 && audioUrls.length === 0) {
showError('Please upload an audio file or add a URL');
return;
}
// Show loading overlay
isProcessing = true;
loadingOverlay.style.display = 'flex';
resultsSection.style.display = 'none';
try {
// Simulate analysis process
updateProgress(0, 'Loading audio files...');
await simulateDelay(500);
updateProgress(20, 'Extracting audio features...');
await simulateDelay(800);
updateProgress(40, 'Analyzing frequency patterns...');
await simulateDelay(1000);
updateProgress(60, 'Comparing audio segments...');
await simulateDelay(1200);
updateProgress(80, 'Calculating similarity scores...');
await simulateDelay(800);
// Generate mock results
const results = generateMockResults();
updateProgress(100, 'Analysis complete!');
await simulateDelay(500);
// Display results
displayResults(results);
} catch (error) {
showError('Analysis failed: ' + error.message);
} finally {
isProcessing = false;
loadingOverlay.style.display = 'none';
}
}
function generateMockResults() {
// Generate random similarity percentage between 30% and 95%
const similarity = Math.floor(Math.random() * 65) + 30;
// Generate 3-7 timestamp matches
const numMatches = Math.floor(Math.random() * 5) + 3;
const timestamps = [];
for (let i = 0; i < numMatches; i++) {
// Generate random timestamps (in seconds)
const start = Math.floor(Math.random() * 180); // 0-3 minutes
const duration = Math.floor(Math.random() * 30) + 5; // 5-35 seconds
// Generate similarity score for this segment (higher than overall)
const segmentSimilarity = Math.min(100, similarity + Math.floor(Math.random() * 30));
timestamps.push({
start: formatTime(start),
end: formatTime(start + duration),
duration: duration,
similarity: segmentSimilarity,
waveform: generateMockWaveform()
});
}
return {
overallSimilarity: similarity,
timestamps: timestamps.sort((a, b) => b.similarity - a.similarity)
};
}
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
function generateMockWaveform() {
const points = [];
const numPoints = 100;
// Generate random waveform data
for (let i = 0; i < numPoints; i++) {
points.push(Math.random() * 0.8 + 0.1);
}
return points;
}
function displayResults(results) {
// Update match percentage
matchPercentage.textContent = `${results.overallSimilarity}%`;
// Clear previous results
timestampsContainer.innerHTML = '';
// Create timestamp cards
results.timestamps.forEach((timestamp, index) => {
const card = document.createElement('div');
card.className = 'timestamp-card';
const header = document.createElement('div');
header.className = 'timestamp-header';
const time = document.createElement('div');
time.className = 'timestamp-time';
time.textContent = `${timestamp.start} - ${timestamp.end}`;
const score = document.createElement('div');
score.className = 'similarity-score';
score.textContent = `${timestamp.similarity}% match`;
header.appendChild(time);
header.appendChild(score);
const details = document.createElement('div');
details.className = 'timestamp-details';
details.textContent = `Duration: ${timestamp.duration} seconds`;
const waveformContainer = document.createElement('div');
waveformContainer.className = 'waveform-container';
const waveform = document.createElement('div');
waveform.className = 'waveform';
waveform.id = `waveform-${index}`;
waveformContainer.appendChild(waveform);
card.appendChild(header);
card.appendChild(details);
card.appendChild(waveformContainer);
timestampsContainer.appendChild(card);
// Render waveform chart
renderWaveformChart(index, timestamp.waveform);
});
// Show results section
resultsSection.style.display = 'block';
updateStatus('Analysis complete. Found ' + results.timestamps.length + ' matching segments.');
}
function renderWaveformChart(index, data) {
const options = {
series: [{
name: 'Amplitude',
data: data
}],
chart: {
type: 'area',
height: 80,
sparkline: {
enabled: true
},
animations: {
enabled: false
}
},
stroke: {
curve: 'smooth',
width: 1.5,
colors: [var(--primary-color)]
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.7,
opacityTo: 0.1,
stops: [0, 100]
}
},
xaxis: {
labels: {
show: false
},
axisBorder: {
show: false
},
axisTicks: {
show: false
}
},
yaxis: {
show: false
},
grid: {
show: false,
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
}
},
tooltip: {
enabled: false
}
};
const chart = new ApexCharts(document.querySelector(`#waveform-${index}`), options);
chart.render();
}
// Utility functions
function updateStatus(text) {
statusText.textContent = text;
}
function updateProgress(percent, text) {
progressBar.style.width = `${percent}%`;
loadingSubtext.textContent = text;
}
function showError(message) {
errorText.textContent = message;
errorMessage.classList.add('show');
// Hide error after 5 seconds
setTimeout(() => {
errorMessage.classList.remove('show');
}, 5000);
}
function simulateDelay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function loadState() {
// In a real app, you might load from localStorage
// For this demo, we'll just initialize empty arrays
uploadedFiles = [];
audioUrls = [];
}
// Save state (not used in this demo but would be in a real app)
function saveState() {
// localStorage.setItem('audioSimilarityState', JSON.stringify({
// uploadedFiles: uploadedFiles.map(f => ({ name: f.name, size: f.size })),
// audioUrls: audioUrls
// }));
}
</script>
</body>
</html>