cmziuk/src/server.ts
Cameron Redmore 6714a4763a
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m51s
Test
2025-08-08 17:49:41 +01:00

135 lines
3.4 KiB
TypeScript

import { Elysia, t } from 'elysia';
import { staticPlugin } from '@elysiajs/static';
import path from 'path';
// Song data interface
interface LastSong {
name: string;
artist: string;
album: string;
url: string;
nowPlaying: boolean;
image: any[];
}
// Current song state
let currentSong: LastSong | null = null;
let lastSongId: string | null = null;
// WebSocket connections
const connections = new Set<any>();
// Function to fetch song from Last.fm
async function fetchLastFmSong(): Promise<LastSong | null> {
const apiKey = Bun.env.LASTFM_API_KEY;
const username = Bun.env.LASTFM_USERNAME;
if (!apiKey || !username) {
console.error('Last.fm API key or username not configured');
return null;
}
const url = `https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${username}&api_key=${apiKey}&format=json&limit=1`;
try {
const response = await fetch(url);
const data = await response.json();
const track = data.recenttracks.track[0];
return {
name: track.name,
artist: track.artist['#text'],
album: track.album['#text'],
url: track.url,
nowPlaying: track['@attr']?.nowplaying === 'true',
image: track.image || [],
};
} catch (error) {
console.error('Error fetching from Last.fm:', error);
return null;
}
}
// Function to check for song changes and notify clients
async function checkAndUpdateSong(): Promise<void> {
const newSong = await fetchLastFmSong();
if (!newSong) return;
// Create a unique identifier for the song
const newSongId = `${newSong.artist}-${newSong.name}-${newSong.nowPlaying}`;
// Check if song has changed
if (newSongId !== lastSongId) {
console.log('Song changed:', newSong.name, 'by', newSong.artist);
currentSong = newSong;
lastSongId = newSongId;
// Notify all connected WebSocket clients
const message = JSON.stringify({
type: 'song-update',
data: newSong
});
connections.forEach(ws => {
try {
ws.send(message);
} catch (error) {
console.error('Error sending to WebSocket client:', error);
connections.delete(ws);
}
});
}
}
// Start polling Last.fm every second
setInterval(checkAndUpdateSong, 1000);
// Initial fetch
checkAndUpdateSong();
// Determine static assets path - adjust for compiled executable
const assetsPath = Bun.env.NODE_ENV === 'production'
? path.join(path.dirname(process.execPath || __dirname), 'dist')
: 'dist';
console.log('Serving static assets from:', assetsPath);
const app = new Elysia()
.use(staticPlugin({
assets: assetsPath,
prefix: ''
}))
.ws('/music', {
message(ws, message) {
// Handle incoming messages if needed
console.log('Received message from client:', message);
},
open(ws) {
console.log('WebSocket client connected');
connections.add(ws);
// Send current song to new client immediately
if (currentSong) {
const message = JSON.stringify({
type: 'song-update',
data: currentSong
});
ws.send(message);
}
},
close(ws) {
console.log('WebSocket client disconnected');
connections.delete(ws);
},
})
.listen({
port: Bun.env.PORT || 3000,
hostname: Bun.env.NODE_ENV === 'production' ? '0.0.0.0' : 'localhost'
});
console.log(
`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}`
);
export type App = typeof app;