PageSourceSearch

https://lyricsgenerator.blog/static/js/main.js

js lyricsgenerator.blog collected 2026-10-08 03:07:17 UTC 14,170 bytes, 388 lines download raw bytes

1/**
2 * AI Song Lyrics Generator - Main JavaScript
3 * Handles lyrics generation, API calls, and result display
4 */
5
6// Global configuration
7const DOMAIN_CONFIG = {
8    API_BASE: 'https://api.lyricsgenerator.blog',
9    FRONTEND_BASE: window.location.origin
10};
11
12const API_ENDPOINTS = {
13    BASE: `${DOMAIN_CONFIG.API_BASE}/api`,
14    LYRICS: {
15        GENERATE: `${DOMAIN_CONFIG.API_BASE}/api/lyrics/generate`,
16        QUOTA: `${DOMAIN_CONFIG.API_BASE}/api/lyrics/quota`,
17        HEALTH: `${DOMAIN_CONFIG.API_BASE}/api/lyrics/health`
18    }
19};
20
21window.DOMAIN_CONFIG = DOMAIN_CONFIG;
22window.API_ENDPOINTS = API_ENDPOINTS;
23
24// Language configuration
25const SUPPORTED_LANGUAGES = ['en', 'es', 'ja', 'fr', 'de', 'pt', 'ko', 'it'];
26const DEFAULT_LANGUAGE = 'en';
27
28function getCurrentLanguage() {
29    const docLang = document.documentElement.lang;
30    if (docLang && SUPPORTED_LANGUAGES.includes(docLang)) {
31        return docLang;
32    }
33    return DEFAULT_LANGUAGE;
34}
35
36window.getCurrentLanguage = getCurrentLanguage;
37
38/**
39 * AI Lyrics Generator Class
40 */
41class LyricsGenerator {
42    constructor() {
43        this.lyricsInput = document.getElementById('lyricsInput');
44        this.styleSelect = document.getElementById('styleSelect');
45        this.moodSelect = document.getElementById('moodSelect');
46        this.structureSelect = document.getElementById('structureSelect');
47        this.generateBtn = document.getElementById('generateBtn');
48        this.trySampleBtn = document.getElementById('trySampleBtn');
49        
50        this.previewDefault = document.getElementById('previewDefault');
51        this.previewLoading = document.getElementById('previewLoading');
52        this.previewResult = document.getElementById('previewResult');
53        this.generatedLyrics = document.getElementById('generatedLyrics');
54        this.resultTitle = document.getElementById('resultTitle');
55        
56        this.copyBtn = document.getElementById('copyBtn');
57        this.downloadBtn = document.getElementById('downloadBtn');
58        this.shareBtn = document.getElementById('shareBtn');
59        this.regenerateBtn = document.getElementById('regenerateBtn');
60        
61        this.lang = getCurrentLanguage();
62        this.lastRequest = null;
63        
64        // Get translations for current language
65        this.translations = window.FRONTEND_TRANSLATIONS?.[this.lang] || window.FRONTEND_TRANSLATIONS?.en || {};
66        
67        // Get sample ideas from translations
68        this.sampleIdeas = window.PAGE_SAMPLE_IDEAS || this.translations.sampleIdeas || [];
69        
70        this.init();
71    }
72
73    init() {
74        if (!this.generateBtn) return;
75
76        this.generateBtn.addEventListener('click', () => this.handleGenerate());
77        
78        if (this.trySampleBtn) {
79            this.trySampleBtn.addEventListener('click', () => this.insertSample());
80        }
81        
82        if (this.regenerateBtn) {
83            this.regenerateBtn.addEventListener('click', () => this.handleGenerate());
84        }
85        
86        if (this.copyBtn) {
87            this.copyBtn.addEventListener('click', () => this.copyLyrics());
88        }
89        
90        if (this.downloadBtn) {
91            this.downloadBtn.addEventListener('click', () => this.downloadLyrics());
92        }
93        
94        if (this.shareBtn) {
95            this.shareBtn.addEventListener('click', () => this.shareLyrics());
96        }
97
98        // Enter key to generate
99        if (this.lyricsInput) {
100            this.lyricsInput.addEventListener('keydown', (e) => {
101                if (e.key === 'Enter' && e.ctrlKey) {
102                    this.handleGenerate();
103                }
104            });
105        }
106    }
107
108    insertSample() {
109        if (!this.lyricsInput) return;
110        const randomIndex = Math.floor(Math.random() * this.sampleIdeas.length);
111        this.lyricsInput.value = this.sampleIdeas[randomIndex];
112        this.lyricsInput.focus();
113    }
114
115    async handleGenerate() {
116        const idea = this.lyricsInput?.value?.trim();
117        const t = this.translations.notifications || {};
118        
119        if (!idea || idea.length < 3) {
120            this.showNotification(t.errorMinLength || 'Please enter a song idea (at least 3 characters)', 'error');
121            return;
122        }
123
124        const mode = window.LYRICS_GENERATOR_MODE || 'song';
125        const style = this.styleSelect?.value || 'auto';
126        const mood = this.moodSelect?.value || 'auto';
127        const structure = this.structureSelect?.value || null;
128
129        this.lastRequest = { idea, style, mood, mode, structure };
130        this.showLoading();
131
132        try {
133            const response = await fetch(API_ENDPOINTS.LYRICS.GENERATE, {
134                method: 'POST',
135                headers: {
136                    'Content-Type': 'application/json',
137                },
138                body: JSON.stringify({
139                    idea: idea,
140                    style: style,
141                    mood: mood,
142                    language: this.lang,
143                    mode: mode,
144                    rapSubtype: mode === 'rap' ? style : undefined,
145                    structure: structure
146                })
147            });
148
149            const result = await response.json();
150
151            if (!result.success) {
152                if (result.error === 'RATE_LIMIT_EXCEEDED') {
153                    this.showNotification(t.rateLimitExceeded || 'Daily limit exceeded. Please try again tomorrow.', 'error');
154                    this.showDefault();
155                    return;
156                }
157                throw new Error(t.errorGeneration || result.message || 'Generation failed');
158            }
159
160            this.displayResult(result.data);
161
162        } catch (error) {
163            console.error('Generation error:', error);
164            this.showNotification(error.message || t.errorGeneration || 'Failed to generate lyrics. Please try again.', 'error');
165            this.showDefault();
166        }
167    }
168
169    showLoading() {
170        if (this.previewDefault) this.previewDefault.style.display = 'none';
171        if (this.previewResult) this.previewResult.style.display = 'none';
172        if (this.previewLoading) this.previewLoading.style.display = 'flex';
173        
174        this.animateLoadingSteps();
175    }
176
177    animateLoadingSteps() {
178        const steps = [
179            document.getElementById('loadStep1'),
180            document.getElementById('loadStep2'),
181            document.getElementById('loadStep3')
182        ];
183
184        steps.forEach((step, index) => {
185            if (step) {
186                step.classList.remove('active', 'step-complete');
187                step.querySelector('i').className = 'fas fa-circle';
188            }
189        });
190
191        let currentStep = 0;
192        const interval = setInterval(() => {
193            if (currentStep < steps.length && steps[currentStep]) {
194                if (currentStep > 0 && steps[currentStep - 1]) {
195                    steps[currentStep - 1].classList.remove('active');
196                    steps[currentStep - 1].classList.add('step-complete');
197                    steps[currentStep - 1].querySelector('i').className = 'fas fa-check-circle';
198                }
199                steps[currentStep].classList.add('active');
200                steps[currentStep].querySelector('i').className = 'fas fa-circle-notch fa-spin';
201                currentStep++;
202            } else {
203                clearInterval(interval);
204            }
205        }, 2000);
206
207        this.loadingInterval = interval;
208    }
209
210    showDefault() {
211        if (this.loadingInterval) clearInterval(this.loadingInterval);
212        if (this.previewLoading) this.previewLoading.style.display = 'none';
213        if (this.previewResult) this.previewResult.style.display = 'none';
214        if (this.previewDefault) this.previewDefault.style.display = 'flex';
215    }
216
217    displayResult(data) {
218        if (this.loadingInterval) clearInterval(this.loadingInterval);
219        
220        if (this.previewLoading) this.previewLoading.style.display = 'none';
221        if (this.previewDefault) this.previewDefault.style.display = 'none';
222        if (this.previewResult) this.previewResult.style.display = 'flex';
223
224        // Set title
225        if (this.resultTitle) {
226            this.resultTitle.textContent = data.title || 'Your Generated Song';
227        }
228
229        // Display lyrics
230        if (this.generatedLyrics && data.sections) {
231            let html = '';
232            for (const section of data.sections) {
233                html += `<p class="section-label">${this.escapeHtml(section.label)}</p>`;
234                for (const line of section.lines) {
235                    html += `<p class="lyrics-line">${this.escapeHtml(line)}</p>`;
236                }
237            }
238            this.generatedLyrics.innerHTML = html;
239        } else if (this.generatedLyrics && data.lyrics) {
240            // Fallback to plain lyrics
241            const lines = data.lyrics.split('\n');
242            let html = '';
243            for (const line of lines) {
244                if (line.startsWith('[') && line.endsWith(']')) {
245                    html += `<p class="section-label">${this.escapeHtml(line.slice(1, -1))}</p>`;
246                } else if (line.trim()) {
247                    html += `<p class="lyrics-line">${this.escapeHtml(line)}</p>`;
248                }
249            }
250            this.generatedLyrics.innerHTML = html;
251        }
252
253        // Store for copy/download
254        this.currentLyrics = data;
255    }
256
257    escapeHtml(text) {
258        const div = document.createElement('div');
259        div.textContent = text;
260        return div.innerHTML;
261    }
262
263    copyLyrics() {
264        if (!this.currentLyrics) return;
265        const t = this.translations.notifications || {};
266        
267        let text = `${this.currentLyrics.title || 'Generated Song'}\n\n`;
268        
269        if (this.currentLyrics.sections) {
270            for (const section of this.currentLyrics.sections) {
271                text += `[${section.label}
271]\n`;
272                text += section.lines.join('\n') + '\n\n';
273            }
274        } else if (this.currentLyrics.lyrics) {
275            text += this.currentLyrics.lyrics;
276        }
277
278        navigator.clipboard.writeText(text.trim()).then(() => {
279            this.showNotification(t.copySuccess || 'Lyrics copied to clipboard!', 'success');
280        }).catch(() => {
281            this.showNotification(t.copyError || 'Failed to copy lyrics', 'error');
282        });
283    }
284
285    downloadLyrics() {
286        if (!this.currentLyrics) return;
287        const t = this.translations.notifications || {};
288        
289        let text = `${this.currentLyrics.title || 'Generated Song'}\n`;
290        text += `Style: ${this.currentLyrics.style || 'N/A'}\n`;
291        text += `Mood: ${this.currentLyrics.mood || 'N/A'}\n\n`;
292        
293        if (this.currentLyrics.sections) {
294            for (const section of this.currentLyrics.sections) {
295                text += `[${section.label}]\n`;
296                text += section.lines.join('\n') + '\n\n';
297            }
298        } else if (this.currentLyrics.lyrics) {
299            text += this.currentLyrics.lyrics;
300        }
301
302        const blob = new Blob([text.trim()], { type: 'text/plain' });
303        const url = URL.createObjectURL(blob);
304        const a = document.createElement('a');
305        a.href = url;
306        a.download = `${(this.currentLyrics.title || 'lyrics').replace(/[^a-z0-9]/gi, '_')}.txt`;
307        a.click();
308        URL.revokeObjectURL(url);
309        
310        this.showNotification(t.downloadSuccess || 'Lyrics downloaded!', 'success');
311    }
312
313    shareLyrics() {
314        const t = this.translations.share || {};
315        if (navigator.share) {
316            navigator.share({
317                title: this.currentLyrics?.title || t.title || 'AI Generated Lyrics',
318                text: t.text || 'Check out these AI generated song lyrics!',
319                url: window.location.href
320            }).catch(() => {});
321        } else {
322            this.copyLyrics();
323        }
324    }
325
326    showNotification(message, type = 'info') {
327        const alertClass = type === 'error' ? 'alert-danger' : type === 'success' ? 'alert-success' : 'alert-info';
328        const notification = document.createElement('div');
329        notification.className = `alert ${alertClass}`;
330        notification.textContent = message;
331        notification.style.cssText = `
332            position: fixed; top: 20px; right: 20px; z-index: 9999;
333            min-width: 280px; padding: 12px 20px; border-radius: 8px;
334            animation: slideIn 0.3s ease-out;
335        `;
336        
337        document.body.appendChild(notification);
338        
339        setTimeout(() => {
340            notification.style.animation = 'slideOut 0.3s ease-out';
341            setTimeout(() => notification.remove(), 300);
342        }, 3000);
343    }
344}
345
346// Notification animations
347const style = document.createElement('style');
348style.textContent = `
349    @keyframes slideIn {
350        from { transform: translateX(400px); opacity: 0; }
351        to { transform: translateX(0); opacity: 1; }
352    }
353    @keyframes slideOut {
354        from { transform: translateX(0); opacity: 1; }
355        to { transform: translateX(400px); opacity: 0; }
356    }
357`;
358document.head.appendChild(style);
359
360// Initialize on DOM ready
361document.addEventListener('DOMContentLoaded', function() {
362    // Initialize lyrics generator
363    new LyricsGenerator();
364
365    // Smooth scrolling for anchor links
366    const navLinks = document.querySelectorAll('a[href^="#"]');
367    navLinks.forEach(link => {
368        link.addEventListener('click', function(e) {
369            const href = this.getAttribute('href');
370            if (href === '#' || href === '') return;
371
372            e.preventDefault();
373            const targetId = href.substring(1);
374            const targetElement = document.getElementById(targetId);
375
376            if (targetElement) {
377                const navbar = document.querySelector('.navbar');
378                const navbarHeight = navbar ? navbar.offsetHeight : 0;
379                const targetPosition = targetElement.offsetTop - navbarHeight - 20;
380
381                window.scrollTo({
382                    top: targetPosition,
383                    behavior: 'smooth'
384                });
385            }
386        });
387    });
388});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.