* docs(storage): server storage design and implementation plans
Design for keeping playlists and user data on the self-hosted server
(#305), revised against master, plus three implementation plans: storage
seams, the web-backend storage API, and the PWA server mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(database): share the playlist row codec
Move buildPlaylistRow, parseAppPlaylist and the upsert conflict helpers
from the Electron database operations into libs/shared/database
(@iptvnator/shared/database/app-playlist) so the self-hosted web backend
can write the same rows. The Electron modules re-export them; behavior is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(database): read the data root from IPTVNATOR_DATA_DIR
Deployments that keep the database on a mounted volume, such as the
self-hosted web backend in Docker, can now set the data root. The E2E
override still wins; without either variable the root stays ~/.iptvnator.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(services): add the playlist storage backend resolver
PlaylistStorageBackend becomes the single place that decides whether
playlists persist through the Electron bridge or IndexedDB, and defines
the PlaylistDocumentStore contract a server store will implement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(services): resolve playlist storage through one backend
PlaylistsService asks PlaylistStorageBackend for its document store
instead of reading window.electron and supportsSqlite directly. Paths that
only make sense on the desktop (legacy IndexedDB import, worker deletion
with progress, the favorites-channel query) check for the Electron kind
explicitly. Behavior is unchanged: the backend still resolves to the
Electron bridge or IndexedDB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(xtream): store PWA user data through USER_DATA_STORAGE
PwaXtreamDataSource and PwaCollectionStorage read and write favorites,
recently viewed items, collection snapshots and playback positions through
the new USER_DATA_STORAGE token (default: localStorage) instead of calling
localStorage directly, so a deployment can substitute a server-synced
storage. Behavior is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(pwa): document the storage seams
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(database): bracket access in the shared playlist codec
libs/shared/database compiles with noPropertyAccessFromIndexSignature,
which the Electron backend did not enforce on the moved code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* build(electron): alias the shared playlist codec in the worker bundle
The database worker is bundled by esbuild with an explicit alias table;
without the new entry, @iptvnator/shared/database/app-playlist resolved
under the library's index file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(services): read localStorage lazily in USER_DATA_STORAGE
When the browser denies storage access, reading localStorage itself
throws. The default factory returned the global, so injecting the token
failed and PwaXtreamDataSource could not be constructed. The adapter now
reads the global inside each call, where the data source already guards
its reads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(database): describe /data as an example data root
The Docker image does not set IPTVNATOR_DATA_DIR yet; that arrives with
the server storage backend.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Added DatabaseWorkerClient to manage database operations in a worker thread.
- Created database-worker-client.ts to handle requests and responses between the main process and the worker.
- Introduced database-worker.types.ts for type definitions related to worker messages and operations.
- Implemented database.worker.ts to execute various database operations, including CRUD operations for playlists, content, and favorites.
- Added database.worker-connection.ts to manage database connections and ensure proper handling of SQLite instances.
- Created tests for DatabaseWorkerClient to ensure correct request handling and error management.
- Updated xtream.events.ts to allow suppression of error logging for specific requests.
- Enhanced EPG parser worker to include a busy timeout for SQLite operations.
- Integrated shutdown logic for the DatabaseWorkerClient in the main application lifecycle.
Add tables, indexes, FTS5 virtual table, and triggers to support EPG
(electronic program guide) data in the shared SQLite connection. The
changes introduce epg_channels and epg_programs tables, multiple
indexes for efficient lookups (by source, name, channel, start, and
time range), and an epg_programs_fts virtual table with triggers to
keep the full-text index synchronized with epg_programs.
Register EPG database event handlers in the Electron backend and
add an IPC handler for bulk-saving channels. The handler deletes
existing channels for a source and inserts new channels in chunks to
avoid large single inserts, returning the inserted count.
These changes enable storing, querying, and full-text searching EPG
data and prepare backend IPC endpoints for importing program guide
data.
Add webpack config and include it in the electron backend build so
worker scripts are correctly bundled for packaged apps. Move worker
files to be packaged as extraResources instead of asar unpack patterns
so the packaged app can load worker JS from Resources/dist/... at
runtime. Fix worker path resolution in epg.events: compute resourcesPath
from app.getAppPath() and point to dist/apps/electron-backend/workers
when app.isPackaged. Remove verbose app path logging and update comments
to reflect the actual packaged layout.
Why: ensure webworker entry points are included in the renderer/backend
bundle and accessible when the Electron app is packaged, preventing
worker loading failures in production builds.