A new README for the combined project with setup from the Flashpoint Ultimate torrent. New feature docs for streaming, proxy sign-in and game saves; deployment, upgrade and streaming READMEs updated for the single compose file; the project renamed throughout.
9.9 KiB
Playlists & Favorites
Overview
Dotcom Arcade provides three systems for organizing games:
- Flashpoint Playlists - JSON files compatible with Flashpoint Launcher
- User Playlists - Database-driven per-user playlists with sharing
- Favorites - Quick per-user favorites list
Playlist Systems Comparison
| Feature | Flashpoint Playlists | User Playlists | Favorites |
|---|---|---|---|
| Storage | JSON files | SQLite (user.db) | SQLite (user.db) |
| Scope | Shared/Community | Per-user | Per-user |
| Sharing | Public by default | Token-based sharing | Not shareable |
| Launcher Compatible | Yes | No | No |
| API Path | /api/playlists |
/api/user-playlists |
/api/favorites |
| Game Order | Manual ordering | Manual ordering | By date added |
| Metadata | Title, author, icon | Title, description, icon | None |
Architecture
Backend Components:
PlaylistService: Flashpoint file-based playlist managementUserPlaylistService: Database-driven user playlist managementFavoritesService: Database-driven favorites managementroutes/playlists.ts: Flashpoint playlists REST APIroutes/user-playlists.ts: User playlists REST API with sharingroutes/shared-playlists.ts: Public access to shared playlistsroutes/favorites.ts: REST API for favorites
Frontend Components:
PlaylistsView: List all Flashpoint playlistsUserPlaylistsView: List user's personal playlistsPlaylistDetailView: Single playlist with gamesSharedPlaylistView: View shared playlist (no auth required)FavoritesView: Favorites-specific viewCreatePlaylistModal: Playlist creation dialogusePlaylistshook: Flashpoint playlist operationsuseUserPlaylistshook: User playlist operationsuseFavoriteshook: Favorites management
Playlist File Format
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Favorite Puzzle Games",
"description": "A collection of puzzle games I enjoy",
"author": "John Doe",
"library": "arcade",
"icon": "",
"games": [{ "gameId": "550e8400-e29b-41d4-a716-446655440001" }]
}
Location: D:/Flashpoint/Data/Playlists/
User Playlists
User playlists are stored in the application database and support:
- Per-user ownership - Each playlist belongs to a specific user
- Privacy - Playlists are private by default
- Sharing - Generate share tokens with optional expiration
- Cloning - Clone from Flashpoint playlists or shared playlists
User Playlist Sharing
Share tokens allow public access without authentication:
// Enable sharing
const { shareToken, shareUrl } = await api.post(
`/user-playlists/${id}/share/enable`,
{ expiresAt: '2024-12-31T23:59:59Z', showOwner: true }
);
// Disable sharing
await api.post(`/user-playlists/${id}/share/disable`);
// Regenerate token (invalidates old links)
const { shareToken: newToken } = await api.post(
`/user-playlists/${id}/share/regenerate`
);
Copying Between Systems
// Copy Flashpoint playlist to User playlist
await api.post('/user-playlists/copy-flashpoint', {
flashpointPlaylistId: 'flashpoint-uuid',
newTitle: 'My Copy'
});
// Clone shared User playlist to own collection
await api.post(`/shared-playlists/${shareToken}/clone`, {
newTitle: 'Cloned Playlist'
});
Database Schema
Flashpoint Playlists: Stored as JSON files in Flashpoint directory. NOT in database. This maintains compatibility with Flashpoint Launcher.
User Playlists:
CREATE TABLE user_playlists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
description TEXT,
icon TEXT,
share_token TEXT UNIQUE,
share_expires_at TIMESTAMP,
show_owner INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE user_playlist_games (
id INTEGER PRIMARY KEY AUTOINCREMENT,
playlist_id INTEGER NOT NULL,
game_id TEXT NOT NULL,
order_index INTEGER DEFAULT 0,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(playlist_id, game_id),
FOREIGN KEY(playlist_id) REFERENCES user_playlists(id) ON DELETE CASCADE
);
Favorites:
CREATE TABLE user_favorites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
game_id TEXT NOT NULL,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, game_id),
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
API Endpoints
GET /api/playlists
Get all playlists.
- Response: Array of playlist summaries with game IDs
GET /api/playlists/:id
Get single playlist with full game data.
- Response: Playlist with games array containing full game metadata
POST /api/playlists
Create a new playlist.
- Body:
{ "title", "description", "author", "library" } - Response: Created playlist (201)
POST /api/playlists/:id/games
Add games to a playlist.
- Body:
{ "gameIds": [...] } - Response: Updated playlist with full game data
DELETE /api/playlists/:id/games
Remove games from a playlist.
- Body:
{ "gameIds": [...] } - Response: Updated playlist
DELETE /api/playlists/:id
Delete a playlist.
- Response:
{ "success": true }
User Playlists API Endpoints
Complete documentation: User Playlists API
Key Endpoints:
GET /api/user-playlists- Get user's playlistsPOST /api/user-playlists- Create playlistGET /api/user-playlists/:id- Get single playlistPATCH /api/user-playlists/:id- Update playlistDELETE /api/user-playlists/:id- Delete playlistPOST /api/user-playlists/:id/games- Add gamesDELETE /api/user-playlists/:id/games- Remove gamesPOST /api/user-playlists/:id/share/enable- Enable sharingPOST /api/user-playlists/:id/share/disable- Disable sharingPOST /api/user-playlists/copy-flashpoint- Copy from Flashpoint playlist
Shared Playlists API Endpoints
Complete documentation: Shared Playlists API
Key Endpoints (no auth required except clone):
GET /api/shared-playlists/:shareToken- Get shared playlistGET /api/shared-playlists/:shareToken/games- Get gamesPOST /api/shared-playlists/:shareToken/generate-access-token- Get temp tokenPOST /api/shared-playlists/:shareToken/clone- Clone to user's collection
Favorites API Endpoints
Complete documentation: Favorites API
Key Endpoints:
GET /api/favorites- Get user's favoritesGET /api/favorites/games- Get favorites with full game dataPOST /api/favorites- Add game to favoritesPOST /api/favorites/toggle- Toggle favorite statusDELETE /api/favorites/:gameId- Remove from favoritesPOST /api/favorites/batch- Batch add favoritesDELETE /api/favorites- Clear all favorites
Favorites Implementation
// Add to Favorites
addFavorite(userId: number, gameId: string): boolean {
const stmt = db.prepare(`
INSERT OR IGNORE INTO user_favorites (user_id, game_id)
VALUES (?, ?)
`);
return stmt.run(userId, gameId).changes > 0;
}
// Toggle Favorite Status
toggleFavorite(userId: number, gameId: string): { isFavorited: boolean } {
if (this.isFavorited(userId, gameId)) {
this.removeFavorite(userId, gameId);
return { isFavorited: false };
} else {
this.addFavorite(userId, gameId);
return { isFavorited: true };
}
}
Permissions
Playlist Permissions:
playlists.read- View playlistsplaylists.create- Create new playlistsplaylists.update- Add/remove gamesplaylists.delete- Delete playlists
Favorites Permissions:
playlists.read- View favoritesplaylists.create- Add games to favoritesplaylists.delete- Remove games from favorites
Default Assignments:
- Admin: All permissions
- User: All permissions
- Guest: Read-only (playlists.read)
Common Use Cases
1. Create New Playlist
const createMutation = useCreatePlaylist();
await createMutation.mutateAsync({
title: 'My Puzzle Games',
description: 'All my favorite puzzle games',
author: 'John Doe',
library: 'arcade',
});
2. Add Games to Playlist
const addMutation = useAddToPlaylist();
await addMutation.mutateAsync({
playlistId: 'my-playlist-id',
gameIds: ['game1', 'game2', 'game3'],
});
3. Toggle Favorite
const { data } = await api.post('/favorites/toggle', { gameId });
// isFavorited is now true or false
4. Get Favorites with Full Data
const { data: favorites } = useQuery({
queryKey: ['favorites-games'],
queryFn: () => api.get('/favorites/games').then((res) => res.data),
});
Best Practices
- Always validate playlist exists before operations
- Prevent duplicate games in playlist
- Use optimistic updates for better UX
- Handle file system errors gracefully
- Show loading states during operations
- Confirm destructive actions (delete)
- Cache playlist data with TanStack Query
- Invalidate queries after mutations
- Backup playlist files regularly
- Use meaningful playlist IDs (UUIDs)
Troubleshooting
Playlist not found:
- Verify playlistId is correct
- Check if JSON file exists in Playlists directory
- Ensure file is valid JSON
- Check file permissions
Duplicate games in playlist:
- Verify duplicate detection logic
- Check gameId format consistency
- Ensure proper filtering on add
Favorites not persisting:
- Verify database migrations ran successfully
- Check user_favorites table exists in user.db
- Ensure user is authenticated
- Check
enableFavoritesfeature flag is enabled - Review backend logs for database errors
File write errors:
- Check directory permissions
- Verify disk space available
- Ensure Flashpoint path is correct