const express = require('express'); const path = require('path'); const fs = require('fs'); const QRCode = require('qrcode'); const app = express(); const PORT = process.env.PORT || 3000; // Function to read current version from main.h function getCurrentVersion() { try { const mainHPath = path.join(__dirname, '../include/main.h'); const content = fs.readFileSync(mainHPath, 'utf8'); const versionMatch = content.match(/#define\s+MICE_VERSION\s+"([^"]+)"/); return versionMatch ? versionMatch[1] : 'unknown'; } catch (error) { console.log('Could not read version:', error.message); return 'unknown'; } } // Function to get build timestamp function getBuildTimestamp() { try { const outputDir = path.join(__dirname, '../output'); if (fs.existsSync(outputDir)) { const stats = fs.statSync(outputDir); return stats.mtime.toLocaleString(); } } catch (error) { console.log('Could not read build timestamp:', error.message); } return 'unknown'; } // Serve static files from the output directory app.use('/downloads', express.static(path.join(__dirname, '../output'))); // Basic styling for the web interface with dark mode support const CSS = ` `; // Main page app.get('/', async (req, res) => { const outputDir = path.join(__dirname, '../output'); // Use the client's actual request URL const protocol = req.get('x-forwarded-proto') || req.protocol || 'http'; const host = req.get('x-forwarded-host') || req.get('host'); const baseUrl = `${protocol}://${host}`; // Check if build files exist const files = []; const fileExtensions = ['.3dsx', '.cia', '.3ds', '.elf']; try { const items = fs.readdirSync(outputDir, { recursive: true }); for (const item of items) { const itemPath = path.join(outputDir, item); const stats = fs.statSync(itemPath); if (stats.isFile()) { const ext = path.extname(item).toLowerCase(); if (fileExtensions.includes(ext)) { const filePath = `/downloads/${item}`; const fileUrl = `${baseUrl}${filePath}`; files.push({ name: item, path: filePath, url: fileUrl, size: (stats.size / 1024).toFixed(1), // KB type: ext.substring(1).toUpperCase() }); } } } } catch (err) { console.error('Error reading output directory:', err); } let html = ` mice - 3DS Music Player Downloads ${CSS}

šŸŽµ mice - 3DS Music Player šŸŽµ

Current Version: ${getCurrentVersion()}
Last Build: ${getBuildTimestamp()}

šŸ“± Installation Instructions:

For .3dsx files (Homebrew Launcher):

For .cia files (installed to HOME menu):

`; if (files.length === 0) { html += `

āš ļø No build files found

Please run make in the mice directory to build the project first.

`; } else { html += `

šŸ“¦ Available Downloads:

`; for (const file of files) { // Generate QR code for each file let qrCodeDataUrl = ''; try { qrCodeDataUrl = await QRCode.toDataURL(file.url); } catch (err) { console.error('Error generating QR code:', err); } html += `

šŸŽ® ${file.name}

Type: ${file.type}    Size: ${file.size} KB
šŸ“„ Download ${file.type} ${qrCodeDataUrl ? `

šŸ“± Scan QR Code for 3DS Browser:

QR Code for ${file.name}
${file.url}
` : ''}
`; } } html += `

šŸŽµ Features:

šŸ”— Access Information:

Server running at: ${baseUrl}

${baseUrl.includes('github.dev') ? '

āœ… GitHub Codespaces detected - URL is ready for 3DS browser!

' : '

šŸ’” For 3DS access, use your local network IP address

' }
`; res.send(html); }); // API endpoint for version info app.get('/api/version', (req, res) => { res.json({ version: getCurrentVersion(), buildTime: getBuildTimestamp(), timestamp: new Date().toISOString() }); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // Start server app.listen(PORT, '0.0.0.0', () => { console.log(`šŸŽµ mice Download Server`); // Detect if we're in GitHub Codespaces const codespace = process.env.CODESPACE_NAME; if (codespace) { const codespacesUrl = `https://${codespace}-${PORT}.app.github.dev/`; console.log(`🌐 GitHub Codespaces URL: ${codespacesUrl}`); console.log(`šŸ“± Use this URL for 3DS browser access!`); } else { console.log(`🌐 Server running at: http://localhost:${PORT}`); console.log(`šŸ“± Access from 3DS browser using your local IP`); } console.log(`šŸ“¦ Serving files from: ${path.join(__dirname, '../output')}`); console.log(`\nšŸ”— To find your local IP address:`); console.log(` - Windows: ipconfig`); console.log(` - Mac/Linux: ifconfig or ip addr show`); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('šŸ›‘ Server shutting down...'); process.exit(0); }); module.exports = app;