/* static/app.js - HİBRİT + DUPLICATE KILLER + AUTO-NEXT FIX */ const playerUI = document.getElementById("player-ui"); const titleEl = playerUI.querySelector(".title"); const subEl = playerUI.querySelector(".subtitle"); const nextBtn = playerUI.querySelector(".next-btn"); const iframe = document.getElementById("soundcloud-player"); const debug = document.getElementById("debug-info"); const vinylEl = playerUI.querySelector(".vinyl"); const bgLayer = document.getElementById("bg-layer"); /* USER CONTEXT */ const urlParams = new URLSearchParams(window.location.search); const currentUser = urlParams.get('user') || 'guest'; let currentLang = urlParams.get('lang') || 'en'; console.log("🚀 App Başladı (Robust Mode) | User:", currentUser); if (debug) debug.textContent = "Sistem Hazırlanıyor..."; /* ====================================================== STATE VARIABLES ====================================================== */ let currentMood = null; let globalDeck = []; let poolIndex = 0; const TRACKS_PER_CHUNK = 5; // Aynı Şarkı Dedektörü İçin let lastTrackID = null; let skipCount = 0; // Widget Yönetimi let scWidget = null; // Widget instance'ını burada tutacağız // Arka Plan Değişkenleri let bgPlaylist = []; let bgIndex = 0; let bgTimer = null; /* THEME & UTIL */ function applyTheme(color) { if (!playerUI) return; playerUI.style.borderColor = color; playerUI.style.boxShadow = `0 0 20px ${color}40`; if (titleEl) { titleEl.style.color = color; titleEl.style.textShadow = `0 0 15px ${color}`; } if (subEl) subEl.style.color = color; if (nextBtn) nextBtn.style.backgroundColor = color; if (vinylEl) vinylEl.style.borderColor = color; } function shuffleArray(arr){ for (let i = arr.length - 1; i > 0; i--){ const j = Math.floor(Math.random() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; } } /* ====================================================== 🔥 SOUNDCLOUD WIDGET EVENTS (YENİLENEN YAPI) ====================================================== */ function setupWidgetEvents() { if (typeof SC === 'undefined' || !iframe) return; // Widget'ı her seferinde iframe elementi üzerinden yeniden tanımla scWidget = SC.Widget(iframe); // 1. READY: Widget hazır olduğunda (Oynatmayı zorla) scWidget.bind(SC.Widget.Events.READY, function() { console.log("✅ Widget Hazır (READY Event)"); // Tarayıcı otomatik oynatmayı engelliyorsa buradaki play komutu // bazen konsola hata düşürür ama denemekten zarar gelmez. scWidget.play(); // Ses seviyesini kontrol et (Opsiyonel: Ses kapalıysa açar) scWidget.setVolume(100); }); // 2. FINISH: Şarkı bittiğinde (Otomatik sıradaki) scWidget.bind(SC.Widget.Events.FINISH, function() { console.log("🏁 Şarkı bitti, sıradakine geçiliyor..."); skipCount = 0; playNext(); }); // 3. PLAY & DUPLICATE CHECK: Çalmaya başladığında scWidget.bind(SC.Widget.Events.PLAY, function() { if(vinylEl) vinylEl.style.animationPlayState = "running"; scWidget.getCurrentSound(function(sound) { if (sound) { console.log(`🔊 Çalan ID: ${sound.id} | Önceki ID: ${lastTrackID}`); // AYNI ŞARKI KONTROLÜ if (lastTrackID !== null && sound.id === lastTrackID) { if (skipCount < 5) { console.warn("⚠️ AYNI ŞARKI ALGILANDI! Otomatik atlanıyor..."); skipCount++; setTimeout(playNext, 300); // Biraz bekle ve geç } else { console.error("❌ Çok fazla deneme yapıldı (Loop). Çalmaya devam ediliyor."); skipCount = 0; } } else { lastTrackID = sound.id; skipCount = 0; } } }); }); // 4. PAUSE scWidget.bind(SC.Widget.Events.PAUSE, function() { if(vinylEl) vinylEl.style.animationPlayState = "paused"; }); // 5. ERROR: Herhangi bir yükleme hatası olursa scWidget.bind(SC.Widget.Events.ERROR, function() { console.error("⚠️ Widget Hatası. Bir sonrakine geçiliyor."); setTimeout(playNext, 1000); }); } /* ====================================================== 🎶 MÜZİK MANTIĞI ====================================================== */ async function loadPlaylist(){ try { const r = await fetch(`/api/music?user=${currentUser}&lang=${currentLang}&t=` + Date.now()); const d = await r.json(); applyTheme(d.color || "#ffffff"); if (titleEl) titleEl.textContent = `${d.icon || "🎵"} ${d.mood_header || d.mood}`; if (subEl) subEl.textContent = d.title || "SoundCloud"; let moodChunks = d.chunks || []; if (moodChunks.length === 0 && d.embed_src) moodChunks = [d.embed_src]; if (moodChunks.length === 0) { console.warn("Müzik listesi boş!"); return; } globalDeck = []; moodChunks.forEach((chunkUrl, cIndex) => { let cleanBase = chunkUrl.split("&")[0]; for (let i = 0; i < TRACKS_PER_CHUNK; i++) { globalDeck.push({ url: cleanBase, trackIndex: i, chunkId: cIndex }); } }); shuffleArray(globalDeck); poolIndex = 0; console.log(`🎵 Playlist Hazır. ${globalDeck.length} Şarkı.`); playNext(); } catch (err) { console.error("Playlist Load Error:", err); } } function playNext(){ if (!globalDeck || globalDeck.length === 0) return; if (!iframe) return; if (poolIndex >= globalDeck.length) { shuffleArray(globalDeck); poolIndex = 0; } let card = globalDeck[poolIndex]; poolIndex++; // Yeni SRC oluştur // ÖNEMLİ: visual=true görsel moddur, bazen API'da daha stabil çalışır. let newSrc = `${card.url}&auto_play=true&visual=true&start_track=${card.trackIndex}&cb=${Date.now()}`; // SRC'yi değiştirmeden önce event listener'ları temizlemeye gerek yok, // çünkü iframe yenilenince widget instance'ı boşa düşer. iframe.src = newSrc; // Debug güncelle if (debug && currentMood) { debug.textContent = `[${currentMood.toUpperCase()}] Parça: ${card.trackIndex + 1} (Deste: ${card.chunkId + 1})`; } // Iframe yüklendiğinde Widget Eventlerini TEKRAR bağla // Iframe'in "onload" eventi, yeni src tamamen yüklendiğinde tetiklenir. iframe.onload = function() { console.log("📥 Iframe Yüklendi -> Eventler Bağlanıyor..."); setupWidgetEvents(); }; } /* 🔥 FETCH & WATCH LOGIC */ async function watchMood(){ try{ const r = await fetch(`/api/get_current_mood?user=${currentUser}`); const d = await r.json(); if (d.mood && (d.mood !== currentMood || currentMood === null)){ console.log("Mood Algılandı:", d.mood); currentMood = d.mood; if (debug) debug.textContent = "Mood → " + currentMood.toUpperCase(); lastTrackID = null; loadPlaylist(); loadBackgroundPlaylist(); } }catch(e){ console.error("Mood watch error", e); } } /* ====================================================== 🖼️ ARKA PLAN MANTIĞI ====================================================== */ async function loadBackgroundPlaylist(){ try{ const r = await fetch(`/api/playlist?user=${currentUser}&t=` + Date.now()); const d = await r.json(); if (!Array.isArray(d) || d.length === 0) return; bgPlaylist = d; bgIndex = 0; showNextBackground(); }catch(e){ console.error("BG Load Error:", e); } } function showNextBackground(){ if (!bgPlaylist.length || !bgLayer) return; const item = bgPlaylist[bgIndex]; bgIndex = (bgIndex + 1) % bgPlaylist.length; bgLayer.src = item.url; if (bgTimer) clearTimeout(bgTimer); bgTimer = setTimeout(showNextBackground, item.duration); } /* ====================================================== LISTENERS ====================================================== */ if (nextBtn) nextBtn.addEventListener("click", () => { skipCount = 0; playNext(); }); window.addEventListener("message", (event) => { if (event.data.type === "LANGUAGE_UPDATE") { if (event.data.lang && event.data.lang !== currentLang) { currentLang = event.data.lang; loadPlaylist(); } } }); // Başlat (async () => { if(debug) debug.textContent = "Veriler Alınıyor..."; await loadBackgroundPlaylist(); await watchMood(); setInterval(watchMood, 3000); })();