@francofantomius/jellyfin 0.2.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/llms.txt ADDED
@@ -0,0 +1,542 @@
1
+ # @francofantomius/jellyfin
2
+
3
+ > A lightweight, modular, and type-safe Jellyfin SDK for Node.js and the Browser.
4
+
5
+ `@francofantomius/jellyfin` is a universal TypeScript client library for interacting with [Jellyfin](https://jellyfin.org) media servers. Designed with zero production dependencies, it operates seamlessly across Node.js (18+), Bun, Deno, and modern web browsers (vanilla, React, Vue, Svelte, Electron, Tauri).
6
+
7
+ It targets both Jellyfin 10.x and Jellyfin 12.x server architectures with automatic version negotiation, modular sub-clients, full TypeScript definitions, progressive and adaptive (HLS) streaming, trickplay thumbnail scrubbing, chapter & intro/credits skipping, IndexedDB offline media caching, real-time WebSockets, and remote session control.
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ - [Package Metadata & Installation](#package-metadata--installation)
14
+ - [Architecture & Design Principles](#architecture--design-principles)
15
+ - [Quickstart](#quickstart)
16
+ - [Client Core: JellyfinClient](#client-core-jellyfinclient)
17
+ - [Constructor & Options](#constructor--options)
18
+ - [Authentication & Session Restore](#authentication--session-restore)
19
+ - [Lifecycle Events](#lifecycle-events)
20
+ - [Pluggable Storage & Caching](#pluggable-storage--caching)
21
+ - [Modules & API Reference](#modules--api-reference)
22
+ - [Auth & System (client.auth, client.system)](#auth--system-clientauth-clientsystem)
23
+ - [Media Library Browsing (client.library)](#media-library-browsing-clientlibrary)
24
+ - [Media, Streaming & Artwork (client.media)](#media-streaming--artwork-clientmedia)
25
+ - [Playback Reporting & Transcode Management (client.playback, client.transcode)](#playback-reporting--transcode-management-clientplayback-clienttranscode)
26
+ - [Search (client.search)](#search-clientsearch)
27
+ - [In-Progress & Dynamic Feeds (client.resume, client.nextUp, client.latest)](#in-progress--dynamic-feeds-clientresume-clientnextup-clientlatest)
28
+ - [Playlists, Lyrics & Favorites (client.playlists, client.lyrics, client.favorites)](#playlists-lyrics--favorites-clientplaylists-clientlyrics-clientfavorites)
29
+ - [Trickplay & Scrubbing Previews (client.trickplay)](#trickplay--scrubbing-previews-clienttrickplay)
30
+ - [Chapters & Intro/Credits Skipping (client.chapters)](#chapters--introcredits-skipping-clientchapters)
31
+ - [Genres, Studios & Collections (client.genres, client.collections)](#genres-studios--collections-clientgenres-clientcollections)
32
+ - [Display Preferences (client.displayPreferences)](#display-preferences-clientdisplaypreferences)
33
+ - [Remote Session Control (client.sessions)](#remote-session-control-clientsessions)
34
+ - [Real-Time WebSocket Events (client.websocket)](#real-time-websocket-events-clientwebsocket)
35
+ - [Quick Connect Device Pairing (client.quickConnect)](#quick-connect-device-pairing-clientquickconnect)
36
+ - [Offline Storage Manager (client.offline)](#offline-storage-manager-clientoffline)
37
+ - [Quality Presets & Utilities](#quality-presets--utilities)
38
+ - [Error Handling](#error-handling)
39
+ - [Documentation Index](#documentation-index)
40
+
41
+ ---
42
+
43
+ ## Package Metadata & Installation
44
+
45
+ - **Package Name**: `@francofantomius/jellyfin`
46
+ - **Version**: `0.4.1`
47
+ - **Runtime Support**: Node.js 18+, Bun, Deno, modern web browsers
48
+ - **Module Formats**: Native ESM (`dist/index.js`), CommonJS (`dist/index.cjs`), TypeScript Declarations (`dist/index.d.ts`)
49
+ - **Dependencies**: 0 production dependencies (zero bloat)
50
+
51
+ ```bash
52
+ # npm
53
+ npm install @francofantomius/jellyfin
54
+
55
+ # pnpm
56
+ pnpm add @francofantomius/jellyfin
57
+
58
+ # yarn
59
+ yarn add @francofantomius/jellyfin
60
+
61
+ # bun
62
+ bun add @francofantomius/jellyfin
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Architecture & Design Principles
68
+
69
+ 1. **Modular Domain Clients**: All server endpoints are grouped logically into dedicated modules accessible on the root `JellyfinClient` instance (e.g. `client.library`, `client.media`, `client.trickplay`).
70
+ 2. **Version Negotiation**: Seamlessly switches between Jellyfin 10.x and 12.x endpoints (e.g. `/Sessions/Capabilities` vs `/Sessions/Capabilities/Full`, playlist creation schemas).
71
+ 3. **Pluggable Persistence & Cache**: Supports `LocalStorageAdapter` for browsers, `MemoryStorage` for Node.js, and `IndexedDBCacheAdapter` for persistent browser response caching using Stale-While-Revalidate (`fetchWithCache`).
72
+ 4. **Isomorphic HTTP Transport**: Uses native `fetch` with automatic URL normalization, query serialization, and standard authorization headers (`X-Emby-Authorization` / `MediaBrowser Token`).
73
+ 5. **Type Safety**: Comprehensive TypeScript types exported for all Jellyfin DTOs, stream parameters, filter queries, and event payloads.
74
+
75
+ ---
76
+
77
+ ## Quickstart
78
+
79
+ ```typescript
80
+ import { JellyfinClient } from '@francofantomius/jellyfin';
81
+
82
+ // 1. Initialize client
83
+ const client = new JellyfinClient({
84
+ serverUrl: 'https://jellyfin.example.com',
85
+ clientInfo: {
86
+ name: 'My Media App',
87
+ version: '1.0.0',
88
+ device: 'Web Browser'
89
+ }
90
+ });
91
+
92
+ // 2. Authenticate
93
+ const auth = await client.authenticate('alice', 'password123');
94
+ console.log(`Logged in as ${auth.User.Name} (ID: ${auth.User.Id})`);
95
+
96
+ // 3. Query movies
97
+ const movies = await client.library.getMovies({ limit: 10, sortBy: 'PremiereDate', sortOrder: 'Descending' });
98
+ console.log('Recent Movies:', movies.Items.map(m => m.Name));
99
+
100
+ // 4. Generate video stream URL
101
+ const firstMovie = movies.Items[0];
102
+ const streamUrl = client.media.getVideoStreamUrl(firstMovie.Id, {
103
+ quality: '1080p'
104
+ });
105
+ console.log('Stream URL:', streamUrl);
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Client Core: JellyfinClient
111
+
112
+ ### Constructor & Options
113
+
114
+ ```typescript
115
+ new JellyfinClient(options: JellyfinClientOptions)
116
+ ```
117
+
118
+ `JellyfinClientOptions`:
119
+ - `serverUrl` (`string`, required): Jellyfin server base URL (trailing slashes are auto-trimmed).
120
+ - `accessToken` (`string`, optional): Pre-existing authorization token.
121
+ - `userId` (`string`, optional): Authenticated Jellyfin user ID.
122
+ - `clientInfo` (`ClientInfo`, optional): Client metadata:
123
+ - `name`: Application name (default: `'Jellyfin SDK'`).
124
+ - `version`: Application version (default: SDK version).
125
+ - `device`: Device name (e.g. `'Living Room TV'`, `'Chrome on Windows'`).
126
+ - `deviceId`: Unique persistent device ID (auto-generated if omitted).
127
+ - `storage` (`StorageAdapter`, optional): Storage implementation for tokens/device IDs (defaults to `getDefaultStorage()`).
128
+ - `cache` (`CacheAdapter`, optional): Cache adapter for Stale-While-Revalidate caching (defaults to `MemoryCacheAdapter`).
129
+
130
+ ### Authentication & Session Restore
131
+
132
+ ```typescript
133
+ // Login with credentials (auto-stores token and user ID, reports capabilities)
134
+ const authResult = await client.authenticate('username', 'password');
135
+
136
+ // Restore previously saved session
137
+ const savedClient = new JellyfinClient({
138
+ serverUrl: 'https://jellyfin.example.com',
139
+ accessToken: localStorage.getItem('jellyfin_token') || undefined,
140
+ userId: localStorage.getItem('jellyfin_user_id') || undefined
141
+ });
142
+
143
+ // Logout (clears tokens and emits 'logout')
144
+ await client.logout();
145
+ ```
146
+
147
+ ### Lifecycle Events
148
+
149
+ `JellyfinClient` implements `on(event, listener)` and `off(event, listener)`:
150
+
151
+ ```typescript
152
+ // Emitted after successful login or session token update
153
+ client.on('authenticated', (authResult) => {
154
+ console.log('Authenticated:', authResult.User.Name);
155
+ });
156
+
157
+ // Emitted when any server request fails with 401 or 403
158
+ client.on('unauthorized', () => {
159
+ console.warn('Session expired. Redirect to login.');
160
+ });
161
+
162
+ // Emitted upon calling client.logout()
163
+ client.on('logout', () => {
164
+ console.log('Session terminated.');
165
+ });
166
+ ```
167
+
168
+ ### Pluggable Storage & Caching
169
+
170
+ - `StorageAdapter`: Interface with `getItem`, `setItem`, `removeItem`.
171
+ - `LocalStorageAdapter`: Browser `window.localStorage`.
172
+ - `MemoryStorage`: Ephemeral in-memory `Map` (ideal for Node.js).
173
+ - `getDefaultStorage()`: Auto-selects `LocalStorageAdapter` in browsers, `MemoryStorage` in Node.js.
174
+ - `generateDeviceId()`: Generates random hex/base36 device identifiers.
175
+ - `CacheAdapter`: Interface with `get<T>`, `set<T>`, `delete`, `clear`.
176
+ - `MemoryCacheAdapter`: Fast in-memory TTL map.
177
+ - `IndexedDBCacheAdapter`: Persistent browser IndexedDB cache.
178
+ - `fetchWithCache(cache, key, fetchFn, onFreshData)`: Stale-While-Revalidate executor.
179
+
180
+ ---
181
+
182
+ ## Modules & API Reference
183
+
184
+ ### Auth & System (`client.auth`, `client.system`)
185
+
186
+ Documented in: `docs/api/auth-and-system.md`
187
+
188
+ #### `client.auth`
189
+ - `authenticateByName(username, password)`: Authenticates against `/Users/AuthenticateByName`.
190
+ - `reportCapabilities(capabilities?)`: Reports client playback capabilities (supports Jellyfin 12 `/Sessions/Capabilities/Full` and Jellyfin 10 `/Sessions/Capabilities`).
191
+ - `getUserImageUrl(userId?, options?)`: Generates user profile avatar URL.
192
+ - `logout()`: Clears credentials and active user session.
193
+ - `getUserId()` / `setUserId(id)`: Read or change current active user ID.
194
+
195
+ #### `client.system`
196
+ - `getPublicInfo()`: Unauthenticated server metadata (`GET /System/Info/Public`).
197
+ - `getInfo()`: Full server system information (`GET /System/Info`, requires auth).
198
+ - `detectVersion()`: Probes server version and sets internal version flag. Returns `Promise<boolean>` (`true` if Jellyfin 12+).
199
+ - `isV12()`: Synchronously checks if server is 12.0+.
200
+ - `isVersionAtLeast(major, minor?, patch?)`: Checks minimum semantic version threshold.
201
+ - `parseSemVer(versionString)`: Parses version strings into `{ major, minor, patch }`.
202
+
203
+ ---
204
+
205
+ ### Media Library Browsing (`client.library`)
206
+
207
+ Documented in: `docs/api/library.md`
208
+
209
+ #### Views & Folders
210
+ - `getVideoLibraries()`: Retrieves top-level video libraries (Movies, TV, Home Videos).
211
+ - `getMusicLibraries()`: Retrieves top-level music views.
212
+
213
+ #### Movies & TV Shows
214
+ - `getMovies(options?)`: Retrieves movie items with filtering (`genres`, `years`, `sortBy`, `sortOrder`, `limit`, `startIndex`).
215
+ - `getSeries(options?)`: Queries TV series.
216
+ - `getSeasons(seriesId, options?)`: Retrieves seasons for a TV series (`GET /Shows/{seriesId}/Seasons`).
217
+ - `getEpisodes(seriesId, seasonId?, options?)`: Retrieves episodes for a show or specific season (`GET /Shows/{seriesId}/Episodes`).
218
+ - `getVideos(options?)`: Queries generic video files (home videos, music videos).
219
+
220
+ #### Music
221
+ - `getAlbums(options?)`: Queries music albums.
222
+ - `getArtists(options?)`: Queries artists.
223
+ - `getSongs(options?)`: Queries individual audio tracks (`albumId`, `artistId`, etc.).
224
+ - `getFavoriteSongs(options?)`: Queries user favorite audio tracks.
225
+
226
+ #### Item Details & Stream Negotiation
227
+ - `getItem(itemId)`: Full metadata for a single item (`BaseItemDto`).
228
+ - `getPlaybackInfo(itemId, options?)`: POST to `/Items/{itemId}/PlaybackInfo` to negotiate direct play compatibility, transcode profiles, audio track selection, and bitrate constraints.
229
+
230
+ ---
231
+
232
+ ### Media, Streaming & Artwork (`client.media`)
233
+
234
+ Documented in: `docs/api/media.md`, `docs/guides/streaming-and-media.md`
235
+
236
+ #### Progressive & Adaptive Streams
237
+ - `getVideoStreamUrl(itemId, options?)`: Direct play (`static: true`) or progressive transcode stream URL (`/Videos/{itemId}/stream`). Supports `quality`, `audioStreamIndex`, `subtitleStreamIndex`, `subtitleMethod` (`'Encode' | 'Embed' | 'External'`), `useQueryToken`. Appends `ApiKey`, `api_key`, `X-Emby-Token`.
238
+ - `getVideoHlsStreamUrl(itemId, options?)`: Master HLS playlist URL (`/Videos/{itemId}/master.m3u8`) with `segmentLength`, quality presets, `useQueryToken`.
239
+ - `getAudioStreamUrl(itemId, options?)`: Progressive audio stream URL (`/Audio/{itemId}/stream` or `/Audio/{itemId}/universal`) with container, bitrate, optional explicit `mediaSourceId`, and `useQueryToken`.
240
+ - `getAudioHlsStreamUrl(itemId, options?)`: Adaptive audio HLS URL (`/Audio/{itemId}/master.m3u8`). Only appends `MediaSourceId` if explicitly specified via `options.mediaSourceId` (no unsafe fallback to item ID). Supports `useQueryToken: false` for header-based auth.
241
+ - `getStreamHeaders(options?)`: Returns `{ Authorization, ['X-Emby-Authorization'] }`. Pass `{ legacy: false }` to omit legacy `X-Emby-*` headers and prevent auth conflict / CORS preflight issues on Jellyfin 12+.
242
+
243
+ #### Subtitle Extraction & Streaming
244
+ - `getSubtitleTracks(item)`: Normalizes subtitle tracks from `item.MediaStreams` into structured `SubtitleTrackInfo` objects (`index`, `language`, `displayTitle`, `codec`, `isDefault`, `isForced`, `url`).
245
+ - `getSubtitleUrl(itemId, mediaSourceId, index, options?)`: Constructs URL for WebVTT/SRT stream.
246
+ - `getSubtitles(itemId, mediaSourceId, index, options?)`: Fetches raw subtitle plain text.
247
+
248
+ #### Responsive Artwork
249
+ - `getArtworkUrl(itemOrId, options?)`: Builds image URLs with `type` (`'Primary' | 'Art' | 'Backdrop' | 'Banner' | 'Logo' | 'Thumb' | 'Disc'`), `maxWidth`, `maxHeight`, `quality`, and `fallbackUrl`.
250
+
251
+ #### File Downloads
252
+ - `getDownloadUrl(itemId, options?)`: Direct download URL for the original source file.
253
+ - `downloadFile(itemId, options?)`: Downloads file to in-memory `Blob` with progress callback (`onProgress: (fraction: number) => void`).
254
+
255
+ ---
256
+
257
+ ### Playback Reporting & Transcode Management (`client.playback`, `client.transcode`)
258
+
259
+ Documented in: `docs/api/playback-and-transcode.md`
260
+
261
+ - `client.playback.reportStart(itemId, positionTicks?, options?)`: Notifies server that playback started (`/Sessions/Playing`).
262
+ - `client.playback.reportProgress(itemId, positionTicks?, isPaused?, options?)`: Reports ongoing playback progress (`/Sessions/Playing/Progress`).
263
+ - `client.playback.reportStopped(itemId, positionTicks?, options?)`: Reports playback ended (`/Sessions/Playing/Stopped`). When `playSessionId` is included in `options`, automatically calls `client.transcode.stopActiveEncoding(playSessionId)`.
264
+ - `client.transcode.stopActiveEncoding(playSessionId, deviceId?)` (alias `client.transcode.stop`): Explicitly terminates server-side FFmpeg transcoding process via `DELETE /Videos/ActiveEncodings`.
265
+
266
+ ---
267
+
268
+ ### Search (`client.search`)
269
+
270
+ Documented in: `docs/api/search.md`
271
+
272
+ - `search(query, options?)`: Universal search across all media types.
273
+ - `searchInLibrary(parentId, query, options?)`: Search scoped to a specific library folder.
274
+ - **Typed Shortcuts**:
275
+ - `searchMovies(query, options?)`
276
+ - `searchSeries(query, options?)`
277
+ - `searchEpisodes(query, options?)`
278
+ - `searchSongs(query, options?)`
279
+ - `searchAlbums(query, options?)`
280
+ - `searchArtists(query, options?)`
281
+ - Supported `SearchOptions`: `parentId`, `includeItemTypes`, `mediaTypes`, `genres`, `years`, `isFavorite`, `sortBy`, `sortOrder`, `limit`, `startIndex`, `fields`, `recursive`.
282
+
283
+ ---
284
+
285
+ ### In-Progress & Dynamic Feeds (`client.resume`, `client.nextUp`, `client.latest`)
286
+
287
+ #### Resume (`client.resume`)
288
+ - `getResumeItems(options?)` (alias `getItems`): Items currently in progress for the user (`GET /UserItems/Resume`). Supports `limit`, `startIndex`, `parentId`, `mediaTypes`.
289
+
290
+ #### Next Up (`client.nextUp`)
291
+ - `getNextUp(options?)` (alias `getEpisodes`): Next unplayed episodes for followed series (`GET /Shows/NextUp`). Supports `seriesId`, `limit`, `disableFirstEpisode`.
292
+
293
+ #### Recently Added (`client.latest`)
294
+ - `getLatest(options?)`: Recent media items (`GET /Users/{userId}/Items/Latest`).
295
+ - `getLatestMovies(options?)`: Convenience shortcut for recent movies.
296
+ - `getLatestEpisodes(options?)`: Convenience shortcut for recent episodes.
297
+ - `getLatestAlbums(options?)`: Convenience shortcut for recent music albums.
298
+
299
+ ---
300
+
301
+ ### Playlists, Lyrics & Favorites (`client.playlists`, `client.lyrics`, `client.favorites`)
302
+
303
+ Documented in: `docs/api/playlists-lyrics-favorites.md`
304
+
305
+ #### Playlists (`client.playlists`)
306
+ - `createPlaylist(options)`: Creates playlist (handles both Jellyfin 12 POST JSON and Jellyfin 10 query params).
307
+ - `getPlaylists()`: Lists all user playlists.
308
+ - `getPlaylistItems(playlistId, options?)`: Items in a playlist.
309
+ - `deletePlaylist(playlistId)`: Deletes a playlist.
310
+ - `addTracks(playlistId, trackIds)`: Appends tracks to a playlist.
311
+ - `removeTrack(playlistId, trackId)`: Removes item by track item ID.
312
+ - `removePlaylistItem(playlistId, playlistItemId)`: Removes item by unique playlist entry ID.
313
+ - `uploadPlaylistImage(playlistId, base64Data, mimeType)`: Uploads custom playlist cover art.
314
+
315
+ #### Lyrics (`client.lyrics`)
316
+ - `getLyrics(itemId)`: Retrieves synced or unsynced song lyrics (`GET /Audio/{itemId}/Lyrics`). Returns `{ Lyrics: [{ Start: number, Text: string }] }`.
317
+
318
+ #### Favorites (`client.favorites`)
319
+ - `markFavorite(itemId)`: Marks an item as favorite (`POST /UserFavoriteItems/{itemId}`).
320
+ - `unmarkFavorite(itemId)`: Unmarks an item as favorite (`DELETE /UserFavoriteItems/{itemId}`).
321
+
322
+ ---
323
+
324
+ ### Trickplay & Scrubbing Previews (`client.trickplay`)
325
+
326
+ Documented in: `docs/api/trickplay.md`
327
+
328
+ Provides seekbar thumbnail previews (Jellyfin 10.9+ Trickplay tile sheets):
329
+
330
+ - `getTilesHlsUrl(itemId, width, options?)`: Returns `tiles.m3u8` HLS playlist URL.
331
+ - `getTileImageUrl(itemId, width, index, options?)`: Direct URL for a specific tile sprite sheet.
332
+ - `getTrickplayManifest(itemId, width, options?)`: Fetches and parses `tiles.m3u8` into structured tile definitions.
333
+ - `getAvailableResolutions(item, mediaSourceId?)`: Discovers available thumbnail widths from item metadata (e.g. `[320, 640]`).
334
+ - `getTrickplayInfo(item, width?, mediaSourceId?)`: Retrieves `TrickplayInfoDto` (sheet dimensions, thumbnail interval, tile counts).
335
+ - `getThumbnailForTimestamp({ item, timestampMs, width })`: Computes exact sprite coordinates (`x`, `y`, `width`, `height`, `tileIndex`, `imageUrl`) for any playback timestamp.
336
+
337
+ ---
338
+
339
+ ### Chapters & Intro/Credits Skipping (`client.chapters`)
340
+
341
+ Documented in: `docs/api/chapters-and-markers.md`
342
+
343
+ - `extractChapters(item)`: Extracts chapter markers from existing `BaseItemDto`.
344
+ - `getChapters(itemId)`: Fetches chapters from server (`Fields=Chapters`).
345
+ - `getChapterImageUrl(itemId, chapterIndex, options?)`: Generates chapter thumbnail URL (`/Items/{itemId}/Images/Chapter/{chapterIndex}`).
346
+ - `getMediaSegments(itemId, options?)`: Typed media segments (`Intro`, `Outro`, `Commercial`, `Recap`) via `GET /MediaSegments` (Jellyfin 10.10+).
347
+ - `getIntroCredits(itemOrChaptersOrId)`: Resolves intro/outro boundaries via MediaSegments or chapter title heuristics.
348
+ - `findCurrentSegment(positionTicks, markers)`: Determines if current playback position is inside an intro or credits segment.
349
+ - `getSkipPosition(positionTicks, markers)`: Calculates target timestamp ticks to skip over an intro or credits block.
350
+
351
+ ---
352
+
353
+ ### Genres, Studios & Collections (`client.genres`, `client.collections`)
354
+
355
+ Documented in: `docs/api/genres-and-collections.md`
356
+
357
+ #### Genres & Studios (`client.genres`)
358
+ - `getGenres(options?)` / `getGenre(name, options?)`: Browse video genres.
359
+ - `getMusicGenres(options?)` / `getMusicGenre(name, options?)`: Browse music genres.
360
+ - `getStudios(options?)` / `getStudio(name, options?)`: Browse production studios.
361
+
362
+ #### BoxSet Collections (`client.collections`)
363
+ - `getCollections(options?)`: Lists all BoxSet collections.
364
+ - `getCollectionItems(collectionId, options?)`: Queries items inside a BoxSet.
365
+ - `createCollection(options)`: Creates a new BoxSet (`POST /Collections`).
366
+ - `addToCollection(collectionId, itemIds)`: Adds items to an existing BoxSet.
367
+ - `removeFromCollection(collectionId, itemIds)`: Removes items from a BoxSet.
368
+
369
+ ---
370
+
371
+ ### Display Preferences (`client.displayPreferences`)
372
+
373
+ Documented in: `docs/api/display-preferences.md`
374
+
375
+ Persists user view layouts and sorting across sessions via `/DisplayPreferences/{id}`:
376
+
377
+ - `getDisplayPreferences(id, options?)`: Retrieves display settings for a view/folder.
378
+ - `updateDisplayPreferences(id, preferences, options?)`: Saves updated view preferences.
379
+ - `setCustomPreference(id, key, value, options?)`: Stores custom client-specific key-value pairs in `CustomPrefs`.
380
+ - `setSortPreferences(id, sortBy, sortOrder, options?)`: Updates sort field and direction.
381
+ - `setViewType(id, viewType, options?)`: Updates presentation layout (`'Poster'`, `'Thumb'`, `'Banner'`, `'List'`).
382
+
383
+ ---
384
+
385
+ ### Remote Session Control (`client.sessions`)
386
+
387
+ Documented in: `docs/api/sessions-and-remote.md`, `docs/guides/realtime-and-remote.md`
388
+
389
+ Controls secondary devices and active players across the local network:
390
+
391
+ - `getSessions(options?)`: Queries active sessions on the server.
392
+ - `play(sessionId, itemIds, options?)`: Instructs remote player to play items (`PlayNow`, `PlayNext`, `PlayLast`).
393
+ - `playPause(sessionId)` / `pause(sessionId)` / `unpause(sessionId)` / `stop(sessionId)`.
394
+ - `seek(sessionId, positionTicks)`: Seeks remote player (1 sec = 10,000,000 ticks).
395
+ - `nextTrack(sessionId)` / `previousTrack(sessionId)`.
396
+ - `setVolume(sessionId, volume)`: Volume from `0` to `100`.
397
+ - `mute(sessionId)` / `unmute(sessionId)` / `toggleMute(sessionId)`.
398
+ - `setAudioStreamIndex(sessionId, index)` / `setSubtitleStreamIndex(sessionId, index)` (`-1` turns off subtitles).
399
+ - `sendMessage(sessionId, options)`: Displays modal dialog alert on remote screen.
400
+ - `viewItem(sessionId, itemType, itemId, itemName)`: Commands remote client to navigate to an item's details view.
401
+
402
+ ---
403
+
404
+ ### Real-Time WebSocket Events (`client.websocket`)
405
+
406
+ Documented in: `docs/api/websocket.md`, `docs/guides/realtime-and-remote.md`
407
+
408
+ - `connect(options?)`: Connects to `/socket` with `autoReconnect`, `reconnectIntervalMs`, `keepAliveIntervalMs`.
409
+ - `disconnect()`: Closes socket connection cleanly.
410
+ - `isConnected`: Boolean getter for connection state.
411
+ - **Subscriptions**:
412
+ - `startSessionsSubscription(intervalMs?)` / `stopSessionsSubscription()`
413
+ - `startScheduledTasksSubscription(intervalMs?)` / `stopScheduledTasksSubscription()`
414
+ - `startActivityLogSubscription(intervalMs?)` / `stopActivityLogSubscription()`
415
+ - **Event Listeners (`client.websocket.on`)**:
416
+ - `'open'`: Connected.
417
+ - `'close'`: Disconnected.
418
+ - `'error'`: Socket error.
419
+ - `'message'`: Raw incoming WebSocket message.
420
+ - `'libraryChanged'`: Library content created, updated, or deleted (`ItemsAdded`, `ItemsUpdated`, `ItemsRemoved`).
421
+ - `'userDataChanged'`: User watch status, progress ticks, or favorite toggles updated.
422
+ - `'sessions'`: Active server playback sessions.
423
+ - `'restartRequired'` / `'serverShuttingDown'`: Server lifecycle events.
424
+
425
+ ---
426
+
427
+ ### Quick Connect Device Pairing (`client.quickConnect`)
428
+
429
+ Documented in: `docs/api/quick-connect.md`, `docs/guides/realtime-and-remote.md`
430
+
431
+ Passwordless pairing flow for Smart TVs and game consoles:
432
+
433
+ 1. **Initiating Device (TV / Console)**:
434
+ - `isEnabled()`: Checks if Quick Connect is enabled on server.
435
+ - `initiate()`: Returns `{ Code, Secret }`. Display `Code` on screen.
436
+ - `poll(secret, options?)`: Polls until authenticated, returning access token and user ID.
437
+ 2. **Authorizing Device (Phone / PC already logged in)**:
438
+ - `authorize(code, userId?)`: Approves code entered by user.
439
+
440
+ ---
441
+
442
+ ### Offline Storage Manager (`client.offline`)
443
+
444
+ Documented in: `docs/api/offline.md`, `docs/guides/offline-and-downloads.md`
445
+
446
+ Browser IndexedDB media caching for offline audio and video playback:
447
+
448
+ - `isSupported()`: Checks if IndexedDB is available in the current environment.
449
+ - `downloadItem(item, onProgress?, options?)`: Downloads media file blob, artwork, and metadata into IndexedDB with progress callback (`fraction: 0..1`).
450
+ - `downloadItems(items, onProgress?, options?)`: Batch downloads items with aggregated progress tracking.
451
+ - `isItemDownloaded(itemId)`: Verifies if item is cached offline.
452
+ - `getDownloadedBlob(itemId)`: Retrieves raw media `Blob`.
453
+ - `getDownloadedBlobUrl(itemId)`: Creates object URL (`blob:...`) for HTML5 `<video>` or `<audio>` elements.
454
+ - `getAllDownloads()`: Lists all stored records with file size and metadata.
455
+ - `removeDownload(itemId)`: Deletes item from IndexedDB.
456
+ - `clearAllDownloads()`: Clears all offline items.
457
+
458
+ ---
459
+
460
+ ## Quality Presets & Utilities
461
+
462
+ Exported directly from root package:
463
+
464
+ ### Video Quality Presets (`VIDEO_QUALITY_PRESETS`)
465
+ - `4k`: 40 Mbps, 3840x2160
466
+ - `1080p-high`: 20 Mbps, 1920x1080
467
+ - `1080p`: 10 Mbps, 1920x1080
468
+ - `720p-high`: 8 Mbps, 1280x720
469
+ - `720p`: 4 Mbps, 1280x720
470
+ - `480p`: 2 Mbps, 720x480
471
+ - `360p`: 1 Mbps, 640x360
472
+ - `240p`: 500 kbps, 426x240
473
+ - `direct`: Original source bitrate and resolution (no transcode cap)
474
+
475
+ ### Audio Quality Presets (`AUDIO_QUALITY_PRESETS`)
476
+ - `320k`: 320 kbps
477
+ - `256k`: 256 kbps
478
+ - `192k`: 192 kbps
479
+ - `128k`: 128 kbps
480
+ - `64k`: 64 kbps
481
+
482
+ ### Helper Functions
483
+ - `resolveVideoQuality(presetOrOptions)`: Resolves preset key or custom options into `{ maxBitrate, maxWidth, maxHeight }`.
484
+ - `resolveAudioQuality(presetOrOptions)`: Resolves audio preset into `{ maxBitrate }`.
485
+ - `formatBitrate(bitrate)`: Formats numeric bitrates into human-readable strings (e.g. `10_000_000` -> `"10 Mbps"`, `320_000` -> `"320 kbps"`).
486
+
487
+ ---
488
+
489
+ ## Error Handling
490
+
491
+ All SDK errors inherit from standard TypeScript Error classes:
492
+
493
+ - `JellyfinError`: Base class for all SDK runtime errors.
494
+ - `JellyfinApiError`: Thrown on non-2xx HTTP responses from Jellyfin server.
495
+ - `status`: HTTP response status code (e.g. `400`, `404`, `500`).
496
+ - `endpoint`: API route that produced the error.
497
+ - `JellyfinAuthError`: Subclass of `JellyfinApiError` thrown on authentication failures (`401` / `403`).
498
+
499
+ ```typescript
500
+ import { JellyfinError, JellyfinApiError, JellyfinAuthError } from '@francofantomius/jellyfin';
501
+
502
+ try {
503
+ await client.authenticate('admin', 'wrong-password');
504
+ } catch (error) {
505
+ if (error instanceof JellyfinAuthError) {
506
+ console.error('Invalid username or password!');
507
+ } else if (error instanceof JellyfinApiError) {
508
+ console.error(`API error [${error.status}] at ${error.endpoint}:`, error.message);
509
+ } else if (error instanceof JellyfinError) {
510
+ console.error('SDK error:', error.message);
511
+ }
512
+ }
513
+ ```
514
+
515
+ ---
516
+
517
+ ## Documentation Index
518
+
519
+ For in-depth guides and detailed API signatures, see the `docs/` directory:
520
+
521
+ - **Guides**:
522
+ - Getting Started: `docs/guides/getting-started.md`
523
+ - Streaming, Playback & Artwork: `docs/guides/streaming-and-media.md`
524
+ - Offline Storage & Downloads: `docs/guides/offline-and-downloads.md`
525
+ - Real-Time WebSockets & Remote Control: `docs/guides/realtime-and-remote.md`
526
+ - **API Reference**:
527
+ - Core Client (`JellyfinClient`): `docs/api/client.md`
528
+ - Auth & System (`client.auth`, `client.system`): `docs/api/auth-and-system.md`
529
+ - Library Browsing (`client.library`): `docs/api/library.md`
530
+ - Media & Streaming (`client.media`): `docs/api/media.md`
531
+ - Playback & Transcoding (`client.playback`, `client.transcode`): `docs/api/playback-and-transcode.md`
532
+ - Search (`client.search`): `docs/api/search.md`
533
+ - Playlists, Lyrics & Favorites (`client.playlists`, `client.lyrics`, `client.favorites`): `docs/api/playlists-lyrics-favorites.md`
534
+ - Trickplay Scrubbing Previews (`client.trickplay`): `docs/api/trickplay.md`
535
+ - Chapters & Media Markers (`client.chapters`): `docs/api/chapters-and-markers.md`
536
+ - Genres, Studios & Collections (`client.genres`, `client.collections`): `docs/api/genres-and-collections.md`
537
+ - User Display Preferences (`client.displayPreferences`): `docs/api/display-preferences.md`
538
+ - Remote Sessions (`client.sessions`): `docs/api/sessions-and-remote.md`
539
+ - Real-Time WebSocket (`client.websocket`): `docs/api/websocket.md`
540
+ - Quick Connect (`client.quickConnect`): `docs/api/quick-connect.md`
541
+ - Offline IndexedDB Storage (`client.offline`): `docs/api/offline.md`
542
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@francofantomius/jellyfin",
3
- "version": "0.2.0",
3
+ "version": "0.4.1",
4
4
  "description": "A lightweight, modular, and type-safe Jellyfin SDK for Node.js and the Browser.",
5
5
  "author": "Franco Fantomius",
6
6
  "license": "MIT",
@@ -35,7 +35,8 @@
35
35
  "files": [
36
36
  "dist",
37
37
  "README.md",
38
- "LICENSE"
38
+ "LICENSE",
39
+ "llms.txt"
39
40
  ],
40
41
  "scripts": {
41
42
  "build": "tsup",
@@ -43,12 +44,13 @@
43
44
  "test": "vitest run",
44
45
  "test:watch": "vitest",
45
46
  "typecheck": "tsc --noEmit",
46
- "prepublishOnly": "npm run build"
47
+ "prepublishOnly": "npm run build",
48
+ "clean": "git clean -fdX"
47
49
  },
48
50
  "devDependencies": {
49
51
  "@types/node": "^22.13.1",
50
- "tsup": "^8.3.6",
51
- "typescript": "^5.7.3",
52
- "vitest": "^3.0.5"
52
+ "tsup": "^8.5.1",
53
+ "typescript": "^6.0.3",
54
+ "vitest": "^5.0.0"
53
55
  }
54
56
  }