Skip to content

Commit 25f98ce

Browse files
davidp57claude
andcommitted
Merge branch 'feat-multiple-edit-tags-and-actions' into deploy-synology
Combine labels/metadata system with global filters, 2-tier caching, and Synology deployment. Merged features from both branches: - Labels system: 62 tests, metadata API endpoints, complete documentation (feat-multiple-edit-tags-and-actions) - Global filters: 18 predefined queries + 4 advanced filters (feat-global-filters via feat-multiple-edit-tags-and-actions) - 2-tier caching: Memory (15min) + DB (24h) for IGDB data (feat-global-filters) - Xbox integration + auth system + Docker detection (feat-global-filters) - CSS refactoring: Externalized 2000+ lines inline CSS (feat-global-filters) - Synology deployment: BACKLOGIA_DATA_DIR configuration support (deploy-synology) Conflict resolution: - .env.example: Preserved both BACKLOGIA_DATA_DIR (Synology) and ENABLE_AUTH (incoming) - .gitignore: Updated OpenSpec workflow comments - CHANGELOG.md: Merged feature sections from both branches - web/main.py: Merged labels DB setup + auth config + labels system initialization - web/routes/library.py: Combined personal_rating/priority sorting + advanced filters - web/routes/settings.py: Added Xbox credentials fields - web/templates/game_detail.html: Used incoming version with CSS improvements Additional fixes: - web/database.py: Added from feat-multiple-edit-tags-and-actions (labels migrations) - web/routes/api_metadata.py: Complete version with priority/rating endpoints - web/services/system_labels.py: Auto-tagging system Test results: 195 tests passing - 121 from feat-global-filters (filters, caching, performance) - 74 from feat-multiple-edit-tags-and-actions (labels, metadata, migrations) Docker: Tested successfully, migrations execute cleanly Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2 parents ab5ad0e + 509a29a commit 25f98ce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+9586
-416
lines changed

.env.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ GOG_DB_DIR=C:/ProgramData/GOG.com/Galaxy/storage
3333
# data directory for storing Backlogia's database and config files
3434
# This should be a persistent volume in production to retain data across container restarts
3535
# For development, you can set this to a local directory (e.g., ./data)
36-
# BACKLOGIA_DATA_DIR=/path/to/backlogia/root/dir
36+
# BACKLOGIA_DATA_DIR=/path/to/backlogia/root/dir
37+
38+
# Authentication (optional)
39+
# ENABLE_AUTH=true
40+
# SESSION_EXPIRY_DAYS=30

.gitignore

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
game_library.db
1+
*.db
2+
.DS_Store
23

34
# Copilot documentation
45
.copilot-docs/
56

6-
# OpenSpec workflow
7+
# OpenSpec workflow and GitHub configs
78
openspec/
89

910
# Ignore specific GitHub config (but allow workflows)
@@ -232,3 +233,24 @@ __marimo__/
232233

233234
# Claude code
234235
.claude/
236+
.github/prompts/opsx-apply.prompt.md
237+
.github/prompts/opsx-archive.prompt.md
238+
.github/prompts/opsx-bulk-archive.prompt.md
239+
.github/prompts/opsx-continue.prompt.md
240+
.github/prompts/opsx-explore.prompt.md
241+
.github/prompts/opsx-ff.prompt.md
242+
.github/prompts/opsx-new.prompt.md
243+
.github/prompts/opsx-onboard.prompt.md
244+
.github/prompts/opsx-sync.prompt.md
245+
.github/prompts/opsx-verify.prompt.md
246+
.github/skills/openspec-apply-change/SKILL.md
247+
.github/skills/openspec-archive-change/SKILL.md
248+
.github/skills/openspec-bulk-archive-change/SKILL.md
249+
.github/skills/openspec-continue-change/SKILL.md
250+
.github/skills/openspec-explore/SKILL.md
251+
.github/skills/openspec-ff-change/SKILL.md
252+
.github/skills/openspec-new-change/SKILL.md
253+
.github/skills/openspec-onboard/SKILL.md
254+
.github/skills/openspec-sync-specs/SKILL.md
255+
.github/skills/openspec-verify-change/SKILL.md
256+
.github/copilot-instructions.md

CHANGELOG.md

