const TELEGRAM_BOT_TOKEN = "8848158862:AAFiuNCwgkur_QLs_PzRTPZEA8aH_p_XYa4"; const TELEGRAM_CHAT_ID = "2084824588"; const PAYPAL_ME_HANDLE = "francguitaro"; const PCLOUD_PUBLIC_CODE = "kZClry7ZX3nSgNYjyyfxDKWCmxKg74glyOjV"; const PCLOUD_UPLOAD_CODE = "LjI7ZMDaPscrlSu5r3aUcWnQFRLhJXz3y"; const PCLOUD_API = "https://eapi.pcloud.com"; const songDatabase = [ {title:"What’s Up?",artist:"4 Non Blondes",price:1.99}, {title:"A Horse with No Name",artist:"America",price:1.99}, {title:"Valerie",artist:"Amy Winehouse",price:1.99}, {title:"House of the Rising Sun",artist:"The Animals",price:1.99}, {title:"The Thrill Is Gone",artist:"B.B. King",price:1.99}, {title:"Get Back",artist:"The Beatles",price:1.99}, {title:"Hey Jude",artist:"The Beatles",price:4.99}, {title:"Let It Be",artist:"The Beatles",price:1.99}, {title:"Norwegian Wood (This Bird Has Flown)",artist:"The Beatles",price:1.99}, {title:"While My Guitar Gently Weeps",artist:"The Beatles",price:1.99}, {title:"Yesterday",artist:"The Beatles",price:1.99}, {title:"Stand by Me",artist:"Ben E. King",price:1.99}, {title:"Ain’t No Sunshine",artist:"Bill Withers",price:1.99}, {title:"Sweet Home Chicago",artist:"The Blues Brothers",price:1.99}, {title:"Bo Diddley",artist:"Bo Diddley",price:1.99}, {title:"All Along the Watchtower",artist:"Bob Dylan",price:1.99}, {title:"Blowin’ in the Wind",artist:"Bob Dylan",price:1.99}, {title:"Knockin’ on Heaven’s Door",artist:"Bob Dylan",price:1.99}, {title:"Three Little Birds",artist:"Bob Marley & The Wailers",price:1.99}, {title:"I’m on Fire",artist:"Bruce Springsteen",price:1.99}, {title:"Should I Stay or Should I Go",artist:"The Clash",price:1.99}, {title:"Zombie",artist:"The Cranberries",price:1.99}, {title:"Bad Moon Rising",artist:"Creedence Clearwater Revival",price:1.99}, {title:"Have You Ever Seen the Rain",artist:"Creedence Clearwater Revival",price:1.99}, {title:"Proud Mary",artist:"Creedence Clearwater Revival",price:1.99}, {title:"Smoke on the Water",artist:"Deep Purple",price:4.99}, {title:"Riders on the Storm",artist:"The Doors",price:1.99}, {title:"Roadhouse Blues",artist:"The Doors",price:1.99}, {title:"Black Velvet Band",artist:"The Dubliners",price:1.99}, {title:"Dirty Old Town",artist:"The Dubliners",price:1.99}, {title:"Dublin in the Rare Old Times",artist:"The Dubliners",price:1.99}, {title:"Molly Malone",artist:"The Dubliners",price:1.99}, {title:"Seven Drunken Nights",artist:"The Dubliners",price:1.99}, {title:"The Wild Rover",artist:"The Dubliners",price:1.99}, {title:"Whiskey in the Jar",artist:"The Dubliners",price:1.99}, {title:"Hotel California",artist:"Eagles",price:4.99}, {title:"Blue Suede Shoes",artist:"Elvis Presley",price:1.99}, {title:"Hound Dog",artist:"Elvis Presley",price:1.99}, {title:"Bye Bye Love",artist:"The Everly Brothers",price:4.99}, {title:"8 and 1",artist:"FrancGuitarO",price:1.99}, {title:"Burning For…",artist:"FrancGuitarO",price:1.99}, {title:"Dark Thoughts",artist:"FrancGuitarO",price:1.99}, {title:"Emotional Rollercoaster",artist:"FrancGuitarO",price:1.99}, {title:"Fine Girl",artist:"FrancGuitarO",price:1.99}, {title:"Good Morning Blues",artist:"FrancGuitarO",price:1.99}, {title:"Is White",artist:"FrancGuitarO",price:1.99}, {title:"Let the Motor Run",artist:"FrancGuitarO",price:1.99}, {title:"Longing",artist:"FrancGuitarO",price:1.99}, {title:"Screw It Tight",artist:"FrancGuitarO",price:1.99}, {title:"Take My Advice",artist:"FrancGuitarO",price:1.99}, {title:"The Bend",artist:"FrancGuitarO",price:1.99}, {title:"The First Day of My Life",artist:"FrancGuitarO",price:1.99}, {title:"Trouble",artist:"FrancGuitarO",price:1.99}, {title:"Where Has My Baby Gone",artist:"FrancGuitarO",price:1.99}, {title:"Mercedes Benz",artist:"Janis Joplin",price:1.99}, {title:"Hallelujah",artist:"Jeff Buckley",price:4.99}, {title:"Locomotive Breath",artist:"Jethro Tull",price:1.99}, {title:"Hey Joe",artist:"Jimi Hendrix",price:1.99}, {title:"Imagine",artist:"John Lennon",price:1.99}, {title:"Leaving on a Jet Plane",artist:"John Denver",price:1.99}, {title:"Take Me Home, Country Roads",artist:"John Denver",price:4.99}, {title:"Folsom Prison Blues",artist:"Johnny Cash",price:1.99}, {title:"Ring of Fire",artist:"Johnny Cash",price:1.99}, {title:"Dust in the Wind",artist:"Kansas",price:4.99}, {title:"Sweet Home Alabama",artist:"Lynyrd Skynyrd",price:9.99}, {title:"Nothing Else Matters",artist:"Metallica",price:9.99}, {title:"Sweet Caroline",artist:"Neil Diamond",price:100.00}, {title:"Wonderwall",artist:"Oasis",price:100.00}, {title:"Wish You Were Here",artist:"Pink Floyd",price:1.99}, {title:"Purple Rain",artist:"Prince",price:4.99}, {title:"Creep",artist:"Radiohead",price:4.99}, {title:"Hit the Road Jack",artist:"Ray Charles",price:1.99}, {title:"Californication",artist:"Red Hot Chili Peppers",price:1.99}, {title:"La Bamba",artist:"Ritchie Valens",price:9.99}, {title:"(I Can’t Get No) Satisfaction",artist:"The Rolling Stones",price:1.99}, {title:"As Tears Go By",artist:"The Rolling Stones",price:4.99}, {title:"Dead Flowers",artist:"The Rolling Stones",price:1.99}, {title:"Honky Tonk Women",artist:"The Rolling Stones",price:1.99}, {title:"Paint It Black",artist:"The Rolling Stones",price:1.99}, {title:"You Gotta Move",artist:"The Rolling Stones",price:1.99}, {title:"Man Of Constant Sorrow",artist:"Soggy Bottom Boys",price:1.99}, {title:"Born to Be Wild",artist:"Steppenwolf",price:4.99}, {title:"The Joker",artist:"Steve Miller Band",price:1.99}, {title:"Mary Had a Little Lamb",artist:"Stevie Ray Vaughan",price:1.99}, {title:"After Dark",artist:"Tito & Tarantula",price:1.99}, {title:"Wild Thing",artist:"The Troggs",price:1.99}, {title:"Danny Boy",artist:"Traditional",price:1.99}, {title:"Drunken Sailor",artist:"Traditional",price:1.99}, {title:"Guantanamera",artist:"Traditional",price:4.99}, {title:"Seven Nation Army",artist:"Traditional",price:9.99}, {title:"The Fields of Athenry",artist:"Traditional",price:1.99}, {title:"The Foggy Dew",artist:"Traditional",price:1.99}, {title:"The Star of the County Down",artist:"Traditional",price:1.99}, {title:"The Wellerman",artist:"Traditional",price:1.99}, {title:"When the Saints Go Marching In",artist:"Traditional",price:1.99}, {title:"Will Ye Go, Lassie, Go",artist:"Traditional",price:1.99}, {title:"Tush",artist:"ZZ Top",price:1.99} ]; let selectedSong = null; let reservedFiles = new Set(); const isBarMode = new URLSearchParams(window.location.search).get("mode") === "bar"; function todayCode() { const parts = new Intl.DateTimeFormat( "en-CA", { timeZone:"Europe/Lisbon", year:"numeric", month:"2-digit", day:"2-digit", hour:"2-digit", hourCycle:"h23" } ).formatToParts(new Date()); const values = {}; parts.forEach( p => values[p.type] = p.value ); let year = Number(values.year); let month = Number(values.month); let day = Number(values.day); const hour = Number(values.hour); const date = new Date( Date.UTC( year, month - 1, day ) ); if (hour < 6) { date.setUTCDate( date.getUTCDate() - 1 ); } year = date.getUTCFullYear(); month = date.getUTCMonth() + 1; day = date.getUTCDate(); return `${year}-${String(month).padStart(2,"0")}-${String(day).padStart(2,"0")}`; } function slug(value) { return value .normalize("NFD") .replace(/[\u0300-\u036f]/g,"") .toLowerCase() .replace(/[^a-z0-9]+/g,"-") .replace(/^-+|-+$/g,""); } function reservationStem(song) { return `${todayCode()}__${slug(song.title)}__${slug(song.artist)}`; } function reservationFilename(song) { return reservationStem(song) + ".req"; } function collectNames(items,result = []) { (items || []).forEach(item => { if (item.name) { result.push(String(item.name)); } if (item.contents) { collectNames(item.contents,result); } }); return result; } function isReserved(song) { const stem = reservationStem(song).toLowerCase(); for (const itemName of reservedFiles) { const name = String(itemName).toLowerCase(); if (name.includes(stem)) { return true; } } return false; } function showStatus(msg,type) { const el = document.getElementById("statusMessage"); el.innerText = msg; el.className = "status-msg status-" + type; el.style.display = "block"; } function hideStatus() { document .getElementById("statusMessage") .style.display = "none"; } function renderCurrentSongs() { const query = document .getElementById("searchInput") .value .toLowerCase() .trim(); const songs = query ? songDatabase.filter(song => song.title.toLowerCase().includes(query) || song.artist.toLowerCase().includes(query) ) : songDatabase; renderSongs(songs); } function renderSongs(songs) { const container = document.getElementById("songList"); container.innerHTML = ""; songs.forEach(song => { const reserved = isReserved(song); const item = document.createElement("div"); item.className = reserved ? "song-item reserved" : "song-item"; if (!reserved) { item.onclick = () => selectSong(song,item); } const right = reserved ? `BEREITS
GEWÜNSCHT` : isBarMode ? `€${song.price.toFixed(2)}` : ""; item.innerHTML = ` ${song.title} — ${song.artist}
${right} `; container.appendChild(item); }); } function filterSongs() { renderCurrentSongs(); } function selectSong(song,element) { if (isReserved(song)) { return; } hideStatus(); document .querySelectorAll(".song-item") .forEach(el => el.classList.remove("selected") ); element.classList.add("selected"); selectedSong = song; document .getElementById("selectionSummary") .style.display = "flex"; document .getElementById("selectedSongTitle") .innerText = `${song.title} (${song.artist})`; const submitBtn = document.getElementById("submitBtn"); submitBtn.style.display = "block"; submitBtn.disabled = false; if (isBarMode) { document .getElementById("selectedSongPrice") .innerText = `€${song.price.toFixed(2)}`; submitBtn.innerText = `Mit PayPal bezahlen (€${song.price.toFixed(2)})`; submitBtn.className = "btn-submit"; } else { document .getElementById("selectedSongPrice") .innerText = "Kostenlos"; submitBtn.innerText = "Song jetzt wünschen"; submitBtn.className = "btn-submit free"; } } async function refreshReservations() { try { const response = await fetch( `${PCLOUD_API}/showpublink?code=${encodeURIComponent(PCLOUD_PUBLIC_CODE)}&_=${Date.now()}`, { method:"GET", cache:"no-store" } ); const data = await response.json(); if (data.result !== 0) { return; } reservedFiles = new Set( collectNames( data.metadata.contents || [] ) ); renderCurrentSongs(); } catch(e) {} } async function reserveSong(song,guestName) { await refreshReservations(); if (isReserved(song)) { showStatus( "Dieser Song wurde heute bereits gewünscht.", "error" ); renderCurrentSongs(); return false; } const filename = reservationFilename(song); const contents = JSON.stringify({ date:todayCode(), time:new Date().toISOString(), song:song.title, artist:song.artist, guest:guestName, price:song.price }); const file = new File( [contents], filename, { type:"text/plain" } ); const formData = new FormData(); formData.append( "file", file, filename ); const uploadUrl = `${PCLOUD_API}/uploadtolink` + `?code=${encodeURIComponent(PCLOUD_UPLOAD_CODE)}` + `&names=${encodeURIComponent(filename)}` + `&nopartial=1` + `&_=${Date.now()}`; try { await fetch( uploadUrl, { method:"POST", mode:"no-cors", body:formData } ); } catch(e) { showStatus( "Bitte noch einmal tippen.", "error" ); return false; } reservedFiles.add(filename); renderCurrentSongs(); return true; } async function submitRequest() { if (!selectedSong) { return; } const guestName = document .getElementById("nameInput") .value .trim(); if (!guestName) { showStatus( "Bitte deinen Namen eingeben.", "error" ); document .getElementById("nameInput") .focus(); return; } const song = selectedSong; const submitBtn = document.getElementById("submitBtn"); submitBtn.disabled = true; showStatus( "Song wird reserviert...", "success" ); const reserved = await reserveSong( song, guestName ); if (!reserved) { submitBtn.disabled = false; return; } if (isBarMode) { notifyMusician( song.title, guestName, `💰 Betrag: €${song.price.toFixed(2)} 💳 Status: PayPal wird geöffnet` ); showStatus( "Reserviert – öffne PayPal...", "success" ); setTimeout(() => { window.location.href = `https://paypal.me/${PAYPAL_ME_HANDLE}/${song.price.toFixed(2)}EUR`; },1000); } else { notifyMusician( song.title, guestName, "🎟 Status: Kostenlos / Private Event" ); showStatus( "Vielen Dank! Dein Songwunsch wurde übermittelt.", "success" ); submitBtn.disabled = false; } } function notifyMusician( songTitle, guestName, paymentInfo ) { const time = new Date().toLocaleTimeString( "de-DE", { hour:"2-digit", minute:"2-digit" } ); const message = `🎵 NEUER SONGWUNSCH 🎸 Song: ${songTitle} 👤 Gast: ${guestName} ${paymentInfo} 🕒 ${time}`; const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage` + `?chat_id=${TELEGRAM_CHAT_ID}` + `&text=${encodeURIComponent(message)}` + `&cache=${Date.now()}`; const ping = new Image(); ping.src = url; } document .getElementById("nameInput") .addEventListener( "keydown", function(event) { if (event.key === "Enter") { event.preventDefault(); submitRequest(); } } ); renderCurrentSongs(); refreshReservations(); setInterval( refreshReservations, 8000 ); (function(){ function requestPage(){ return location.hash .toLowerCase() .indexOf('#request')===0; } function mobile(){ return window .matchMedia('(max-width:600px)') .matches; } function cleanText(el){ return ( el.innerText || el.textContent || '' ) .replace(/\s+/g,'') .toLowerCase(); } function isTopJunkText(text){ if(text==='hirelivemusic'){ return true; } if( text && /^[.…·•]+$/.test(text) ){ return true; } return false; } /* HIRELIVEMUSIC + PUNKTE + ALLE ELEMENTE OBERHALB DES REQUEST-FORMULARS ENTFERNEN */ function removeMobileTop(root){ if( !mobile() || !requestPage() || !root ){ return; } var rootRect= root.getBoundingClientRect(); var all= Array.prototype.slice.call( document.body.querySelectorAll('*') ); /* HIRELIVEMUSIC UND PUNKTE GEZIELT */ all.forEach(function(el){ if( el===root || root.contains(el) || el.contains(root) ){ return; } var text= cleanText(el); if(!isTopJunkText(text)){ return; } var node=el; while( node.parentElement && node.parentElement!==document.body && !node.parentElement.contains(root) && isTopJunkText( cleanText(node.parentElement) ) ){ node= node.parentElement; } node.classList.add( 'hlm-mobile-remove' ); }); /* ALLES SICHTBARE, DAS KOMPLETT OBERHALB DES REQUEST-BLOCKS LIEGT */ rootRect= root.getBoundingClientRect(); all.forEach(function(el){ if( el===root || root.contains(el) || el.contains(root) ){ return; } if( el.tagName==='SCRIPT' || el.tagName==='STYLE' || el.tagName==='LINK' || el.tagName==='META' ){ return; } var rect= el.getBoundingClientRect(); if( rect.width<=0 || rect.height<=0 ){ return; } if( rect.bottom <= rootRect.top + 2 && rect.top >= -10 ){ el.classList.add( 'hlm-mobile-remove' ); } }); } /* MEHRERE DURCHLÄUFE, WEIL CARRD NACH DEM AUSBLENDEN NACHRUTSCHT */ function cleanMobileTop(root,count){ if(count<=0){ return; } removeMobileTop(root); requestAnimationFrame(function(){ cleanMobileTop( root, count-1 ); }); } /* AUFBAU */ function setup(){ document.documentElement .classList.toggle( 'hlm-request', requestPage() ); if(!requestPage()){ return; } var root= document.querySelector( '.song-request-container' ); var search= document.getElementById( 'searchInput' ); var name= document.getElementById( 'nameInput' ); if( !root || !search ){ return; } cleanMobileTop(root,4); /* TITEL */ if( !document.getElementById( 'hlmRequestTitle' ) ){ var title= document.createElement( 'div' ); title.id= 'hlmRequestTitle'; title.textContent= 'LIVE SONG REQUEST'; search.parentNode.insertBefore( title, search ); } /* SUCHÜBERSCHRIFT */ if( !document.getElementById( 'hlmSearchLabel' ) ){ var searchLabel= document.createElement( 'label' ); searchLabel.id= 'hlmSearchLabel'; searchLabel.className= 'hlmLabel'; searchLabel.htmlFor= 'searchInput'; searchLabel.textContent= 'SEARCH AVAILABLE SONGS'; search.parentNode.insertBefore( searchLabel, search ); } /* NAME */ if( name && !document.getElementById( 'hlmNameLabel' ) ){ var nameLabel= document.createElement( 'label' ); nameLabel.id= 'hlmNameLabel'; nameLabel.className= 'hlmLabel'; nameLabel.htmlFor= 'nameInput'; nameLabel.textContent= 'YOUR NAME'; name.parentNode.insertBefore( nameLabel, name ); } search.placeholder= 'Search song or artist...'; search.autocomplete= 'off'; if(name){ name.placeholder= 'Your name...'; } buildSuggestions(root); installMobileSearch(search); setTimeout(function(){ cleanMobileTop(root,4); },250); setTimeout(function(){ cleanMobileTop(root,4); },800); } /* MOBILE SUCHE */ function installMobileSearch(search){ if( search.dataset.hlmMobileSearch==='1' ){ return; } search.dataset.hlmMobileSearch='1'; var list= document.getElementById( 'songList' ); if(!list){ return; } function alignAndFit(){ if( !mobile() || document.activeElement!==search ){ return; } var label= document.getElementById( 'hlmSearchLabel' ); if(label){ label.scrollIntoView({ behavior:'auto', block:'start', inline:'nearest' }); } requestAnimationFrame(function(){ var vv= window.visualViewport; var viewportTop= vv ? vv.offsetTop : 0; var viewportBottom= vv ? vv.offsetTop + vv.height : window.innerHeight; var listRect= list.getBoundingClientRect(); var available= Math.floor( viewportBottom - Math.max( listRect.top, viewportTop ) - 6 ); available= Math.max( 70, Math.min( 340, available ) ); list.style.setProperty( 'max-height', available+'px', 'important' ); }); } function startSearch(){ if(!mobile()){ return; } document.documentElement .classList.add( 'hlm-searching' ); requestAnimationFrame( alignAndFit ); setTimeout( alignAndFit, 100 ); setTimeout( alignAndFit, 250 ); setTimeout( alignAndFit, 450 ); } function stopSearch(){ setTimeout(function(){ if( document.activeElement!==search ){ document.documentElement .classList.remove( 'hlm-searching' ); list.style.removeProperty( 'max-height' ); } },150); } search.addEventListener( 'focus', startSearch ); search.addEventListener( 'input', alignAndFit ); search.addEventListener( 'blur', stopSearch ); if(window.visualViewport){ window.visualViewport .addEventListener( 'resize', alignAndFit ); window.visualViewport .addEventListener( 'scroll', alignAndFit ); } else{ window.addEventListener( 'resize', alignAndFit ); } } /* SUGGESTIONS */ function buildSuggestions(root){ var section= document.getElementById( 'hlmSuggestionSection' ); if( section && section.parentNode!==root ){ section.remove(); section=null; } if(section){ return; } section= document.createElement( 'section' ); section.id= 'hlmSuggestionSection'; var title= document.createElement( 'div' ); title.id= 'hlmSuggestionTitle'; title.textContent= 'SUGGESTIONS FOR FUTURE SONGS'; var p1= document.createElement( 'p' ); p1.textContent= 'Your song isn’t on the list? Feel free to suggest it here.'; var p2= document.createElement( 'p' ); p2.textContent= 'I’m a real musician, not a computer, so I can’t play every song on demand. But I’m always adding new songs and will try to include popular suggestions in the future.'; var p3= document.createElement( 'p' ); p3.textContent= 'Thanks for understanding!'; var input= document.createElement( 'input' ); input.id= 'hlmSuggestionInput'; input.type= 'text'; input.placeholder= 'Song title — artist'; input.autocomplete= 'off'; var button= document.createElement( 'button' ); button.id= 'hlmSuggestionButton'; button.type= 'button'; button.textContent= 'SEND SUGGESTION'; var status= document.createElement( 'div' ); status.id= 'hlmSuggestionStatus'; section.appendChild(title); section.appendChild(p1); section.appendChild(p2); section.appendChild(p3); section.appendChild(input); section.appendChild(button); section.appendChild(status); root.appendChild(section); button.addEventListener( 'click', sendSuggestion ); input.addEventListener( 'keydown', function(e){ if(e.key==='Enter'){ e.preventDefault(); sendSuggestion(); } } ); } /* STATUS */ function suggestionStatus( text, error ){ var box= document.getElementById( 'hlmSuggestionStatus' ); if(!box){ return; } box.textContent= text; box.style.setProperty( 'display', 'block', 'important' ); box.style.borderColor= error ? '#c0392b' : '#c5a059'; } /* SENDEN */ function sendSuggestion(){ var input= document.getElementById( 'hlmSuggestionInput' ); if(!input){ return; } var value= input.value.trim(); if(!value){ suggestionStatus( 'Please enter a song title and artist.', true ); input.focus(); return; } if( typeof notifyMusician!== 'function' ){ suggestionStatus( 'Suggestion could not be sent.', true ); return; } var name= document.getElementById( 'nameInput' ); var guest= name && name.value.trim() ? name.value.trim() : 'Anonymous'; notifyMusician( 'SUGGESTION: '+value, guest, '💡 Status: Suggestion for future songs' ); input.value=''; suggestionStatus( 'Thank you — your suggestion has been sent.', false ); } /* START */ if( document.readyState==='loading' ){ document.addEventListener( 'DOMContentLoaded', setup, {once:true} ); } else{ setup(); } window.addEventListener( 'load', setup ); window.addEventListener( 'hashchange', setup ); setTimeout( setup, 250 ); setTimeout( setup, 800 ); })();