package model import "fmt" func GetTemplate1(streamerID int, donatHost, ttsHost string) string { style := `body { margin: 0; padding: 0; height: 100vh; display: flex; justify-content: center; align-items: flex-start; background-color: #000; font-family: Arial, sans-serif; } #content { display: flex; flex-direction: column; align-items: center; width: 100%; max-width: 1920px; margin: 20px auto; gap: 20px; } #content img { width: 100%; height: auto; max-height: 90vh; object-fit: contain; border-radius: 15px; } .text-container, .donation-user { opacity: 0; animation: fadeIn 2s forwards; } .text-container { display: flex; align-items: center; gap: 20px; font-size: 40px; color: #fff; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); } .donation-user { font-size: 35px; color: #FFD700; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); } .donation-text { margin: 0; } .donation-amount { color: #4CAF50; font-weight: bold; padding: 5px 15px; background: rgba(0,0,0,0.7); border-radius: 8px; } @keyframes fadeIn { to { opacity: 1; } }` script := fmt.Sprintf(` let widgetUrl = 'https://%s/api'; let ttsUrl = 'https://%s/api/tts'; function createTextWithAmount(text, amount) { const container = document.createElement('div'); container.className = 'text-container'; const textElem = document.createElement('p'); textElem.className = 'donation-text'; textElem.textContent = text; const amountElem = document.createElement('div'); amountElem.className = 'donation-amount'; amountElem.textContent = amount + '₽'; container.appendChild(textElem); container.appendChild(amountElem); return container; } function createTextElement(text) { const container = document.createElement('div'); container.className = 'text-container'; const textElem = document.createElement('p'); textElem.className = 'donation-text'; textElem.textContent = text; container.appendChild(textElem); return container; } async function getDonatInfo(streamerID) { try { let response = await fetch(widgetUrl + '/widget/get-donat-for-playing/' + streamerID); return await response.json(); } catch (error) { console.error('Fetch error:', error); return null; } } function playAudio(url, volume) { return new Promise((resolve, reject) => { const audio = new Audio(url); audio.volume = volume; audio.play().then(() => { audio.addEventListener('ended', resolve); }).catch(error => { console.error('Error playing audio:', error); reject(error); }); }); } function playSpeech(text, voiceSettings) { return new Promise((resolve, reject) => { if (!voiceSettings.voice_enabled) return resolve(); const requestBody = { text: text, speed: (voiceSettings.voice_speed || 'medium').toLowerCase(), scenery: voiceSettings.scenery || 'default', sound_percent: voiceSettings.voice_sound_percent || 100, min_price: voiceSettings.min_price || 0, languages: voiceSettings.languages || ['ru'] }; fetch(ttsUrl + '/generate', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(requestBody) }) .then(response => { if (!response.ok) throw new Error('TTS error'); return response.blob(); }) .then(blob => { const url = URL.createObjectURL(blob); const audio = new Audio(url); audio.volume = (voiceSettings.voice_sound_percent || 100) / 100; audio.play().catch(reject); audio.addEventListener('ended', () => { URL.revokeObjectURL(url); resolve(); }); }) .catch(reject); }); } async function playMedia(donat, voiceSettings) { try { let mediaPromise = Promise.resolve(); const volume = (voiceSettings.voice_sound_percent || 100) / 100; if (donat.play_content && donat.audio_link) { mediaPromise = playAudio(donat.audio_link, volume) .then(() => playSpeech(donat.text, voiceSettings)); } else if (donat.text && donat.voice_enabled) { mediaPromise = playSpeech(donat.text, voiceSettings); } const timeoutPromise = new Promise(r => setTimeout(r, donat.duration * 1000)); await Promise.race([mediaPromise, timeoutPromise]); } catch (error) { console.error('Media play error:', error); } } function clearContainer(container) { while (container.firstChild) { container.removeChild(container.firstChild); } } async function widgetView() { const streamerID = '%v'; const contentDiv = document.getElementById('content'); if (!contentDiv) { console.error('Content container not found!'); return; } while (true) { const iterationStart = Date.now(); try { const donat = await getDonatInfo(streamerID); if (!donat || Object.keys(donat).length === 0) { await new Promise(r => setTimeout(r, 5000)); continue; } clearContainer(contentDiv); // Добавление изображения if (donat.image_link) { const img = document.createElement('img'); img.src = donat.image_link; contentDiv.appendChild(img); } // Отображение имени пользователя if (donat.show_name && donat.donat_user) { const userElem = document.createElement('div'); userElem.className = 'donation-user'; userElem.textContent = donat.donat_user; contentDiv.appendChild(userElem); } // Отображение текста if (donat.show_text && donat.text) { const textElem = donat.amount ? createTextWithAmount(donat.text, donat.amount) : createTextElement(donat.text); contentDiv.appendChild(textElem); } // Воспроизведение медиа const voiceSettings = { voice_speed: donat.voice_speed, scenery: donat.scenery, voice_sound_percent: donat.voice_sound_percent, min_price: donat.min_price, languages: donat.languages, voice_enabled: donat.voice_enabled }; await playMedia(donat, voiceSettings); // Отправка подтверждения if (donat.order_id) { try { await fetch(widgetUrl + '/widget/donat/viewed', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({order_id: donat.order_id}), }); } catch (error) { console.error('Ошибка подтверждения:', error); } } } catch (error) { console.error('Ошибка обработки доната:', error); } // Пауза между итерациями const elapsed = Date.now() - iterationStart; const remaining = 5000 - elapsed; if (remaining > 0) { await new Promise(r => setTimeout(r, remaining)); } } } document.addEventListener('DOMContentLoaded', widgetView);`, donatHost, ttsHost, streamerID) template := fmt.Sprintf(`
`, style, script) return template }