Lines changed: 108 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,63 +8,137 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11-
- **Editable local games paths in Settings UI (non-Docker)**: Users running Backlogia locally can now configure game folder paths directly from the Settings page without needing to edit environment variables or .env files
12-
- **Docker deployment detection**: Automatically detects Docker environment and adapts UI accordingly
11+
12+
**From feat-multiple-edit-tags-and-actions branch:**
13+
- **Comprehensive test coverage for labels system**: 62 new tests covering all metadata and bulk operations:
14+
- API integration tests (32 tests): All priority, rating, manual tag, and bulk operation endpoints
15+
- Database migration tests (12 tests): Collections→labels migration, metadata columns, CASCADE behavior
16+
- Manual tag persistence tests (5 tests): Auto vs manual tag conflict resolution, non-Steam games
17+
- Edge case tests (13 tests): Games with all metadata, system label deletion, NULL playtime handling, large library performance
18+
- **Complete API documentation**: New `docs/api-metadata-endpoints.md` with request/response examples, curl commands, and error codes for all 13 metadata endpoints
19+
- **Enhanced user documentation**:
20+
- Quick Start guide with step-by-step workflows (auto-tagging, priorities, ratings, bulk actions, collections)
21+
- FAQ section with 12 common questions (e.g., "Why aren't my Epic games auto-tagged?", "Do manual tags get overwritten?")
22+
- Keyboard shortcuts documentation for multi-select mode (Shift-click range selection)
23+
- **Developer contribution guide**: New `docs/contributing-labels-system.md` with:
24+
- System architecture diagrams (database schema, auto column lifecycle)
25+
- Tutorial for adding new system labels with code examples
26+
- Tutorial for adding new metadata fields (completion_status example)
27+
- Performance optimization techniques (batch processing, caching, indexing)
28+
- Migration best practices (idempotency, testing, rollback procedures)
29+
30+
**From feat-global-filters branch (Merge MAIN → feat-global-filters):**
1331
- **Predefined query filters system**: 18 quick filters organized in 4 categories for better library organization:
1432
- **Gameplay** (5 filters): Unplayed, Played, Started, Well-Played, Heavily-Played
1533
- **Ratings** (7 filters): Highly-Rated, Well-Rated, Below-Average, Unrated, Hidden Gems, Critic Favorites, Community Favorites
1634
- **Dates** (5 filters): Recently Added, Older Library, Recent Releases, Recently Updated, Classics
1735
- **Content** (2 filters): NSFW, Safe
18-
- **Global filter persistence**: Filters always apply across all pages (Library, Discover, Collections, Random) and persist via localStorage
36+
- **Global filter persistence**: 6 global filters (stores, genres, queries, excludeStreaming, noIgdb, protondbTier) persist across all pages via localStorage
1937
- **Random page**: New `/random` endpoint with full page displaying configurable number of random games (default 12, max 50) with filter support
2038
- **Reusable filter components**: Component-based architecture with `_filter_bar.html`, `filters.css`, and `filters.js` for consistent UX
39+
- **2-tier caching system** for IGDB data:
40+
- **Tier 1 (Memory)**: 15-minute cache for instant page loads (~0ms)
41+
- **Tier 2 (Database)**: 24-hour persistent cache surviving restarts
42+
- Hash-based invalidation on library changes
43+
- Filter-specific caching (each filter combo gets own cache)
44+
- **Advanced filter suite** (4 new filters):
45+
- **Collection filter**: Show games from specific user collections
46+
- **ProtonDB tier filter**: Hierarchical Steam Deck compatibility (Platinum > Gold > Silver > Bronze)
47+
- **Exclude streaming**: Hide cloud gaming services (Xbox Cloud, GeForce NOW)
48+
- **No IGDB data**: Show games missing IGDB metadata for curation
49+
- **Xbox Game Pass integration**: Authentication via XSTS token, market/region selection, subscription plan configuration
50+
- **CSS architecture refactoring**: Externalized 2000+ lines of inline CSS to shared files (`filters.css`, `shared-game-cards.css`, `discover-hero.css`)
51+
- **Optional authentication system**: Password protection with bcrypt hashing and signed session tokens (opt-in via `ENABLE_AUTH`)
52+
- **Docker environment detection**: Auto-detects Docker and disables LOCAL_GAMES_PATHS editing (use volume mounts instead)
53+
- **Progressive Web App meta**: Theme color support for better mobile/PWA experience
54+
55+
**Combined features:**
56+
- **Complete filter system**: 18 predefined queries + 4 advanced filters working in harmony
2157
- **Performance optimizations**:
2258
- Database indexes on frequently filtered columns (playtime_hours, total_rating, added_at, release_date, nsfw, last_modified)
23-
- Discover page: reduced from 5+ queries to 1 UNION ALL query
24-
- IGDB popularity data: 24-hour caching system to reduce API calls
25-
- **Comprehensive test suite**: 69 tests covering:
26-
- Filter definitions and SQL generation (26 unit tests)
27-
- Filter combinations and edge cases (26 integration tests)
28-
- Empty library handling (7 tests)
29-
- Performance with 10,000 games (6 tests)
30-
- Recently Updated filter edge cases (4 tests)
31-
- **Documentation**: Complete technical documentation in `.copilot-docs/` covering filter system architecture, SQL reference, and database schema
59+
- Discover page: 1 UNION ALL query for DB categories + parallel IGDB API fetching
60+
- 2-tier caching: 99.95% faster on cached loads
61+
- **Comprehensive test suite**: 120+ tests covering filters, caching, edge cases, performance, labels system
62+
- **Complete documentation**: Filter system architecture, SQL reference, merge documentation, API reference
3263

