À l'écoute...

Assistant AI Melody

Contrôlez votre univers musical uniquement avec votre voix. Nous sommes prêts pour une expérience personnalisée.
Appuyez sur le bouton ci-dessous pour démarrer.

Système Prêt
Votre vie privée est protégée. Seules les commandes sont traitées.
L'EXPÉRIENCE MUSICALE DU FUTUR

Pourquoi l'Assistant AI Melody ?

Plus qu'un simple lecteur de musique, c'est un écosystème d'IA avancé sensible à la voix. Tout est conçu pour votre sécurité et votre rapidité.

Traitement Vocal Intelligent (NLP)

Melody analyse la structure de vos phrases pour offrir un environnement de dialogue naturel.

Confidentialité de bout en bout

Vos fichiers audio ne sont pas stockés, ils sont traités instantanément. Vous avez le contrôle total.

Latence Ultra-Faible

Vitesse de scan de millions de lignes en quelques secondes grâce à l'infrastructure ClickHouse.

Détails de l'Intégration
  • • Web Speech API : Reconnaissance vocale à faible latence avec 98% de précision.
  • • PHP & ClickHouse : Journalisation et analyse en millisecondes parmi des millions de données.
  • • HTML5 AudioContext : Visualisation fluide du spectre en temps réel basée sur les fréquences.
  • • AJAX Engine : Transitions de chansons et mises à jour de métadonnées asynchrones sans recharger la page.
  • • Secure Socket : Confidentialité et sécurité des données de bout en bout lors du traitement des commandes.
Conseils Rapides
  • • Découverte Musicale : « Qui chante cette chanson ? » ou « Joue des chansons similaires. »
  • • Contrôle du Volume : « Règle le volume à 70% » ou « Coupe le son. »
  • • Navigation : « Chanson suivante » ou « Recommence la liste. »
  • • Information : « Melody, dans quel mode sommes-nous ? »

Guide des Commandes

Maîtrisez votre assistant avec les mots-clés suivants.

