@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/README.md +181 -1
- package/dist/index.cjs +1527 -172
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +686 -9
- package/dist/index.d.ts +686 -9
- package/dist/index.js +1520 -173
- package/dist/index.js.map +1 -1
- package/llms.txt +542 -0
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
A modern, lightweight, modular, and type-safe Jellyfin SDK for Node.js and the Browser.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@francofantomius/jellyfin)
|
|
6
|
+
[](https://francofantomius.github.io/jellyfin-sdk/)
|
|
6
7
|
[](LICENSE)
|
|
7
8
|
|
|
8
|
-
**Current Version: `v0.
|
|
9
|
+
**Current Version: `v0.4.1`** (THE PROJECT IS BEING ACTIVELY DEVELOPED. WAIT FOR VERSION 0.5.0 TO USE IT)
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
---
|
|
@@ -29,6 +30,14 @@ A modern, lightweight, modular, and type-safe Jellyfin SDK for Node.js and the B
|
|
|
29
30
|
- `client.nextUp` - Next unplayed TV show episodes (`/Shows/NextUp`).
|
|
30
31
|
- `client.latest` - Recently added media (movies, episodes, albums) (`/Users/{userId}/Items/Latest`).
|
|
31
32
|
- `client.transcode` - Teardown active transcoding sessions (`/Videos/ActiveEncodings`).
|
|
33
|
+
- `client.websocket` - Real-time WebSocket event streaming, keep-alive heartbeat, and automated reconnection.
|
|
34
|
+
- `client.sessions` - Remote control active sessions, playback commands, volume adjustment, and message modals.
|
|
35
|
+
- `client.quickConnect` - Quick Connect pairing flow for TVs and secondary devices.
|
|
36
|
+
- `client.trickplay` - Jellyfin 10.9+ trickplay HLS tile manifests, image tiles, and scrubbing thumbnail coordinate calculations.
|
|
37
|
+
- `client.chapters` - Chapter navigation, preview thumbnails, typed MediaSegments, and intro/credits skipping.
|
|
38
|
+
- `client.genres` - Video genres, music genres, and production studios browsing.
|
|
39
|
+
- `client.collections` - BoxSet collection querying, creation, and item membership management.
|
|
40
|
+
- `client.displayPreferences` - Persist and retrieve view types, sorting configurations, and custom client UI preferences via `/DisplayPreferences/{id}`.
|
|
32
41
|
- **Quality Settings & Presets**: Standard video (4K, 1080p, 720p, 480p, 360p, direct) and audio (320k, 256k, 192k, 128k, direct) presets and resolution utilities.
|
|
33
42
|
- **Subtitles**: Direct VTT/SRT subtitle streaming URLs, raw text fetching, and automated track extraction from media metadata.
|
|
34
43
|
- **Direct Downloads**: Server file download URLs, progress-tracked file downloads, and offline caching.
|
|
@@ -349,6 +358,177 @@ const series = await client.search.searchSeries('Breaking Bad');
|
|
|
349
358
|
|
|
350
359
|
---
|
|
351
360
|
|
|
361
|
+
### 14. Real-time WebSocket Event Streaming
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
// Connect to real-time WebSocket event feed
|
|
365
|
+
client.websocket.connect({
|
|
366
|
+
autoReconnect: true,
|
|
367
|
+
keepAliveIntervalMs: 30000
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Listen to connection state
|
|
371
|
+
client.websocket.on('open', () => console.log('WebSocket connected'));
|
|
372
|
+
client.websocket.on('close', (code, reason) => console.log('WebSocket closed', code, reason));
|
|
373
|
+
|
|
374
|
+
// Listen for library updates (scanned/added items)
|
|
375
|
+
client.websocket.on('libraryChanged', (data) => {
|
|
376
|
+
console.log('Library changed. Items added:', data.ItemsAdded);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Listen for user data updates (played state, favorites)
|
|
380
|
+
client.websocket.on('userDataChanged', (data) => {
|
|
381
|
+
console.log('User data changed for user:', data.UserId);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Subscribe to live session updates across the server
|
|
385
|
+
client.websocket.startSessionsSubscription(2000);
|
|
386
|
+
client.websocket.on('sessions', (sessions) => {
|
|
387
|
+
console.log('Active server sessions:', sessions);
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
### 15. Remote Control & Sessions Management
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
// Query all active sessions
|
|
397
|
+
const sessions = await client.sessions.getSessions();
|
|
398
|
+
const targetSession = sessions.find((s) => s.DeviceName === 'Living Room TV');
|
|
399
|
+
|
|
400
|
+
if (targetSession) {
|
|
401
|
+
// Remote playback control
|
|
402
|
+
await client.sessions.play(targetSession.Id, ['movie-id-1'], {
|
|
403
|
+
playCommand: 'PlayNow',
|
|
404
|
+
startPositionTicks: 0
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Playback commands
|
|
408
|
+
await client.sessions.pause(targetSession.Id);
|
|
409
|
+
await client.sessions.playPause(targetSession.Id);
|
|
410
|
+
await client.sessions.seek(targetSession.Id, 120_000_000); // 12 seconds in ticks
|
|
411
|
+
|
|
412
|
+
// Volume control
|
|
413
|
+
await client.sessions.setVolume(targetSession.Id, 75);
|
|
414
|
+
await client.sessions.toggleMute(targetSession.Id);
|
|
415
|
+
|
|
416
|
+
// Send an on-screen dialog message
|
|
417
|
+
await client.sessions.sendMessage(targetSession.Id, {
|
|
418
|
+
header: 'Notification',
|
|
419
|
+
text: 'Dinner is ready in 5 minutes!',
|
|
420
|
+
timeoutMs: 8000
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// Instruct session to navigate to an item
|
|
424
|
+
await client.sessions.viewItem(targetSession.Id, 'Movie', 'movie-id-1', 'Inception');
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
### 16. Quick Connect Device Pairing Flow
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
// On a secondary device (e.g. Smart TV or CLI app):
|
|
434
|
+
if (await client.quickConnect.isEnabled()) {
|
|
435
|
+
const { Code, Secret } = await client.quickConnect.initiate();
|
|
436
|
+
console.log(`Enter this code on your phone/PC: ${Code}`);
|
|
437
|
+
|
|
438
|
+
// Automatically poll until authorized
|
|
439
|
+
const state = await client.quickConnect.poll(Secret, { intervalMs: 2000, timeoutMs: 120000 });
|
|
440
|
+
if (state.Authenticated && state.AuthenticationToken) {
|
|
441
|
+
client.accessToken = state.AuthenticationToken;
|
|
442
|
+
if (state.UserId) client.userId = state.UserId;
|
|
443
|
+
console.log('Successfully authenticated via Quick Connect!');
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// On an already authenticated device (e.g. phone or PC):
|
|
448
|
+
await client.quickConnect.authorize('123 456');
|
|
449
|
+
console.log('Device authorized!');
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
### 17. Trickplay & Scrubbing Thumbnails
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
// Fetch manifest and available resolutions
|
|
458
|
+
const item = await client.library.getItem('video-id');
|
|
459
|
+
const resolutions = client.trickplay.getAvailableResolutions(item); // e.g. [320]
|
|
460
|
+
|
|
461
|
+
// Calculate exact sprite coordinates for scrubbing seekbar
|
|
462
|
+
const thumbnail = client.trickplay.getThumbnailForTimestamp({
|
|
463
|
+
item,
|
|
464
|
+
timestampMs: 45000, // 45 seconds into the video
|
|
465
|
+
width: 320
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
if (thumbnail) {
|
|
469
|
+
console.log(`Image: ${thumbnail.imageUrl}`);
|
|
470
|
+
console.log(`Offset: x=${thumbnail.x}px, y=${thumbnail.y}px, size=${thumbnail.width}x${thumbnail.height}px`);
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
### 18. Chapters & Intro / Credits Skipping
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// Fetch item chapters and images
|
|
480
|
+
const chapters = await client.chapters.getChapters('episode-id');
|
|
481
|
+
const chapterImage = client.chapters.getChapterImageUrl('episode-id', 1, { maxWidth: 400 });
|
|
482
|
+
|
|
483
|
+
// Detect intro/credits from /MediaSegments or chapter heuristics
|
|
484
|
+
const markers = await client.chapters.getIntroCredits('episode-id');
|
|
485
|
+
|
|
486
|
+
// During playback, check if skip button should appear
|
|
487
|
+
const currentTicks = 850_000_000; // 85s
|
|
488
|
+
const activeSegment = client.chapters.findCurrentSegment(currentTicks, markers);
|
|
489
|
+
if (activeSegment?.type === 'Intro') {
|
|
490
|
+
const skipToTicks = client.chapters.getSkipPosition(currentTicks, markers);
|
|
491
|
+
console.log(`Skip Intro available -> Jump to ${skipToTicks / 10_000_000}s`);
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
### 19. Genres, Studios & BoxSet Collections
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
// Browse genres and studios
|
|
501
|
+
const movieGenres = await client.genres.getGenres({ limit: 20 });
|
|
502
|
+
const rockMusic = await client.genres.getMusicGenre('Rock');
|
|
503
|
+
const studios = await client.genres.getStudios({ searchTerm: 'Universal' });
|
|
504
|
+
|
|
505
|
+
// Browse and manage BoxSet Collections
|
|
506
|
+
const collections = await client.collections.getCollections();
|
|
507
|
+
const newBoxSet = await client.collections.createCollection({
|
|
508
|
+
name: 'Alien Anthology',
|
|
509
|
+
ids: ['alien-1', 'alien-2']
|
|
510
|
+
});
|
|
511
|
+
await client.collections.addToCollection(newBoxSet.Id, ['alien-3', 'alien-4']);
|
|
512
|
+
await client.collections.removeFromCollection(newBoxSet.Id, 'alien-1');
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
### 20. User Display Preferences
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
// Get folder/view UI preferences
|
|
521
|
+
const prefs = await client.displayPreferences.getDisplayPreferences('folder-id');
|
|
522
|
+
|
|
523
|
+
// Update view layout and sorting
|
|
524
|
+
await client.displayPreferences.setViewType('folder-id', 'Poster');
|
|
525
|
+
await client.displayPreferences.setSortPreferences('folder-id', 'CommunityRating', 'Descending');
|
|
526
|
+
await client.displayPreferences.setCustomPreference('folder-id', 'showBadges', 'true');
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
352
531
|
## License
|
|
353
532
|
|
|
354
533
|
MIT © [Franco Fantomius](https://github.com/francofantomius)
|
|
534
|
+
|