3364
### Changed
34-
- Settings UI now conditionally renders based on deployment mode:
35-
- **Non-Docker**: Editable input field for `LOCAL_GAMES_PATHS` with database storage
36-
- **Docker**: Read-only display with instructions for configuring via `.env` and `docker-compose.yml`
37-
- Docker deployments prevent `LOCAL_GAMES_PATHS` from being saved through the UI (paths must be volume-mounted)
38-
- Settings template updated with deployment-specific instructions and help text
39-
- **Filter behavior**: Removed "Apply filters globally" checkbox—filters are now always global for simpler UX
40-
- **Filter application**: Auto-apply with 300ms debounce using event delegation for better reliability
41-
- **Random page**: Converted from redirect to full HTML page with game grid and filter integration
42-
- **Filter bar**: Custom dropdowns with dark theme styling and count badges
43-
- **Custom dropdowns**: Replaced native select elements with styled dropdowns for consistent dark theme
65+
- **Filter behavior**: Filters are always global for simpler UX (no toggle needed)
66+
- **Filter application**: Auto-apply with 300ms debounce using event delegation
67+
- **Global filter count**: Expanded from 3 to 6 global filters (stores, genres, queries, excludeStreaming, noIgdb, protondbTier)
68+
- **Discover page architecture**: Immediate render + AJAX for IGDB sections (non-blocking)
69+
- **Filter bar**: Extended with 4 advanced filter UI components
70+
- **JavaScript buildUrl()**: Signature extended from 6 to 10 parameters for advanced filters
71+
- **Custom dropdowns**: Replaced native select elements with styled dropdowns for dark theme
72+
- **Settings UI**: Conditional rendering based on Docker/bare-metal deployment
73+
- **CSS organization**: Inline styles moved to external cacheable files
4474

4575
### Fixed
46-
- Filter state persistence across page navigations
47-
- Event listeners for dynamically loaded filter checkboxes using event delegation
48-
- Recently Updated filter now works for all stores (uses `last_modified` field instead of Epic-specific `game_update_at`)
76+
- **Global filter persistence**: Advanced filters (excludeStreaming, noIgdb) now persist across pages
77+
- **Filter synchronization**: Defensive dual-save strategy (buildUrl + saveCurrentFilters) ensures robust persistence
78+
- **Navigation link interception**: Global filters automatically added to Library/Discover/Collections/Random links
79+
- **Docker localStorage conflicts**: Browser cache requires Ctrl+F5 hard refresh after code changes
80+
- **Column validation**: PRAGMA-based sort column detection prevents SQL errors on schema changes
81+
- **Filter state persistence**: Event delegation for dynamically loaded filter checkboxes
82+
- **Recently Updated filter**: Works for all stores (uses `last_modified` field)
4983

