Spaces:
Sleeping
Sleeping
| /* 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); | |
| })(); |