// Smooth scrolling functions function scrollToDownload() { document.getElementById('download').scrollIntoView({ behavior: 'smooth' }); } function scrollToFeatures() { document.getElementById('features').scrollIntoView({ behavior: 'smooth' }); } // GitHub repository configuration const GITHUB_REPO = 'timeshiftsauce/CeruMusic'; const GITHUB_API_URL = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`; // Cache for release data let releaseData = null; let releaseDataTimestamp = null; const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes // Download functionality async function downloadApp(platform) { const button = event.target; const originalText = button.innerHTML; // Show loading state button.innerHTML = ` 获取下载链接... `; button.disabled = true; try { // Get latest release data const release = await getLatestRelease(); if (!release) { throw new Error('无法获取最新版本信息'); } // Find the appropriate download asset const downloadUrl = findDownloadAsset(release.assets, platform); if (!downloadUrl) { throw new Error(`暂无 ${getPlatformName(platform)} 版本下载`); } // Show success notification showNotification(`正在下载 ${getPlatformName(platform)} 版本 v${release.tag_name}...`, 'success'); // Start download window.open('https://gh.bugdey.us.kg/'+downloadUrl, '_blank'); // Track download trackDownload(platform, release.tag_name); } catch (error) { console.error('Download error:', error); showNotification(`下载失败: ${error.message}`, 'error'); // Fallback to GitHub releases page setTimeout(() => { showNotification('正在跳转到GitHub下载页面...', 'info'); window.open(`https://gh.bugdey.us.kg/https://github.com/${GITHUB_REPO}/releases/latest`, '_blank'); }, 2000); } finally { // Restore button state setTimeout(() => { button.innerHTML = originalText; button.disabled = false; }, 1500); } } // Get latest release from GitHub API async function getLatestRelease() { // Check cache first const now = Date.now(); if (releaseData && releaseDataTimestamp && (now - releaseDataTimestamp) < CACHE_DURATION) { return releaseData; } try { const response = await fetch(GITHUB_API_URL); if (!response.ok) { throw new Error(`GitHub API error: ${response.status}`); } const data = await response.json(); // Cache the data releaseData = data; releaseDataTimestamp = now; return data; } catch (error) { console.error('Failed to fetch release data:', error); return null; } } // Find appropriate download asset based on platform function findDownloadAsset(assets, platform) { if (!assets || !Array.isArray(assets)) { return null; } // Define file patterns for each platform const patterns = { windows: [ /\.exe$/i, /windows.*\.zip$/i, /win32.*\.zip$/i, /win.*x64.*\.zip$/i ], macos: [ /\.dmg$/i, /darwin.*\.zip$/i, /macos.*\.zip$/i, /mac.*\.zip$/i, /osx.*\.zip$/i ], linux: [ /\.AppImage$/i, /linux.*\.zip$/i, /linux.*\.tar\.gz$/i, /\.deb$/i, /\.rpm$/i ] }; const platformPatterns = patterns[platform] || []; // Try to find exact match for (const pattern of platformPatterns) { const asset = assets.find(asset => pattern.test(asset.name)); if (asset) { return asset.browser_download_url; } } // Fallback: look for any asset that might match the platform const fallbackPatterns = { windows: /win|exe/i, macos: /mac|darwin|dmg/i, linux: /linux|appimage|deb|rpm/i }; const fallbackPattern = fallbackPatterns[platform]; if (fallbackPattern) { const asset = assets.find(asset => fallbackPattern.test(asset.name)); if (asset) { return asset.browser_download_url; } } return null; } function getPlatformName(platform) { const names = { windows: 'Windows', macos: 'macOS', linux: 'Linux' }; return names[platform] || platform; } // Notification system function showNotification(message, type = 'info') { // Remove existing notifications const existingNotifications = document.querySelectorAll('.notification'); existingNotifications.forEach(notification => notification.remove()); // Create notification element const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.innerHTML = `
${message}
`; // Add notification styles if not already added if (!document.querySelector('#notification-styles')) { const styles = document.createElement('style'); styles.id = 'notification-styles'; styles.textContent = ` .notification { position: fixed; top: 90px; right: 20px; background: white; border: 1px solid var(--border); border-radius: var(--border-radius); box-shadow: var(--shadow-lg); z-index: 1001; min-width: 300px; animation: slideInRight 0.3s ease-out; } .notification-info { border-left: 4px solid var(--primary-color); } .notification-success { border-left: 4px solid #10b981; } .notification-error { border-left: 4px solid #ef4444; } .notification-content { display: flex; align-items: center; justify-content: space-between; padding: 1rem; } .notification-message { color: var(--text-primary); font-weight: 500; } .notification-close { background: none; border: none; cursor: pointer; color: var(--text-muted); padding: 0.25rem; border-radius: 4px; transition: var(--transition); } .notification-close:hover { background: var(--surface); color: var(--text-primary); } .notification-close svg { width: 16px; height: 16px; } @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @media (max-width: 480px) { .notification { right: 10px; left: 10px; min-width: auto; } } `; document.head.appendChild(styles); } // Add to page document.body.appendChild(notification); // Auto remove after 5 seconds setTimeout(() => { if (notification.parentElement) { notification.style.animation = 'slideInRight 0.3s ease-out reverse'; setTimeout(() => notification.remove(), 300); } }, 5000); } // Navbar scroll effect function handleNavbarScroll() { const navbar = document.querySelector('.navbar'); if (window.scrollY > 50) { navbar.style.background = 'rgba(255, 255, 255, 0.98)'; navbar.style.boxShadow = 'var(--shadow)'; } else { navbar.style.background = 'rgba(255, 255, 255, 0.95)'; navbar.style.boxShadow = 'none'; } } // Intersection Observer for animations function setupAnimations() { const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.animation = 'fadeInUp 0.6s ease-out forwards'; } }); }, observerOptions); // Observe feature cards and download cards document.querySelectorAll('.feature-card, .download-card').forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(30px)'; observer.observe(card); }); } // Auto-detect user's operating system function detectOS() { const userAgent = navigator.userAgent.toLowerCase(); if (userAgent.includes('win')) return 'windows'; if (userAgent.includes('mac')) return 'macos'; if (userAgent.includes('linux')) return 'linux'; return 'windows'; // default } // Highlight user's OS download option function highlightUserOS() { const userOS = detectOS(); const downloadCards = document.querySelectorAll('.download-card'); downloadCards.forEach((card, index) => { const platforms = ['windows', 'macos', 'linux']; if (platforms[index] === userOS) { card.style.border = '2px solid var(--primary-color)'; card.style.transform = 'scale(1.02)'; // Add "推荐" badge const badge = document.createElement('div'); badge.className = 'recommended-badge'; badge.textContent = '推荐'; badge.style.cssText = ` position: absolute; top: -10px; right: 20px; background: var(--primary-color); color: white; padding: 0.25rem 0.75rem; border-radius: 12px; font-size: 0.75rem; font-weight: 600; `; card.style.position = 'relative'; card.appendChild(badge); } }); } // Keyboard navigation function setupKeyboardNavigation() { document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { // Close notifications document.querySelectorAll('.notification').forEach(notification => { notification.remove(); }); } if (e.key === 'Enter' && e.target.classList.contains('btn')) { e.target.click(); } }); } // Performance optimization: Lazy load images function setupLazyLoading() { const images = document.querySelectorAll('img[data-src]'); const imageObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.removeAttribute('data-src'); imageObserver.unobserve(img); } }); }); images.forEach(img => imageObserver.observe(img)); } // Initialize everything when DOM is loaded document.addEventListener('DOMContentLoaded', async () => { // Setup scroll effects window.addEventListener('scroll', handleNavbarScroll); // Setup animations setupAnimations(); // Highlight user's OS highlightUserOS(); // Setup keyboard navigation setupKeyboardNavigation(); // Setup lazy loading setupLazyLoading(); // Add GitHub links addGitHubLinks(); // Update version information from GitHub await updateVersionInfo(); // Add smooth scrolling to all anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Remove the old download button click handlers since downloadApp now handles everything // The downloadApp function is called directly from the HTML onclick attributes }); // Add spinning animation for loading state const spinningStyles = document.createElement('style'); spinningStyles.textContent = ` .spinning { animation: spin 1s linear infinite; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } `; document.head.appendChild(spinningStyles); // Error handling for failed downloads window.addEventListener('error', (e) => { console.error('页面错误:', e.error); }); // Update version information on page async function updateVersionInfo() { try { const release = await getLatestRelease(); if (release) { const versionElement = document.querySelector('.version'); const versionInfoElement = document.querySelector('.version-info p'); if (versionElement) { versionElement.textContent = release.tag_name; } if (versionInfoElement) { const publishDate = new Date(release.published_at); const formattedDate = publishDate.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long' }); versionInfoElement.innerHTML = `当前版本: ${release.tag_name} | 更新时间: ${formattedDate}`; } // Update download button text with file sizes if available updateDownloadButtonsWithAssets(release.assets); } } catch (error) { console.error('Failed to update version info:', error); } } // Update download buttons with asset information function updateDownloadButtonsWithAssets(assets) { if (!assets || !Array.isArray(assets)) return; const downloadCards = document.querySelectorAll('.download-card'); const platforms = ['windows', 'macos', 'linux']; downloadCards.forEach((card, index) => { const platform = platforms[index]; const asset = findAssetForPlatform(assets, platform); if (asset) { const button = card.querySelector('.btn-download'); const sizeText = formatFileSize(asset.size); const originalText = button.innerHTML; // Add file size info button.innerHTML = originalText.replace(/下载 \.(.*?)$/, `下载 .${getFileExtension(asset.name)} (${sizeText})`); } }); } // Helper function to find asset for platform function findAssetForPlatform(assets, platform) { const patterns = { windows: [/\.exe$/i, /windows.*\.zip$/i, /win32.*\.zip$/i], macos: [/\.dmg$/i, /darwin.*\.zip$/i, /macos.*\.zip$/i], linux: [/\.AppImage$/i, /linux.*\.zip$/i, /\.deb$/i] }; const platformPatterns = patterns[platform] || []; for (const pattern of platformPatterns) { const asset = assets.find(asset => pattern.test(asset.name)); if (asset) return asset; } return null; } // Helper function to get file extension function getFileExtension(filename) { return filename.split('.').pop(); } // Helper function to format file size function formatFileSize(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } // Analytics tracking (placeholder) function trackDownload(platform, version) { // Add your analytics tracking code here console.log(`Download tracked: ${platform} v${version}`); // Example: Google Analytics // gtag('event', 'download', { // 'event_category': 'software', // 'event_label': platform, // 'value': version // }); } // Add GitHub link functionality function addGitHubLinks() { // Add GitHub link to footer if not exists const footerSection = document.querySelector('.footer-section:nth-child(3) ul'); if (footerSection) { const githubLink = document.createElement('li'); githubLink.innerHTML = `GitHub 仓库`; footerSection.appendChild(githubLink); } // Add "查看所有版本" link to download section const versionInfo = document.querySelector('.version-info'); if (versionInfo) { const allVersionsLink = document.createElement('p'); allVersionsLink.innerHTML = `查看所有版本 →`; allVersionsLink.style.marginTop = '1rem'; versionInfo.appendChild(allVersionsLink); } }