feat: Enhance iOS Safari audio streaming support with optimizations and specific headers
All checks were successful
Build and Push Docker Image / docker (push) Successful in 9s

This commit is contained in:
2025-10-19 23:18:25 +02:00
parent 18d14b097d
commit 70be1e7e39
11 changed files with 137 additions and 1328 deletions

View File

@@ -1,371 +0,0 @@
# Hitstar Backend Migration Guide
## Node.js to Deno 2 + TypeScript
This document provides comprehensive information about the new Deno backend rewrite.
## Overview
The new backend has been completely rewritten using:
- **Deno 2** - Modern JavaScript/TypeScript runtime
- **TypeScript** - Full type safety
- **Clean Architecture** - Clear separation of concerns
- **SOLID Principles** - Maintainable and extensible code
## Architecture
### Layer Structure
```
src/server-deno/
├── domain/ # Core business logic (no dependencies)
│ ├── models/ # Domain models (Player, Room, GameState)
│ └── types.ts # TypeScript interfaces and types
├── application/ # Use cases and orchestration
│ ├── AnswerCheckService.ts # Fuzzy matching for guesses
│ ├── GameService.ts # Game flow and logic
│ ├── RoomService.ts # Room and player management
│ └── TrackService.ts # Playlist and track loading
├── infrastructure/ # External concerns
│ ├── FileSystemService.ts # File operations
│ ├── TokenStoreService.ts # Audio streaming tokens
│ ├── CoverArtService.ts # Cover art extraction
│ ├── MetadataService.ts # Audio metadata parsing
│ ├── AudioStreamingService.ts # Audio streaming with range support
│ └── MimeTypeService.ts # MIME type detection
├── presentation/ # API layer
│ ├── HttpServer.ts # Oak HTTP server
│ ├── WebSocketServer.ts # Socket.IO game server
│ └── routes/ # HTTP route handlers
├── shared/ # Cross-cutting concerns
│ ├── config.ts # Configuration management
│ ├── constants.ts # Application constants
│ ├── errors.ts # Custom error types
│ ├── logger.ts # Logging utilities
│ └── utils.ts # Helper functions
└── main.ts # Application entry point
```
### Design Patterns Used
1. **Clean Architecture**
- Domain layer has no external dependencies
- Dependencies point inward (dependency inversion)
- Business logic isolated from frameworks
2. **Dependency Injection**
- Services injected through constructors
- Easy to test and swap implementations
3. **Repository Pattern**
- RoomService acts as repository for rooms/players
- Abstracts storage mechanism
4. **Service Layer**
- Business logic in services
- Controllers are thin
5. **Strategy Pattern**
- AnswerCheckService uses different matching strategies
## Key Improvements
### Code Quality
- ✅ Full TypeScript type safety
- ✅ Explicit error handling
- ✅ Consistent naming conventions
- ✅ Comprehensive JSDoc comments
- ✅ SOLID principles throughout
### Architecture
- ✅ Clear separation of concerns
- ✅ Testable code structure
- ✅ Dependency injection
- ✅ No circular dependencies
- ✅ Single responsibility principle
### Features
- ✅ Audio streaming with range support
- ✅ Token-based audio URLs (security)
- ✅ Cover art extraction and caching
- ✅ Playlist management
- ✅ Fuzzy matching for answers
- ✅ Real-time game sync
## Migration from Old Backend
### Key Differences
| Aspect | Old (Node.js) | New (Deno) |
|--------|--------------|------------|
| Runtime | Node.js | Deno 2 |
| Language | JavaScript | TypeScript |
| Module System | ESM + package.json | Deno imports |
| HTTP Framework | Express | Oak |
| File Access | fs module | Deno.* APIs |
| Permissions | None | Explicit flags |
| Type Safety | None | Full |
### API Compatibility
The new backend maintains **100% API compatibility** with the old one:
#### HTTP Endpoints
-`GET /api/playlists` - List playlists
-`GET /api/tracks?playlist=<id>` - Get tracks
-`GET /api/reload-years?playlist=<id>` - Reload years
-`HEAD /audio/t/:token` - Check audio
-`GET /audio/t/:token` - Stream audio
-`GET /cover/:name` - Get cover art
#### WebSocket Events
- ✅ All original events supported
- ✅ Same message format
- ✅ Compatible with existing client
## Running the New Backend
### Prerequisites
- Deno 2.x installed ([deno.land](https://deno.land))
- Audio files in `data/` directory
- Optional: `years.json` metadata files
### Installation
No dependencies to install! Deno handles everything automatically.
### Configuration
1. Copy `.env.example` to `.env`:
```bash
cp src/server-deno/.env.example src/server-deno/.env
```
2. Edit `.env` as needed:
```env
PORT=5173
DATA_DIR=./data
PUBLIC_DIR=./public
LOG_LEVEL=INFO
```
### Running
**Development mode** (with auto-reload):
```bash
cd src/server-deno
deno task dev
```
**Production mode**:
```bash
cd src/server-deno
deno task start
```
**Run tests**:
```bash
deno task test
```
**Format code**:
```bash
deno task fmt
```
**Lint code**:
```bash
deno task lint
```
### Permissions
The new backend requires these Deno permissions:
- `--allow-net` - HTTP server and network access
- `--allow-read` - Read audio files and config
- `--allow-env` - Read environment variables
- `--allow-write` - Write logs (optional)
These are already configured in `deno.json` tasks.
## Development Guide
### Adding a New Feature
1. **Define domain types** in `domain/types.ts`
2. **Create service** in appropriate layer
3. **Add routes** in `presentation/routes/`
4. **Wire up in** `main.ts`
5. **Write tests** (optional but recommended)
### Example: Adding New API Endpoint
```typescript
// 1. Add route in presentation/routes/customRoutes.ts
export function createCustomRoutes(service: CustomService): Router {
const router = new Router();
router.get('/api/custom', async (ctx: Context) => {
const result = await service.doSomething();
ctx.response.body = { ok: true, result };
});
return router;
}
// 2. Wire up in HttpServer.ts
const customRoutes = createCustomRoutes(customService);
router.use(customRoutes.routes(), customRoutes.allowedMethods());
```
### Testing
Create test files with `_test.ts` suffix:
```typescript
// services/AnswerCheckService_test.ts
import { assertEquals } from '@std/assert';
import { AnswerCheckService } from './AnswerCheckService.ts';
Deno.test('scoreTitle - exact match', () => {
const service = new AnswerCheckService();
const result = service.scoreTitle('Hello World', 'Hello World');
assertEquals(result.match, true);
assertEquals(result.score, 1.0);
});
```
Run with: `deno task test`
## Troubleshooting
### Common Issues
**Issue**: Module not found errors in IDE
- **Solution**: Run `deno cache main.ts` to download dependencies
**Issue**: Permission denied errors
- **Solution**: Check Deno permissions in task commands
**Issue**: Audio files not streaming
- **Solution**: Verify DATA_DIR path in `.env`
**Issue**: Port already in use
- **Solution**: Change PORT in `.env` or stop old server
### Debugging
Enable debug logging:
```env
LOG_LEVEL=DEBUG
```
Check logs for detailed information about requests, errors, and game events.
## Performance
### Optimizations Implemented
1. **LRU Caching**
- Audio tokens cached
- Cover art cached
- Configurable limits
2. **Batch Processing**
- Metadata parsed in batches
- Prevents "too many open files"
3. **Streaming**
- Audio streamed with range support
- Efficient memory usage
4. **Connection Pooling**
- WebSocket connections reused
- Minimal overhead
### Benchmarks
*TODO: Add performance benchmarks comparing old vs new backend*
## Security
### Improvements
1. **Path Traversal Protection**
- All file paths validated
- Restricted to data directory
2. **Token-Based Audio URLs**
- Short-lived tokens (10 min default)
- No filename exposure in URLs
3. **Input Validation**
- Type checking at boundaries
- Sanitized user input
4. **CORS Configuration**
- Configurable origins
- Secure defaults
## Future Improvements
### Planned Features
- [ ] Database integration (SQLite/PostgreSQL)
- [ ] User authentication
- [ ] OpenAPI/Swagger documentation
- [ ] Comprehensive test suite
- [ ] Performance monitoring
- [ ] Rate limiting
- [ ] WebSocket authentication
- [ ] Player statistics tracking
- [ ] Replay system
### Potential Enhancements
- [ ] GraphQL API option
- [ ] Redis caching layer
- [ ] Multi-room tournament mode
- [ ] Achievements system
- [ ] Leaderboards
- [ ] Custom game modes
- [ ] AI opponent
## Contributing
### Code Style
- Use TypeScript strict mode
- Follow existing patterns
- Add JSDoc comments
- Keep functions small and focused
- Prefer composition over inheritance
### Commit Guidelines
- Use conventional commits
- Reference issues in commits
- Keep commits atomic
### Pull Request Process
1. Create feature branch
2. Implement changes
3. Add tests
4. Update documentation
5. Submit PR with description
## Support
For questions or issues:
- Check this documentation
- Review code comments
- Ask in project discussions
## License
*Same as main project*

View File

@@ -1,297 +0,0 @@
# Hitstar Backend Rewrite - Summary
## What Was Done
I've successfully rewritten your entire Hitstar backend from Node.js/JavaScript to Deno 2/TypeScript following modern software architecture principles and best practices.
## Complete File Structure Created
```
src/server-deno/
├── deno.json # Deno configuration and tasks
├── .env.example # Environment variables template
├── .gitignore # Git ignore rules
├── README.md # Project overview
├── MIGRATION_GUIDE.md # Comprehensive migration guide
├── QUICK_START.md # 5-minute quick start guide
├── main.ts # Application entry point
├── domain/ # Domain Layer (Business Logic)
│ ├── types.ts # Core type definitions
│ └── models/
│ ├── Player.ts # Player domain model
│ ├── GameState.ts # Game state domain model
│ ├── Room.ts # Room domain model
│ └── mod.ts # Model exports
├── application/ # Application Layer (Use Cases)
│ ├── AnswerCheckService.ts # Fuzzy matching for title/artist/year
│ ├── GameService.ts # Game flow orchestration
│ ├── RoomService.ts # Room and player management
│ ├── TrackService.ts # Playlist and track operations
│ └── mod.ts # Service exports
├── infrastructure/ # Infrastructure Layer (External Concerns)
│ ├── FileSystemService.ts # File operations with security
│ ├── TokenStoreService.ts # Audio streaming token management
│ ├── CoverArtService.ts # Cover art extraction and caching
│ ├── MetadataService.ts # Audio metadata parsing
│ ├── AudioStreamingService.ts # HTTP range streaming
│ ├── MimeTypeService.ts # MIME type detection
│ └── mod.ts # Infrastructure exports
├── presentation/ # Presentation Layer (API)
│ ├── HttpServer.ts # Oak HTTP server setup
│ ├── WebSocketServer.ts # Socket.IO game server
│ └── routes/
│ ├── trackRoutes.ts # Playlist/track endpoints
│ └── audioRoutes.ts # Audio streaming endpoints
└── shared/ # Shared Utilities
├── config.ts # Configuration loader
├── constants.ts # Application constants
├── errors.ts # Custom error types
├── logger.ts # Logging utilities
└── utils.ts # Helper functions
```
**Total: 30+ TypeScript files, ~3000+ lines of well-structured code**
## Architecture Highlights
### Clean Architecture ✅
- **Domain Layer**: Pure business logic, no dependencies
- **Application Layer**: Use cases and orchestration
- **Infrastructure Layer**: External systems (file system, caching)
- **Presentation Layer**: HTTP/WebSocket APIs
### SOLID Principles ✅
- **Single Responsibility**: Each class has one job
- **Open/Closed**: Extensible without modification
- **Liskov Substitution**: Proper inheritance
- **Interface Segregation**: Focused interfaces
- **Dependency Inversion**: Depend on abstractions
### Design Patterns ✅
- **Dependency Injection**: Constructor-based DI
- **Repository Pattern**: RoomService as data store
- **Service Layer**: Business logic separation
- **Strategy Pattern**: Flexible answer checking
- **Factory Pattern**: Player/Room creation
## Key Features Implemented
### Core Game Logic
- ✅ Room creation and management
- ✅ Player session handling with resume capability
- ✅ Turn-based gameplay
- ✅ Fuzzy answer matching (title/artist/year)
- ✅ Timeline card placement
- ✅ Token/coin system
- ✅ Win condition checking
- ✅ Spectator mode
- ✅ Game pause/resume
### Audio System
- ✅ Secure token-based streaming
- ✅ HTTP range request support (seeking)
- ✅ .opus preference for bandwidth
- ✅ Cover art extraction and caching
- ✅ Metadata parsing (music-metadata)
- ✅ Path traversal protection
### Playlist Management
- ✅ Multiple playlist support
- ✅ Default and custom playlists
- ✅ Track loading with metadata
- ✅ Years.json integration
- ✅ Batch processing for performance
### Real-Time Communication
- ✅ Socket.IO integration
- ✅ Room state broadcasting
- ✅ Time synchronization
- ✅ Player reconnection
- ✅ Session resumption
## API Compatibility
**100% backward compatible** with existing client!
### HTTP Endpoints
- `GET /api/playlists`
- `GET /api/tracks?playlist=<id>`
- `GET /api/reload-years?playlist=<id>`
- `HEAD /audio/t/:token`
- `GET /audio/t/:token` (with Range support)
- `GET /cover/:name`
- Static file serving
### WebSocket Events
All original events supported:
- create_room, join_room, leave_room
- set_name, ready, start_game
- guess, pause, resume_play, skip_track
- set_spectator, kick_player
- And all server-to-client events
## Code Quality Improvements
### Type Safety
- ✅ Full TypeScript strict mode
- ✅ Explicit types everywhere
- ✅ No `any` types (except where necessary)
- ✅ Compile-time error checking
### Error Handling
- ✅ Custom error classes
- ✅ Proper error propagation
- ✅ Try-catch blocks
- ✅ Meaningful error messages
### Documentation
- ✅ JSDoc comments on all public APIs
- ✅ Inline code comments
- ✅ README files
- ✅ Migration guide
- ✅ Quick start guide
### Testing Ready
- ✅ Dependency injection for mocking
- ✅ Pure functions where possible
- ✅ Deno test structure ready
- ✅ Easy to add unit tests
## Major Improvements Over Old Backend
| Aspect | Old Backend | New Backend |
|--------|-------------|-------------|
| **Language** | JavaScript | TypeScript |
| **Runtime** | Node.js | Deno 2 |
| **Type Safety** | None | Full |
| **Architecture** | Mixed concerns | Clean Architecture |
| **Testability** | Difficult | Easy (DI) |
| **Dependencies** | npm packages | Deno imports |
| **Setup Time** | npm install (~2 min) | Instant |
| **Code Size** | ~1500 lines | ~3000 lines (but cleaner) |
| **Maintainability** | Medium | High |
| **Extensibility** | Coupled | Decoupled |
| **Security** | Basic | Enhanced |
| **Performance** | Good | Better (Deno runtime) |
## Security Enhancements
1. **Path Traversal Protection**: All file paths validated
2. **Token-Based URLs**: Short-lived tokens (10 min)
3. **Permission System**: Explicit Deno permissions
4. **Input Validation**: Type checking at boundaries
5. **No Filename Exposure**: Opaque tokens only
6. **CORS Control**: Configurable origins
## Performance Optimizations
1. **LRU Caching**: Tokens and cover art
2. **Batch Processing**: Metadata parsing
3. **Streaming**: Efficient memory usage
4. **Range Requests**: Partial content support
5. **Deno Runtime**: Faster than Node.js
## How to Run
### Quick Start (5 minutes)
```bash
cd src/server-deno
deno task dev
```
### Available Commands
```bash
deno task dev # Development with hot reload
deno task start # Production mode
deno task test # Run tests
deno task lint # Lint code
deno task fmt # Format code
deno task check # Type check
```
### Configuration
Edit `.env`:
```env
PORT=5173
HOST=0.0.0.0
DATA_DIR=../../data
PUBLIC_DIR=../../public
LOG_LEVEL=INFO
```
## What's Next? (Your Decision)
### Integration Options
1. **Side-by-side**: Run both backends during transition
2. **Gradual migration**: Move features one by one
3. **Complete switch**: Replace old backend entirely
### Testing Recommendation
1. Run new backend on different port
2. Test all features thoroughly
3. Compare with old backend
4. Fix any compatibility issues
5. Switch production traffic
### Potential Enhancements (Future)
**Nice to Have:**
- [ ] Add comprehensive test suite
- [ ] Set up database (SQLite/PostgreSQL)
- [ ] Add authentication system
- [ ] OpenAPI/Swagger docs
- [ ] CI/CD pipeline
- [ ] Docker container
- [ ] Performance monitoring
- [ ] Rate limiting
**Framework Note:**
I kept Socket.IO as requested, but note that Deno has native WebSocket support that could be used as an alternative in the future.
## Important Notes
### Socket.IO Integration
The WebSocket server is implemented but needs Socket.IO Deno package setup. The current Socket.IO Deno package may need additional configuration or you might want to consider using Deno's native WebSocket API.
### Testing
The code structure makes it easy to add tests. Each service can be tested independently due to dependency injection.
### Migration Path
The old backend (`src/server/`) is unchanged. You can run both simultaneously on different ports for testing.
## Questions I Can Help With
1. **Framework choices**: Do you want to use Socket.IO or switch to native WebSockets?
2. **Database**: Should we add a database layer (SQLite/PostgreSQL)?
3. **Authentication**: Do you need user authentication?
4. **Testing**: Want me to add unit tests?
5. **Docker**: Need a Dockerfile for deployment?
6. **Documentation**: Need OpenAPI/Swagger docs?
## Files You Should Review First
1. `src/server-deno/QUICK_START.md` - Get running in 5 minutes
2. `src/server-deno/README.md` - Architecture overview
3. `src/server-deno/MIGRATION_GUIDE.md` - Complete documentation
4. `src/server-deno/main.ts` - Entry point
5. `src/server-deno/domain/types.ts` - Core types
## Summary
**Complete backend rewrite** - Deno 2 + TypeScript
**Clean Architecture** - Proper separation of concerns
**100% API compatible** - Works with existing client
**Type safe** - Full TypeScript strict mode
**Well documented** - README, guides, comments
**Ready to run** - `deno task dev`
**Production ready** - Security, performance, error handling
**Maintainable** - SOLID, DI, clean code
**Extensible** - Easy to add features
The new backend is a significant improvement in code quality, maintainability, and follows industry best practices. It's ready for you to test and deploy! 🚀

View File

@@ -1,155 +0,0 @@
# Quick Start Guide - Deno Backend
Get the new Deno backend running in 5 minutes!
## Prerequisites
1. **Install Deno** (if not already installed):
```bash
# Windows (PowerShell)
irm https://deno.land/install.ps1 | iex
# macOS/Linux
curl -fsSL https://deno.land/install.sh | sh
```
2. **Verify installation**:
```bash
deno --version
```
## Setup
1. **Navigate to the new backend**:
```bash
cd src/server-deno
```
2. **Create environment file** (optional):
```bash
cp .env.example .env
```
Default settings work out of the box!
3. **Run the server**:
```bash
deno task dev
```
That's it! The server will start on `http://localhost:5173`
## What Just Happened?
Deno automatically:
- ✅ Downloaded all dependencies
- ✅ Compiled TypeScript
- ✅ Started the HTTP server
- ✅ Enabled hot reload
No `npm install` needed! 🎉
## Testing the Server
### Check Server Health
```bash
# Get playlists
curl http://localhost:5173/api/playlists
# Get tracks
curl http://localhost:5173/api/tracks?playlist=default
```
### Access from Browser
Open `http://localhost:5173` in your browser to use the web interface.
## Available Commands
```bash
# Development (with auto-reload)
deno task dev
# Production
deno task start
# Run tests
deno task test
# Format code
deno task fmt
# Lint code
deno task lint
# Type check
deno task check
```
## Project Structure
```
src/server-deno/
├── main.ts # Start here!
├── domain/ # Business logic
├── application/ # Services
├── infrastructure/ # File system, streaming
├── presentation/ # HTTP & WebSocket
└── shared/ # Utilities
```
## Configuration
Edit `.env` to customize:
```env
PORT=5173 # Server port
HOST=0.0.0.0 # Server host
DATA_DIR=../../data # Audio files location
PUBLIC_DIR=../../public # Static files location
LOG_LEVEL=INFO # Logging level
```
## Troubleshooting
### Port Already in Use
```bash
# Change port in .env
PORT=3000
```
### Audio Files Not Found
```bash
# Update data directory in .env
DATA_DIR=./data
```
### Module Errors
```bash
# Clear cache and retry
deno cache --reload main.ts
```
## Next Steps
1. **Read the full documentation**: `MIGRATION_GUIDE.md`
2. **Explore the code**: Start with `main.ts`
3. **Check the architecture**: Review layer structure
4. **Run tests**: `deno task test`
## Getting Help
- Check the `README.md` for architecture overview
- See `MIGRATION_GUIDE.md` for detailed documentation
- Review code comments for implementation details
## Comparison with Old Backend
| Feature | Old (Node.js) | New (Deno) |
|---------|--------------|------------|
| Setup time | `npm install` (~2 min) | Instant |
| Dependencies | node_modules/ (100+ MB) | Cached (~10 MB) |
| Type safety | None | Full TypeScript |
| Hot reload | nodemon | Built-in |
| Security | Manual | Permissions-based |
Enjoy the new backend! 🚀

View File

@@ -1,75 +0,0 @@
# Hitstar Backend - Deno 2 + TypeScript
Modern backend rewrite using Deno 2 and TypeScript with clean architecture principles.
## Architecture
This backend follows **Clean Architecture** principles with clear separation of concerns:
```
src/server-deno/
├── domain/ # Domain models, types, and business rules (no dependencies)
├── application/ # Use cases and application services
├── infrastructure/ # External concerns (file system, networking, etc.)
├── presentation/ # HTTP routes, WebSocket handlers
├── shared/ # Shared utilities, constants, types
└── main.ts # Application entry point and DI setup
```
### Layers
1. **Domain Layer**: Pure business logic and types
- No external dependencies
- Models: Player, Room, Track, GameState
- Domain services for core game logic
2. **Application Layer**: Use cases and orchestration
- Game service (start game, process guesses, etc.)
- Track service (load tracks, manage playlists)
- Room service (create/join rooms, manage players)
3. **Infrastructure Layer**: External concerns
- File system operations
- Audio streaming
- Token management
- Metadata parsing
4. **Presentation Layer**: API and WebSocket
- REST routes for HTTP endpoints
- Socket.IO handlers for real-time game
## Running
### Development
```bash
deno task dev
```
### Production
```bash
deno task start
```
### Testing
```bash
deno task test
deno task test:watch
```
### Code Quality
```bash
deno task lint
deno task fmt
deno task check
```
## Dependencies
- **@oak/oak**: Modern HTTP framework for Deno
- **socket.io**: Real-time bidirectional event-based communication
- **music-metadata**: Audio file metadata parsing
- **lru-cache**: Token and cover art caching
## Environment Variables
See `.env.example` for all available configuration options.

View File

@@ -1,229 +0,0 @@
# Quick Reference - Deno Backend
## 🚀 Quick Commands
```bash
# Start development server
deno task dev
# Start production server
deno task start
# Run tests
deno task test
# Format code
deno task fmt
# Lint code
deno task lint
# Type check
deno task check
```
## 📁 Important Files
| File | Purpose |
|------|---------|
| `main.ts` | Entry point - start here |
| `deno.json` | Configuration & tasks |
| `.env` | Environment variables |
| `PROJECT_SUMMARY.md` | Complete overview |
| `QUICK_START.md` | 5-minute guide |
| `MIGRATION_GUIDE.md` | Full documentation |
| `CHECKLIST.md` | Testing checklist |
## 🏗️ Architecture Layers
```
┌─────────────────────────────┐
│ Presentation Layer │ HTTP/WebSocket APIs
│ (routes, controllers) │
├─────────────────────────────┤
│ Application Layer │ Business logic services
│ (services, use cases) │
├─────────────────────────────┤
│ Domain Layer │ Core models & types
│ (models, entities) │
├─────────────────────────────┤
│ Infrastructure Layer │ External integrations
│ (file system, streaming) │
└─────────────────────────────┘
```
## 🔧 Configuration
Edit `.env`:
```env
PORT=5173 # Server port
DATA_DIR=../../data # Audio files
PUBLIC_DIR=../../public # Static files
LOG_LEVEL=INFO # DEBUG|INFO|WARN|ERROR
```
## 📡 API Endpoints
### Playlists
```
GET /api/playlists
GET /api/tracks?playlist=<id>
GET /api/reload-years?playlist=<id>
```
### Audio
```
HEAD /audio/t/:token
GET /audio/t/:token (supports Range header)
GET /cover/:name
```
## 🔌 WebSocket Events
### Client → Server
```
create_room, join_room, leave_room
set_name, ready, start_game
guess, pause, resume_play, skip_track
set_spectator, kick_player, select_playlist
```
### Server → Client
```
connected, room_update, play_track
guess_result, game_ended, sync, error
```
## 📦 Project Structure
```
src/server-deno/
├── domain/ # Models & types
│ ├── models/ # Player, Room, GameState
│ └── types.ts # TypeScript interfaces
├── application/ # Services
│ ├── GameService.ts
│ ├── RoomService.ts
│ ├── TrackService.ts
│ └── AnswerCheckService.ts
├── infrastructure/ # External systems
│ ├── FileSystemService.ts
│ ├── AudioStreamingService.ts
│ ├── TokenStoreService.ts
│ └── CoverArtService.ts
├── presentation/ # API layer
│ ├── HttpServer.ts
│ ├── WebSocketServer.ts
│ └── routes/
└── shared/ # Utilities
├── config.ts
├── logger.ts
└── utils.ts
```
## 🧪 Testing
```bash
# Run all tests
deno task test
# Run specific test
deno test tests/AnswerCheckService_test.ts
# Watch mode
deno task test:watch
# Coverage (if configured)
deno test --coverage=coverage
```
## 🐛 Debugging
Enable debug logs:
```env
LOG_LEVEL=DEBUG
```
Check logs for:
- HTTP requests
- WebSocket connections
- Game events
- Errors
## 🔒 Security
✅ Path traversal protection
✅ Token-based audio URLs
✅ Short-lived tokens (10 min)
✅ Input validation
✅ CORS configuration
✅ Deno permissions
## ⚡ Performance
- LRU caching (tokens, cover art)
- Batch processing (metadata)
- Streaming (range support)
- .opus preference
## 📝 Code Style
```typescript
// Use dependency injection
constructor(
private readonly service: SomeService
) {}
// Use explicit types
function process(data: string): Result {
// ...
}
// Use async/await
async function load(): Promise<Data> {
// ...
}
```
## 🆘 Troubleshooting
| Problem | Solution |
|---------|----------|
| Port in use | Change PORT in .env |
| Files not found | Check DATA_DIR path |
| Permission denied | Add --allow-* flags |
| Module errors | Run `deno cache main.ts` |
## 📚 Documentation
1. **Quick Start**: `QUICK_START.md`
2. **Full Guide**: `MIGRATION_GUIDE.md`
3. **Testing**: `CHECKLIST.md`
4. **Summary**: `PROJECT_SUMMARY.md`
## 🤝 Contributing
1. Follow existing patterns
2. Add TypeScript types
3. Write JSDoc comments
4. Add tests
5. Update documentation
## 💡 Tips
- Use `deno fmt` before committing
- Run `deno lint` to catch issues
- Check types with `deno check`
- Read JSDoc comments in code
- Follow Clean Architecture
## 🔗 Useful Links
- [Deno Manual](https://docs.deno.com)
- [Deno Standard Library](https://deno.land/std)
- [Oak Framework](https://deno.land/x/oak)
- [Socket.IO Deno](https://deno.land/x/socket_io)
---
**Need help?** Check the full documentation or code comments!

View File

@@ -208,5 +208,11 @@ export class AudioStreamingService {
// The token system already provides security
ctx.response.headers.set('Cache-Control', 'public, max-age=3600');
ctx.response.headers.set('Accept-Ranges', 'bytes');
// iOS Safari specific headers to improve streaming
ctx.response.headers.set('Access-Control-Allow-Origin', '*');
ctx.response.headers.set('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
ctx.response.headers.set('Access-Control-Allow-Headers', 'Range');
ctx.response.headers.set('Access-Control-Expose-Headers', 'Content-Length, Content-Range, Accept-Ranges');
}
}

View File

@@ -271,17 +271,18 @@
<h3 class="text-lg font-semibold">Position</h3>
<p class="text-sm text-slate-500 dark:text-slate-400 mb-2">Wähle die Position und klicke Platzieren.</p>
<div class="flex flex-wrap items-center gap-3">
<div id="placeArea" class="hidden flex items-center gap-2">
<div id="placeArea"
class="hidden flex flex-col sm:flex-row items-stretch sm:items-center gap-2 w-full sm:w-auto">
<select id="slotSelect"
class="h-10 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3"></select>
class="h-10 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3 w-full sm:w-auto"></select>
<button id="placeBtn"
class="h-10 px-4 rounded-lg bg-slate-900 hover:bg-slate-700 text-white font-medium dark:bg-slate-700 dark:hover:bg-slate-600">
class="h-10 px-4 rounded-lg bg-slate-900 hover:bg-slate-700 text-white font-medium dark:bg-slate-700 dark:hover:bg-slate-600 w-full sm:w-auto">
Platzieren
</button>
</div>
<div id="nextArea" class="hidden">
<div id="nextArea" class="hidden w-full sm:w-auto">
<button id="nextBtn"
class="h-10 px-4 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium">
class="h-10 px-4 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium w-full sm:w-auto">
Next
</button>
</div>

View File

@@ -47,13 +47,25 @@ export function loadTrack(url, fileName) {
format = formatMap[ext] || ext;
}
// Detect iOS/Safari for optimized settings
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
// iOS ignores programmatic volume, so always use 1.0 for iOS
const initialVolume = isIOS
? 1.0
: parseFloat(localStorage.getItem("volume") || "1");
// Create new Howler sound
const howlConfig = {
src: [url],
html5: true, // Use HTML5 Audio for streaming
// Always use HTML5 Audio for streaming, but optimize for iOS
html5: true,
preload: true,
autoplay: false, // Explicitly prevent autoplay
volume: parseFloat(localStorage.getItem("volume") || "1"),
volume: initialVolume,
// Enable pool for better resource management on iOS
pool: isIOS ? 1 : 5,
onload: function () {
showBuffer(false);
},
@@ -63,6 +75,7 @@ export function loadTrack(url, fileName) {
},
onplayerror: function (id, error) {
console.error("Error playing audio:", error);
// iOS Safari often requires user interaction to unlock audio
currentSound.once("unlock", function () {
currentSound.play();
});
@@ -148,20 +161,49 @@ export function initAudioUI() {
if (isInitialized) return;
isInitialized = true;
// Detect iOS/Safari
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
// Set up volume control if it exists
const $volumeSlider = document.getElementById("volumeSlider");
if ($volumeSlider) {
const savedVolume = localStorage.getItem("volume") || "1";
$volumeSlider.value = savedVolume;
$volumeSlider.addEventListener("input", () => {
const volume = parseFloat($volumeSlider.value);
if (currentSound) {
currentSound.volume(volume);
// iOS doesn't support programmatic volume control
if (isIOS) {
// Disable the volume slider on iOS and show a message
$volumeSlider.disabled = true;
$volumeSlider.style.opacity = "0.5";
$volumeSlider.style.cursor = "not-allowed";
// Add a tooltip/label explaining iOS limitation
const volumeLabel = $volumeSlider.parentElement;
if (volumeLabel) {
// Change layout to vertical (flex-col) instead of horizontal
volumeLabel.classList.remove("inline-flex", "items-center", "gap-2");
volumeLabel.classList.add("flex", "flex-col", "items-start", "gap-1");
const iosNote = document.createElement("span");
iosNote.className = "text-xs text-slate-500 dark:text-slate-400";
iosNote.textContent = "Nutze die Lautstärketasten deines Geräts";
volumeLabel.appendChild(iosNote);
}
Howler.volume(volume); // Set global volume
localStorage.setItem("volume", String(volume));
});
} else {
// Non-iOS: Normal volume control
$volumeSlider.addEventListener("input", () => {
const volume = parseFloat($volumeSlider.value);
if (currentSound) {
currentSound.volume(volume);
}
Howler.volume(volume); // Set global volume
localStorage.setItem("volume", String(volume));
});
// Set initial volume for non-iOS devices
const initialVolume = parseFloat(savedVolume);
Howler.volume(initialVolume);
}
}
// Unlock audio on first user interaction (mobile browsers)
@@ -191,15 +233,25 @@ export function applySync(startAt, serverNow) {
const drift = currentSeek - elapsed;
const abs = Math.abs(drift);
if (abs > 1.0) {
// Detect iOS/Safari - be more conservative with sync
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
const needsGentleSync = isIOS || isSafari;
// iOS Safari: Higher threshold before doing hard seek (less aggressive)
const hardSeekThreshold = needsGentleSync ? 2.0 : 1.0;
// iOS Safari: Higher threshold before adjusting playback rate
const rateAdjustThreshold = needsGentleSync ? 0.3 : 0.12;
if (abs > hardSeekThreshold) {
// Large drift - hard seek
currentSound.seek(Math.max(0, elapsed));
if (!currentSound.playing()) {
currentSound.play();
}
currentSound.rate(1.0);
} else if (abs > 0.12) {
// Small drift - adjust playback rate
} else if (abs > rateAdjustThreshold && !needsGentleSync) {
// Small drift - adjust playback rate (skip this on iOS/Safari to avoid stuttering)
const maxNudge = 0.03;
const sign = drift > 0 ? -1 : 1;
const rate = 1 + sign * Math.min(maxNudge, abs * 0.5);