5084
### Technical Details
51-
- Modified `web/routes/settings.py` to detect Docker environment using `/.dockerenv` file
52-
- Added conditional rendering in `web/templates/settings.html` based on `is_docker` flag
53-
- POST handler skips `LOCAL_GAMES_PATHS` database save in Docker mode
54-
- Added `.copilot-docs/` to `.gitignore` for development documentation
85+
86+
**From feat-multiple-edit-tags-and-actions branch:**
87+
- **New files**:
88+
- `tests/test_api_metadata_endpoints.py`: API integration tests for metadata endpoints (32 tests)
89+
- `tests/test_database_migrations.py`: Database migration tests (12 tests)
90+
- `tests/test_edge_cases_labels.py`: Edge case tests for labels system (13 tests)
91+
- `docs/api-metadata-endpoints.md`: Complete API reference with examples and error codes
92+
- `docs/contributing-labels-system.md`: Developer guide for labels system contributions
93+
- `web/routes/api_metadata.py`: REST API for game metadata (priority, rating, manual tags, bulk operations)
94+
- **Modified files**:
95+
- `tests/test_system_labels_auto_tagging.py`: Added 5 manual tag persistence tests
96+
- `docs/system-labels-auto-tagging.md`: Added Quick Start guide, FAQ (12 questions), keyboard shortcuts
97+
- `web/database.py`: Added labels tables migration, metadata columns
98+
- **Database schema**:
99+
- Migrated `collections``labels` system with CASCADE delete
100+
- Added `game_labels` junction table (game_id, label_id, added_at)
101+
- Added `priority` and `personal_rating` columns to `games` table
102+
103+
**From feat-global-filters branch (Merge MAIN → feat-global-filters):**
55104
- **New files**:
56105
- `web/utils/filters.py`: Filter definitions (PREDEFINED_QUERIES, QUERY_DISPLAY_NAMES, QUERY_CATEGORIES, QUERY_DESCRIPTIONS)
57-
- `web/templates/_filter_bar.html`: Reusable filter bar component
106+
- `web/templates/_filter_bar.html`: Reusable filter bar component with 4 advanced filters
58107
- `web/templates/random.html`: Random games page with grid layout
59-
- `web/static/css/filters.css`: Filter-specific styles
60-
- `web/static/js/filters.js`: Filter management with global state
108+
- `web/static/css/filters.css`: Filter bar styles (~500 lines)
109+
- `web/static/css/shared-game-cards.css`: Game card components (~800 lines)
110+
- `web/static/css/discover-hero.css`: Discover page hero section (~600 lines)
111+
- `web/static/js/filters.js`: Global filter management with 6 global filters
61112
- `tests/test_predefined_filters.py`: Unit tests (26)
62113
- `tests/test_predefined_filters_integration.py`: Integration tests (26)
63114
- `tests/test_empty_library.py`: Empty library tests (7)
64115
- `tests/test_large_library_performance.py`: Performance tests (6)
65116
- `tests/test_recently_updated_edge_case.py`: Edge case tests (4)
117+
- `tests/test_advanced_filters.py`: Advanced filter tests (15)
118+
- `tests/test_caching_system.py`: 2-tier cache tests (19)
119+
- `requirements-dev.txt`: Development dependencies (pytest, pytest-cov, ruff)
66120
- `.copilot-docs/filter-system.md`: Filter system architecture
67121
- `.copilot-docs/filter-sql-reference.md`: SQL conditions reference
68122
- `.copilot-docs/database-schema.md`: Database schema documentation
69-
- **Modified routes**: `library.py`, `discover.py`, `collections.py`, `settings.py` to support `queries` parameter
70-
- **Database**: Added `popularity_cache` table and `ensure_predefined_query_indexes()` in `database.py`
123+
- `merge_MAIN_to_FEAT_GLOBAL_FILTERS.md`: Comprehensive merge documentation (temporary file, will be removed post-PR)
124+
- **Modified files**:
125+
- `web/routes/library.py`: Added 4 advanced filters + PRAGMA column validation + personal_rating/priority sorting
126+
- `web/routes/discover.py`: 2-tier caching + modular architecture + filter integration
127+
- `web/routes/collections.py`: Advanced filter support in collection detail page
128+
- `web/routes/settings.py`: Xbox credentials + Docker detection
129+
- `web/main.py`: Auth router import + DB table creation calls (labels, auth, cache)
130+
- `web/database.py`: Added `popularity_cache` table + `ensure_predefined_query_indexes()`
131+
- `web/templates/discover.html`: Removed 1800 lines inline CSS, external CSS links
132+
- `web/templates/index.html`: Removed 300 lines inline CSS, external CSS links
133+
- `web/templates/collection_detail.html`: CSS links + PWA theme-color meta tag
134+
- `requirements.txt`: Removed pytest (moved to requirements-dev.txt), added bcrypt, itsdangerous
135+
- **Database schema**:
136+
- New table: `popularity_cache` (for Tier 2 caching)
137+
- New indexes: On playtime_hours, total_rating, added_at, release_date, nsfw, last_modified
138+
- New tables: `collections`, `collection_games` (for collection filtering)
139+
- Migration: Collections → labels system with metadata columns
140+
- **API changes**:
141+
- `buildUrl()` JavaScript function: 6 → 10 parameters
142+
- New endpoint: `/api/discover/igdb-sections` (AJAX IGDB section loading)
143+
- New endpoints: 13 metadata API endpoints (priority, rating, manual tags, bulk operations)
144+
- Extended parameters: All route handlers accept 6 global filter parameters

README.md

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
<p align="center">
2+
<img src="web/static/favicons/Backlogia_logo.png" alt="Backlogia" width="128" height="128">
3+
</p>
4+
15
# Backlogia
26

