@francofantomius/jellyfin 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Franco Fantomius
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,285 @@
1
+ # @francofantomius/jellyfin
2
+
3
+ A modern, lightweight, modular, and type-safe Jellyfin SDK for Node.js and the Browser.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@francofantomius/jellyfin.svg)](https://www.npmjs.com/package/@francofantomius/jellyfin)
6
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ ---
9
+
10
+ ## Features
11
+
12
+ - **TypeScript-first**: Full type definitions for Jellyfin items, audio and video metadata, subtitles, search parameters, and responses.
13
+ - **Universal / Isomorphic**: Runs seamlessly in Node.js (scripts, server-side apps, SSR) and the Browser (vanilla, React, Vue, Svelte, Electron).
14
+ - **Dual module exports**: Native ESM (`import`) and CommonJS (`require`).
15
+ - **Clean modular architecture**:
16
+ - `client.auth` - Server authentication, user profiles, capabilities reporting.
17
+ - `client.library` - Browse music and video libraries (movies, series, seasons, episodes, videos, search, playback info).
18
+ - `client.media` - Universal audio and video stream URLs, HLS playlists, subtitles, direct downloads, and responsive artwork.
19
+ - `client.playlists` - Create, edit, delete playlists, manage tracks, and upload cover images.
20
+ - `client.playback` - Report playback start, progress, and stop to Jellyfin for scrobbling and playback sync.
21
+ - `client.lyrics` - Fetch synced or unsynced song lyrics.
22
+ - `client.favorites` - Toggle favorite items.
23
+ - `client.offline` - Browser IndexedDB media download manager for offline audio and video playback.
24
+ - **Quality Settings & Presets**: Standard video (4K, 1080p, 720p, 480p, 360p, direct) and audio (320k, 256k, 192k, 128k, direct) presets and resolution utilities.
25
+ - **Subtitles**: Direct VTT/SRT subtitle streaming URLs, raw text fetching, and automated track extraction from media metadata.
26
+ - **Direct Downloads**: Server file download URLs, progress-tracked file downloads, and offline caching.
27
+ - **Flexible storage & caching**: Built-in memory and local storage adapters with Stale-While-Revalidate caching support.
28
+ - **Event-driven**: Listen to events like `unauthorized`, `authenticated`, and `logout`.
29
+
30
+ ---
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ npm install @francofantomius/jellyfin
36
+ ```
37
+
38
+ Or with yarn / pnpm:
39
+
40
+ ```bash
41
+ pnpm add @francofantomius/jellyfin
42
+ yarn add @francofantomius/jellyfin
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Quick Start
48
+
49
+ ### 1. Initialize Client & Authenticate
50
+
51
+ ```typescript
52
+ import { JellyfinClient } from '@francofantomius/jellyfin';
53
+
54
+ const client = new JellyfinClient({
55
+ serverUrl: 'https://jellyfin.example.com',
56
+ clientInfo: {
57
+ name: 'My Media App',
58
+ version: '1.0.0',
59
+ device: 'Web Browser'
60
+ }
61
+ });
62
+
63
+ // Authenticate using username and password
64
+ const authResult = await client.authenticate('username', 'password');
65
+ console.log('Logged in as:', authResult.User.Name);
66
+ console.log('Access token:', client.accessToken);
67
+ ```
68
+
69
+ If you already have a stored token and user ID:
70
+
71
+ ```typescript
72
+ const client = new JellyfinClient({
73
+ serverUrl: 'https://jellyfin.example.com',
74
+ accessToken: 'your-saved-access-token',
75
+ userId: 'your-user-id'
76
+ });
77
+ ```
78
+
79
+ ---
80
+
81
+ ### 2. Browse Music, Movies & Series
82
+
83
+ ```typescript
84
+ // Browse music
85
+ const albums = await client.library.getAlbums({ limit: 20 });
86
+ const songs = await client.library.getSongs({ limit: 50, albumId: albums.Items[0]?.Id });
87
+
88
+ // Browse video libraries
89
+ const videoViews = await client.library.getVideoLibraries();
90
+
91
+ // Browse movies (with genres and year filtering)
92
+ const movies = await client.library.getMovies({
93
+ limit: 25,
94
+ genres: ['Action', 'Sci-Fi'],
95
+ sortBy: 'CommunityRating',
96
+ sortOrder: 'Descending'
97
+ });
98
+
99
+ // Browse TV series, seasons, and episodes
100
+ const series = await client.library.getSeries({ limit: 20 });
101
+ const seasons = await client.library.getSeasons(series.Items[0].Id);
102
+ const episodes = await client.library.getEpisodes(series.Items[0].Id, seasons.Items[0]?.Id);
103
+
104
+ // Request playback negotiation info (sources, audio/subtitle streams)
105
+ const playbackInfo = await client.library.getPlaybackInfo(movies.Items[0].Id);
106
+ ```
107
+
108
+ ---
109
+
110
+ ### 3. Video Streaming & Quality Presets
111
+
112
+ ```typescript
113
+ import { VIDEO_QUALITY_PRESETS, resolveVideoQuality, formatBitrate } from '@francofantomius/jellyfin';
114
+
115
+ const movieId = 'some-movie-id';
116
+
117
+ // Direct play video stream URL
118
+ const directVideoUrl = client.media.getVideoStreamUrl(movieId, {
119
+ static: true,
120
+ container: 'mp4'
121
+ });
122
+
123
+ // Transcoded video stream URL with a quality preset
124
+ const transcodeVideoUrl = client.media.getVideoStreamUrl(movieId, {
125
+ quality: '1080p', // Automatically sets 10 Mbps bitrate, 1920x1080
126
+ audioStreamIndex: 1,
127
+ subtitleStreamIndex: 2,
128
+ subtitleMethod: 'Encode'
129
+ });
130
+
131
+ // Adaptive HLS video playlist URL
132
+ const videoHlsUrl = client.media.getVideoHlsStreamUrl(movieId, {
133
+ quality: '720p',
134
+ segmentLength: 4
135
+ });
136
+
137
+ // Inspect quality presets
138
+ console.log(VIDEO_QUALITY_PRESETS['1080p'].label); // "1080p (10 Mbps)"
139
+ console.log(formatBitrate(10_000_000)); // "10 Mbps"
140
+ ```
141
+
142
+ ---
143
+
144
+ ### 4. Subtitles
145
+
146
+ ```typescript
147
+ const movie = movies.Items[0];
148
+
149
+ // Extract subtitle tracks from item metadata
150
+ const subtitleTracks = client.media.getSubtitleTracks(movie);
151
+ subtitleTracks.forEach((track) => {
152
+ console.log(`${track.displayTitle} (${track.codec}) -> ${track.url}`);
153
+ });
154
+
155
+ // Generate subtitle stream URL
156
+ const vttUrl = client.media.getSubtitleUrl(movie.Id, movie.MediaSources[0].Id, 2, {
157
+ format: 'vtt'
158
+ });
159
+
160
+ // Fetch raw subtitle text (e.g. WebVTT or SRT content)
161
+ const subtitleText = await client.media.getSubtitles(movie.Id, movie.MediaSources[0].Id, 2, {
162
+ format: 'vtt'
163
+ });
164
+ ```
165
+
166
+ ---
167
+
168
+ ### 5. Downloads & Offline Storage
169
+
170
+ ```typescript
171
+ const itemId = 'some-item-id';
172
+
173
+ // Get server direct download URL (unmodified original file)
174
+ const downloadUrl = client.media.getDownloadUrl(itemId, { filename: 'movie.mp4' });
175
+
176
+ // Download file as a Blob with progress
177
+ const blob = await client.media.downloadFile(itemId, {
178
+ onProgress: (fraction) => console.log(`Download: ${(fraction * 100).toFixed(0)}%`)
179
+ });
180
+
181
+ // Offline Storage Manager (IndexedDB caching in browser)
182
+ if (client.offline.isSupported()) {
183
+ // Download audio or video item
184
+ await client.offline.downloadItem(movie, (progress) => {
185
+ console.log(`Offline download: ${(progress * 100).toFixed(0)}%`);
186
+ });
187
+
188
+ // Check if downloaded
189
+ const isDownloaded = await client.offline.isItemDownloaded(movie.Id);
190
+
191
+ // Get local object URL for offline playback
192
+ const localBlobUrl = await client.offline.getDownloadedBlobUrl(movie.Id);
193
+ }
194
+ ```
195
+
196
+ ---
197
+
198
+ ### 6. Audio Streaming & Artwork URLs
199
+
200
+ ```typescript
201
+ const trackId = 'some-track-id';
202
+
203
+ // Universal audio streaming URL
204
+ const streamUrl = client.media.getAudioStreamUrl(trackId, {
205
+ quality: '320k', // or maxStreamingBitrate: '320000'
206
+ startTimeTicks: 0
207
+ });
208
+
209
+ // Artwork URL (with custom dimensions and quality)
210
+ const album = albums.Items[0];
211
+ const artworkUrl = client.media.getArtworkUrl(album, {
212
+ maxWidth: 500,
213
+ quality: 90,
214
+ fallbackUrl: '/assets/placeholder-cover.png'
215
+ });
216
+ ```
217
+
218
+ ---
219
+
220
+ ### 7. Playback Reporting (Scrobbling & Sync)
221
+
222
+ ```typescript
223
+ // When track starts playing
224
+ await client.playback.reportStart(trackId, 0);
225
+
226
+ // Periodic progress update (e.g. every 10 seconds)
227
+ await client.playback.reportProgress(trackId, positionTicks, false);
228
+
229
+ // When playback stops or completes
230
+ await client.playback.reportStopped(trackId, positionTicks);
231
+ ```
232
+
233
+ ---
234
+
235
+ ### 8. Playlists
236
+
237
+ ```typescript
238
+ // Create a playlist
239
+ const playlistId = await client.playlists.createPlaylist({
240
+ name: 'Favorites 2026',
241
+ trackIds: ['track-id-1', 'track-id-2']
242
+ });
243
+
244
+ // Add more tracks
245
+ await client.playlists.addTracks(playlistId, ['track-id-3']);
246
+
247
+ // Upload a custom cover image (base64)
248
+ await client.playlists.uploadPlaylistImage(playlistId, base64JpgData, 'image/jpeg');
249
+
250
+ // Fetch playlist items
251
+ const playlistItems = await client.playlists.getPlaylistItems(playlistId);
252
+
253
+ // Delete playlist
254
+ await client.playlists.deletePlaylist(playlistId);
255
+ ```
256
+
257
+ ---
258
+
259
+ ### 9. Synced Lyrics & Favorites
260
+
261
+ ```typescript
262
+ // Fetch song lyrics
263
+ const lyrics = await client.lyrics.getLyrics(trackId);
264
+ console.log(lyrics?.Lyrics); // [{ Start: 10000000, Text: 'Hello world' }, ...]
265
+
266
+ // Toggle favorites
267
+ await client.favorites.markFavorite(trackId);
268
+ await client.favorites.unmarkFavorite(trackId);
269
+ ```
270
+
271
+ ---
272
+
273
+ ### 10. Handling Unauthorized Sessions
274
+
275
+ ```typescript
276
+ client.on('unauthorized', () => {
277
+ console.warn('Jellyfin session has expired. Redirecting to login...');
278
+ });
279
+ ```
280
+
281
+ ---
282
+
283
+ ## License
284
+
285
+ MIT © [Franco Fantomius](https://github.com/francofantomius)