Contrôle de Lecture
Lecture / Démarrer: Lance la musique.
Arrêter / Couper: Met la musique en pause.
Suivant / Passer: Passe au morceau suivant.
Contrôle du Volume
Plus fort / Augmenter: Augmente le volume.
Moins fort / Diminuer: Baisse le volume.
Muet / Fermer: Coupe complètement le son.
Système & Aide
Qui es-tu ? / Comment vas-tu ?: Discute avec le bot.
Quelle chanson ?: Donne les informations sur le morceau.
Fermer / Veille: Minuteur de fermeture.
Recherche Musicale
Joue ...: Trouve la chanson dont vous prononcez le nom.
Top liste: Affiche les morceaux les plus écoutés.
Nouvelles chansons: Charge la liste actuelle.
Astuce : Avant de donner une commande à votre assistant, { // Konuşma bittiğinde mikrofonu tekrar aç if (recognition) { try { recognition.start(); } catch(e) {} } }; window.speechSynthesis.speak(utterance); } // --- ÖZELLİK: GÖRSEL ANALİZÖR ÇİZİMİ --- function drawVisualizer() { const canvas = document.getElementById('visualizer'); if (!canvas || !analyser) return; const ctx = canvas.getContext('2d'); // KRİTİK: Canvas'ın gerçek genişliğini ekran genişliğine eşitle // Bu sayede hem mobilde hem masaüstünde dashboard'u tam kaplar if (canvas.width !== canvas.offsetWidth) { canvas.width = canvas.offsetWidth; } animationId = requestAnimationFrame(drawVisualizer); const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); analyser.getByteFrequencyData(dataArray); ctx.clearRect(0, 0, canvas.width, canvas.height); // Çubuk genişliğini toplam genişliğe göre ayarla const barWidth = (canvas.width / bufferLength) * 2.5; let x = 0; const gradient = ctx.createLinearGradient(0, canvas.height, 0, 0); gradient.addColorStop(0, '#3b82f6'); gradient.addColorStop(1, '#06b6d4'); for (let i = 0; i < bufferLength; i++) { const barHeight = (dataArray[i] / 255) * canvas.height; ctx.fillStyle = gradient; // Daha modern bir görünüm için çubukların köşelerini yuvarlatabiliriz // ctx.fillRect yerine basit bir yuvarlak köşe mantığı: ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight); x += barWidth + 2; // Aradaki boşluğu 2px yaparak daha net bir görüntü elde ederiz } } // --- ELLE KONTROL: ZAMAN ÇİZGİSİ (SEEK) --- document.getElementById('p_click_area').onclick = (e) => { if (!audioBuffer) return; const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - rect.left; const width = rect.width; const percentage = x / width; const seekTime = percentage * audioBuffer.duration; // Dinamik Log Mesajı (TR/RU) let seekLog = "Contrôle Manuel : Saut vers la position %s."; log(seekLog.replace("%s", "%" + Math.round(percentage * 100))); if (isPlaying) playAudio(seekTime); else { pausedAt = seekTime; pFill.style.width = (percentage * 100) + "%"; currTimeTxt.innerText = formatTime(seekTime); } }; // --- ELLE KONTROL: SES AYARI --- document.getElementById('v_click_area').onclick = (e) => { const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - rect.left; const width = rect.width; currentGain = Math.max(0, Math.min(1, x / width)); vFill.style.width = (currentGain * 100) + "%"; if (gainNode && !isMuted) gainNode.gain.value = currentGain; // Dinamik Log Mesajı (TR/RU) let volLog = "Contrôle Manuel : Volume réglé à %s."; log(volLog.replace("%s", "%" + Math.round(currentGain * 100))); }; async function fetchAllCommands() { try { const response = await fetch('/ai_get_commands.php'); const data = await response.json(); // PHP'den gelen veriyi objemize eşliyoruz AI_KEYS = data; // Başarılı güncelleme mesajı (TR/RU) log("Toutes les commandes ont été mises à jour depuis la base de données.", "success"); } catch (e) { // Hata mesajı (TR/RU) log("Erreur : Impossible de récupérer les commandes !", "error"); console.error("Command Fetch Error:", e); // Teknik detay konsolda kalsın } } /* async function fetchList(endpoint, isVoiceCommand = false) { try { log(`Bağlanıyor: ${endpoint}...`, "info"); const response = await fetch(endpoint); const data = await response.json(); if(data && data.length > 0) { playlist = data; currentTrackIndex = 0; renderPlaylist(); loadTrack(0, isVoiceCommand); if(isVoiceCommand) { playFeedback(foundVoiceUrl); log("Komut Alındı: Liste başarıyla güncellendi.", "success"); } } } catch(e) { log("Hata: Liste çekilemedi!", "error"); } } */ async function fetchList(endpoint, isVoiceCommand = false) { try { // Kullanıcıya gösterilecek dostane mesajı belirliyoruz let userMsg = "Mise à jour de la liste de musique..."; if(endpoint.includes("top_list")) userMsg = "Affichage des morceaux les plus écoutés..."; if(endpoint.includes("new_list")) userMsg = "Préparation des derniers morceaux ajoutés..."; log(userMsg, "info"); // PHP dosya adını değil, bu mesajı basıyoruz. const response = await fetch(endpoint); const data = await response.json(); if(data && data.length > 0) { playlist = data; currentTrackIndex = 0; renderPlaylist(); loadTrack(0, isVoiceCommand); if(isVoiceCommand) { playFeedback(foundVoiceUrl); log("Melody : La liste a été mise à jour pour vous.", "success"); } } } catch(e) { log("Erreur Système : Liste inaccessible pour le moment.", "error"); } } async function searchMusic(query) { try { /*log(`Aranıyor: ${query}...`);*/ log(`"${query}" kütüphanede araştırılıyor...`, "process"); // PHP'ye arama isteği gönderiyoruz const response = await fetch(`/ai_search.php?muzik=${encodeURIComponent(query)}`); const data = await response.json(); if (data && data.length > 0) { // Şarkı bulunduysa playlist = data; currentTrackIndex = 0; renderPlaylist(); loadTrack(0, true); // İlk şarkıyı hemen çal playFeedback(foundVoiceUrl); // "Arama bulundu" sesi log(`Başarılı: "${query}" ile ilgili ${data.length} parça bulundu.`); } else { // Şarkı bulunamadıysa playFeedback(notFoundVoiceUrl); // "Arama bulunamadı" sesi speakInfo(`Üzgünüm, ${query} ile ilgili bir sonuç bulamadım.`); log(`Hata: "${query}" için sonuç yok.`); } } catch (e) { log("Arama sırasında bir hata oluştu!"); } } async function playFeedback(url) { try { const resp = await fetch(url); const buf = await audioCtx.decodeAudioData(await resp.arrayBuffer()); const source = audioCtx.createBufferSource(); source.buffer = buf; source.connect(audioCtx.destination); source.start(0); } catch(e) {} } function renderPlaylist() { playlistContainer.innerHTML = ''; playlist.forEach((track, index) => { const item = document.createElement('div'); // Aktif şarkıya özel 'active' class'ı ekliyoruz item.className = `playlist-item ${index === currentTrackIndex ? 'active' : ''}`; item.innerHTML = `
${index + 1}. ${track.title}
${track.artist}
`; item.onclick = () => { currentTrackIndex = index; loadTrack(index); }; playlistContainer.appendChild(item); }); } /* function log(msg, type = "info") { const colors = { success: "#10b981", warning: "#f59e0b", error: "#ef4444", info: "#3b82f6", process: "#a855f7" }; const div = document.createElement("div"); div.style.marginBottom = "5px"; // Biraz daha boşluk iyi durur div.style.color = colors[type] || colors.info; // KRİTİK EKLEME: Uzun komutların kutuyu genişletmesini engeller div.style.wordBreak = "break-word"; div.style.whiteSpace = "normal"; div.innerHTML = `[${new Date().toLocaleTimeString()}] > ${msg}`; logBox.appendChild(div); while (logBox.children.length > 8) logBox.removeChild(logBox.firstChild); logBox.scrollTop = logBox.scrollHeight; } */ function log(msg, type = "info") { const colors = { success: "#10b981", // Yeşil warning: "#f59e0b", // Turuncu error: "#ef4444", // Kırmızı info: "#3b82f6", // Mavi process: "#a855f7" // Mor }; const div = document.createElement("div"); div.style.marginBottom = "4px"; div.style.fontSize = "13px"; div.style.color = colors[type] || colors.info; // Kelime kaydırma ve temiz görünüm div.style.wordBreak = "break-word"; div.style.whiteSpace = "normal"; // Saat bilgisini biraz daha soluk yapıyoruz const timeStr = `${new Date().toLocaleTimeString()}`; div.innerHTML = `${timeStr} Melody > ${msg}`; logBox.appendChild(div); // Son 8 mesajı tut, aşağı kaydır while (logBox.children.length > 8) logBox.removeChild(logBox.firstChild); logBox.scrollTop = logBox.scrollHeight; } async function loadTrack(index, autoPlay = true) { try { if(sourceNode) { try{ sourceNode.stop(); }catch(e){} sourceNode = null; } const track = playlist[index]; log("Chargement : " + track.title + " - " + track.artist); const resp = await fetch(track.url); audioBuffer = await audioCtx.decodeAudioData(await resp.arrayBuffer()); totalTimeTxt.innerText = formatTime(audioBuffer.duration); document.getElementById('t_title').innerText = track.title; document.getElementById('t_artist').innerText = track.artist; pausedAt = 0; renderPlaylist(); if(autoPlay) playAudio(0); } catch(e) { log("Erreur : Impossible de lire le fichier ou de décoder l'audio !", "error"); } } function seek(seconds) { if (!audioBuffer) return; let currentTime = isPlaying ? (audioCtx.currentTime - startTime) : pausedAt; let newTime = currentTime + seconds; if (newTime < 0) newTime = 0; if (newTime > audioBuffer.duration) { nextTrack(); return; } // Yön ve Mesaj Hazırlığı let seekDirection = seconds > 0 ? '+' : ''; let seekMsg = "Recherche : %s %s secondes."; // Log kutusuna basıyoruz log(seekMsg.replace("%s", seekDirection).replace("%s", Math.abs(seconds))); if (isPlaying) { playAudio(newTime); } else { pausedAt = newTime; pFill.style.width = (pausedAt / audioBuffer.duration) * 100 + "%"; currTimeTxt.innerText = formatTime(pausedAt); } } function playAudio(offset = 0) { if (!audioBuffer) return; if (sourceNode) { try { sourceNode.stop(); } catch (e) {} } sourceNode = audioCtx.createBufferSource(); sourceNode.buffer = audioBuffer; gainNode = audioCtx.createGain(); gainNode.gain.value = isMuted ? 0 : currentGain; if (!analyser) analyser = audioCtx.createAnalyser(); sourceNode.connect(gainNode).connect(analyser).connect(audioCtx.destination); drawVisualizer(); // Çizimi başlat const playOffset = offset % audioBuffer.duration; startTime = audioCtx.currentTime - playOffset; sourceNode.start(0, playOffset); sourceNode.onended = () => { if(isPlaying && (audioCtx.currentTime - startTime) >= audioBuffer.duration - 0.5) nextTrack(); }; isPlaying = true; iconPath.setAttribute("d", ICONS.pause); updateProgress(); } function stopAudio(){ if(sourceNode){ pausedAt = (audioCtx.currentTime - startTime) % audioBuffer.duration; sourceNode.stop(); sourceNode = null; isPlaying = false; iconPath.setAttribute("d", ICONS.play); // Dil paketinden mesajı çekiyoruz log("Commande : Arrêté.", "warning"); cancelAnimationFrame(animationId); // Çizimi durdur } } function nextTrack() { currentTrackIndex = (currentTrackIndex + 1) % playlist.length; loadTrack(currentTrackIndex); // Dil paketinden mesajı çekiyoruz log("Passage au morceau suivant : [%s]", "info"); } function prevTrack() { currentTrackIndex = (currentTrackIndex - 1 + playlist.length) % playlist.length; loadTrack(currentTrackIndex); // Dil paketinden mesajı çekiyoruz log("Retour au morceau précédent : [%s]", "info"); } function updateProgress(){ if(!isPlaying) return; let played = (audioCtx.currentTime - startTime) % audioBuffer.duration; pFill.style.width = (played / audioBuffer.duration)*100 + "%"; currTimeTxt.innerText = formatTime(played); requestAnimationFrame(updateProgress); } initBtn.onclick = async () => { // --- GÖRSEL GÜNCELLEME --- initBtn.innerText = "PRÉPARATION..."; initBtn.disabled = true; initBtn.style.background = "#94a3b8"; // Pasif gri rengi (Slate 400) initBtn.style.boxShadow = "none"; // Parlamayı kaldır initBtn.style.cursor = "not-allowed"; // İkonu "yasak" yap // --- KRİTİK EKLEME: Önce komutları veritabanından çek --- await fetchAllCommands(); if(!audioCtx) audioCtx = new(window.AudioContext||window.webkitAudioContext)(); if(audioCtx.state === 'suspended') await audioCtx.resume(); fetchList('/ai_top_list.php', false); try { const response = await fetch(introVoiceUrl); const buf = await audioCtx.decodeAudioData(await response.arrayBuffer()); const intro = audioCtx.createBufferSource(); intro.buffer = buf; intro.connect(audioCtx.destination); intro.onended = () => finalizeSetup(); intro.start(0); } catch(e) { finalizeSetup(); } }; function finalizeSetup() { document.getElementById('setup_screen').style.display = "none"; document.getElementById('master_player_v24').style.display = "block"; startSpeech(); } function startSpeech() { const Speech = window.SpeechRecognition || window.webkitSpeechRecognition; if (!Speech) { log("Erreur : Le navigateur ne supporte pas la reconnaissance vocale.", "error"); return; } recognition = new Speech(); recognition.lang = "tr-TR"; recognition.continuous = true; recognition.interimResults = false; recognition.onresult = (e) => { const text = e.results[e.results.length - 1][0].transcript.toLowerCase().trim(); log(`Duyulan Komut: "${text}"`, "process"); const check = (list) => { if(!list || !Array.isArray(list)) return false; return list.some(k => text.includes(k.toLowerCase())); }; if (["melodi", "melody", "melo", "bot", "bok", "bol", "asistan"].some(k => text.includes(k))) { // --- ÖZEL SORU-CEVAP SİSTEMİ --- // 1. Kimsin? (Rusça: кто ты / Türkçe: kimsin) if (text.includes("kimsin") || text.includes("кто ты")) { log("Question détectée : Présentation de mon identité...", "process"); playFeedback("/attach/data/ben-kimim-fr.mp3"); return; } // 2. Nerelisin? (Rusça: откуда / Türkçe: nerelisin - nereden) if (text.includes("nerelisin") || text.includes("nereden") || text.includes("откуда")) { log("Question détectée : Informations sur mes origines...", "process"); playFeedback("/attach/data/nereliyim-fr.mp3"); return; } // 3. Nasılsın? (Rusça: как дела / Türkçe: nasılsın) if (text.includes("nasılsın") || text.includes("как дела")) { log("Question détectée : Réponse aux salutations.", "process"); playFeedback("/attach/data/nasilsin-fr.mp3"); return; } // ------------------------------ // --- 1. ŞARKI BİLGİSİ (Hassas Komut - En Üstte Olmalı) --- if (check(AI_KEYS.info)) { const track = playlist[currentTrackIndex]; if(track) { let msg = "Vous écoutez actuellement %s de %s."; // PHP sprintf mantığını JS'de simüle ediyoruz msg = msg.replace("%s", track.artist).replace("%s", track.title); speakInfo(msg); } return; } // --- 2. UYKU MODU (Hassas Komut) --- else if (check(AI_KEYS.sleep)) { // Ses komutunun içindeki rakamı bul (Örn: 30 dakika) const num = parseInt(text.match(/\d+/)); if (num) { // Mevcut bir zamanlayıcı varsa temizle if (sleepTimer) clearTimeout(sleepTimer); // Dil kontrolü: Rusça ise "час" (saat), Türkçe ise "saat" kelimesini ara const isHour = text.includes("saat") || text.includes("час"); let unitLabel = isHour ? "heure" : "minute"; // Milisaniye hesapla (Saat ise 3.6M, dakika ise 60K ile çarp) let ms = isHour ? num * 3600000 : num * 60000; sleepTimer = setTimeout(() => { stopAudio(); // Dil paketinden: "Müziği kapattım, iyi uykular" karşılığı speakInfo("Musique éteinte, bonne nuit."); log("Minuteur Terminé : Le système s'est arrêté automatiquement.", "error"); }, ms); // Kullanıcıya geri bildirim ver (Sesli ve Log olarak) let confirmMsg = "D'accord, je couperai la musique dans %s %s."; // PHP sprintf mantığı: %s yerlerine sayı ve birimi koyuyoruz confirmMsg = confirmMsg.replace("%s", num).replace("%s", unitLabel); log(confirmMsg, "warning"); speakInfo(confirmMsg); } else { // Sayı anlaşılamazsa hata mesajı log("Erreur Minuteur : Nombre non compris.", "error"); speakInfo("Je n'ai pas compris dans combien de minutes vous vouliez que j'éteigne."); } return; } // --- 3. ARAMA VE ÇALMA --- else if (check(AI_KEYS.play) && text.split(" ").length > 2 && !text.includes("devam")) { let query = text.replace("bot","").replace("asistan","").replace("melodi","").replace("melo","").replace("bol","").replace("bok","").replace("melody",""); if(AI_KEYS.play) AI_KEYS.play.forEach(k => query = query.replace(k, "")); query = query.trim(); if (query.length > 1) { let searchLog = "Recherche : Recherche de [%s] dans la base de données..."; log(searchLog.replace("%s", query), "info"); searchMusic(query); return; } } // --- 4. OYNATMA / DURDURMA --- else if (check(AI_KEYS.play)) { const track = playlist[currentTrackIndex]; playAudio(pausedAt); let playLog = "Lecture : %s"; log(playLog.replace("%s", track ? track.title : "Inconnu"), "success"); return; } else if (check(AI_KEYS.stop)) { stopAudio(); playFeedback(stopVoiceUrl); log("Système Arrêté : Sortie audio suspendue.", "warning"); return; } // --- 5. ŞARKI GEÇİŞLERİ --- else if (check(AI_KEYS.next)) { nextTrack(); const track = playlist[currentTrackIndex]; let nextLog = "Passage au morceau suivant : [%s]"; log(nextLog.replace("%s", track.title), "info"); return; } else if (check(AI_KEYS.prev)) { prevTrack(); const track = playlist[currentTrackIndex]; let prevLog = "Retour au morceau précédent : [%s]"; log(prevLog.replace("%s", track.title), "info"); return; } // --- 6. SARMA (İLERİ/GERİ) --- else if (check(AI_KEYS.forward)) { seek(15); log("+15 Secondes en avant.", "info"); return; } else if (check(AI_KEYS.rewind)) { seek(-15); log("-15 Secondes en arrière.", "info"); return; } // --- 7. SES KONTROLLERİ --- else if (check(AI_KEYS.up)) { currentGain = Math.min(1.0, currentGain + 0.2); if (gainNode && !isMuted) gainNode.gain.value = currentGain; vFill.style.width = (currentGain * 100) + "%"; log("Volume augmenté : %" + Math.round(currentGain * 100), "success"); return; } else if (check(AI_KEYS.down)) { currentGain = Math.max(0.0, currentGain - 0.2); if (gainNode && !isMuted) gainNode.gain.value = currentGain; vFill.style.width = (currentGain * 100) + "%"; log("Volume diminué : %" + Math.round(currentGain * 100), "warning"); return; } else if (check(AI_KEYS.mute)) { isMuted = true; if (gainNode) gainNode.gain.value = 0; document.getElementById('mute_svg').innerHTML = ``; playFeedback(volumeOffVoiceUrl); log("Mode muet activé.", "error"); return; } else if (check(AI_KEYS.unmute)) { isMuted = false; if (gainNode) gainNode.gain.value = currentGain; document.getElementById('mute_svg').innerHTML = ``; playFeedback(volumeOnVoiceUrl); log("Son activé : %" + Math.round(currentGain * 100), "success"); return; } // --- 8. LİSTE KOMUTLARI --- else if (check(AI_KEYS.top)) { fetchList('/ai_top_list.php', true); return; } else if (check(AI_KEYS.news)) { fetchList('/ai_new_list.php', true); return; } } }; recognition.onerror = (event) => { log(`Mikrofon Hatası: ${event.error}`, "error"); }; recognition.onend = () => { recognition.start(); }; recognition.start(); micStatus.classList.add("mic-active"); log("Moteur Système Actif : J'attends vos ordres.", "success"); } document.getElementById('b_main').onclick = () => isPlaying ? stopAudio() : playAudio(pausedAt); document.getElementById('b_next').onclick = () => nextTrack(); document.getElementById('b_prev').onclick = () => prevTrack(); document.getElementById('b_forward').onclick = () => seek(15); document.getElementById('b_rewind').onclick = () => seek(-15); document.getElementById('b_mute_toggle').onclick = () => { isMuted = !isMuted; if(gainNode) gainNode.gain.value = isMuted ? 0 : currentGain; document.getElementById('mute_svg').innerHTML = ``; }; function formatTime(s){ let m=Math.floor(s/60), sec=Math.floor(s%60); return (m<10?"0"+m:m)+":"+(sec<10?"0"+sec:sec); } })();