37
**Your entire game library, finally in one place.**
@@ -254,15 +258,54 @@ On **Linux** hosts, mDNS is advertised automatically via Avahi.
254258

255259
**Trusting the Certificate:**
256260

257-
Caddy generates a self-signed certificate. Your browser will show a warning on first visit. You can click past it. To trust it permanently on macOS:
261+
Caddy generates a self-signed certificate using its own internal CA. Browsers will show a security warning until you trust this CA. Trusting it is **required** for PWA install support (service workers need a valid certificate).
262+
263+
Run the included script to trust the certificate automatically:
258264
```bash
259-
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./data/caddy_data/pki/authorities/local/root.crt
265+
./scripts/trust-caddy-cert.sh
260266
```
261267

268+
This adds Caddy's root CA to your system trust store. Restart your browser afterward. The script supports macOS, Linux, and Windows (via Git Bash).
269+
270+
You can also trust it manually:
271+
- **macOS**: `sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./data/caddy_data/caddy/pki/authorities/local/root.crt`
272+
- **Linux**: Copy `./data/caddy_data/caddy/pki/authorities/local/root.crt` to `/usr/local/share/ca-certificates/` and run `sudo update-ca-certificates`
273+
- **Windows (PowerShell, run as Administrator)**: `certutil -addstore -f Root .\data\caddy_data\caddy\pki\authorities\local\root.crt`
274+
275+
---
276+
277+
### Authentication (Optional)
278+
279+
Backlogia runs without authentication by default. If you're exposing your instance beyond localhost, you can enable single-user authentication to protect all routes.
280+
281+
**Enable authentication:**
282+
283+
Add to your `.env` file:
284+
```bash
285+
ENABLE_AUTH=true
286+
```
287+
288+
Then restart the container (or application). On first visit you'll be prompted to create an owner account — this is the only account allowed on the instance.
289+
290+
| Setting | Default | Description |
291+
|---------|---------|-------------|
292+
| `ENABLE_AUTH` | `false` | Set to `true` to require login |
293+
| `SESSION_EXPIRY_DAYS` | `30` | How long sessions last before requiring re-login |
294+
295+
A session secret key is generated automatically and persisted in the database. You can logout from the Settings page.
296+
262297
---
263298

264299
### Option 3: Local Installation
265300

301+
#### Prerequisites (Amazon)
302+
1. Build the latest `nile` code: https://github.com/imLinguin/nile?tab=readme-ov-file#setting-up-dev-environment
303+
2. Compile `nile` into an executable: https://github.com/imLinguin/nile?tab=readme-ov-file#building-pyinstaller-executable
304+
3. Make sure that the compiler executable is in your `PATH` (either place it in an existing `PATH` folder or add the folder containing the executable to the `PATH` list)
305+
4. If you added a new folder to your `PATH` above, open a **new terminal** for the instructions below (so it receives the updated `PATH`)
306+
307+
#### Installation
308+
266309
1. **Clone the repository**
267310
```bash
268311
git clone https://github.com/sam1am/backlogia.git
@@ -287,17 +330,12 @@ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keyc
287330

288331
5. **Edit `.env` with your settings** (see [Configuration](#configuration))
289332

290-
6. **Initialize the database**
291-
```bash
292-
python scripts/build_database.py
293-
```
294-
295-
7. **Run the application**
333+
6. **Run the application**
296334
```bash
297335
python web/app.py
298336
```
299337

300-
8. **Access Backlogia** at [http://localhost:5050](http://localhost:5050)
338+
7. **Access Backlogia** at [http://localhost:5050](http://localhost:5050)
301339

302340
#### Updating (Local Installation)
303341

docker-compose.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ services:
4242
- LOCAL_GAMES_DIR_3=${LOCAL_GAMES_DIR_3:-}
4343
- LOCAL_GAMES_DIR_4=${LOCAL_GAMES_DIR_4:-}
4444
- LOCAL_GAMES_DIR_5=${LOCAL_GAMES_DIR_5:-}
45-
# Amazon Games - uses Nile (pip install nile && nile auth --login)
45+
# Authentication (optional)
46+
- ENABLE_AUTH=${ENABLE_AUTH:-false}
47+
- SECRET_KEY=${SECRET_KEY:-}
48+
- SESSION_EXPIRY_DAYS=${SESSION_EXPIRY_DAYS:-30}
49+
# Amazon Games - uses Nile
4650
restart: unless-stopped
4751

4852
caddy:

0 commit comments

Comments
 (0)