@francofantomius/jellyfin 0.1.0 → 0.2.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/README.md +69 -0
- package/dist/index.cjs +392 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +175 -3
- package/dist/index.d.ts +175 -3
- package/dist/index.js +387 -15
- package/dist/index.js.map +1 -1
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -5,6 +5,9 @@ A modern, lightweight, modular, and type-safe Jellyfin SDK for Node.js and the B
|
|
|
5
5
|
[](https://www.npmjs.com/package/@francofantomius/jellyfin)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
+
**Current Version: `v0.2.0`** (THE PROJECT IS BEING ACTIVELY DEVELOPED. WAIT FOR VERSION 0.5.0 TO USE IT)
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
---
|
|
9
12
|
|
|
10
13
|
## Features
|
|
@@ -15,12 +18,17 @@ A modern, lightweight, modular, and type-safe Jellyfin SDK for Node.js and the B
|
|
|
15
18
|
- **Clean modular architecture**:
|
|
16
19
|
- `client.auth` - Server authentication, user profiles, capabilities reporting.
|
|
17
20
|
- `client.library` - Browse music and video libraries (movies, series, seasons, episodes, videos, search, playback info).
|
|
21
|
+
- `client.search` - Dedicated universal search with granular item type, genre, year, and library view scoping.
|
|
18
22
|
- `client.media` - Universal audio and video stream URLs, HLS playlists, subtitles, direct downloads, and responsive artwork.
|
|
19
23
|
- `client.playlists` - Create, edit, delete playlists, manage tracks, and upload cover images.
|
|
20
24
|
- `client.playback` - Report playback start, progress, and stop to Jellyfin for scrobbling and playback sync.
|
|
21
25
|
- `client.lyrics` - Fetch synced or unsynced song lyrics.
|
|
22
26
|
- `client.favorites` - Toggle favorite items.
|
|
23
27
|
- `client.offline` - Browser IndexedDB media download manager for offline audio and video playback.
|
|
28
|
+
- `client.resume` - Continue watching / resume items querying (`/UserItems/Resume`).
|
|
29
|
+
- `client.nextUp` - Next unplayed TV show episodes (`/Shows/NextUp`).
|
|
30
|
+
- `client.latest` - Recently added media (movies, episodes, albums) (`/Users/{userId}/Items/Latest`).
|
|
31
|
+
- `client.transcode` - Teardown active transcoding sessions (`/Videos/ActiveEncodings`).
|
|
24
32
|
- **Quality Settings & Presets**: Standard video (4K, 1080p, 720p, 480p, 360p, direct) and audio (320k, 256k, 192k, 128k, direct) presets and resolution utilities.
|
|
25
33
|
- **Subtitles**: Direct VTT/SRT subtitle streaming URLs, raw text fetching, and automated track extraction from media metadata.
|
|
26
34
|
- **Direct Downloads**: Server file download URLs, progress-tracked file downloads, and offline caching.
|
|
@@ -280,6 +288,67 @@ client.on('unauthorized', () => {
|
|
|
280
288
|
|
|
281
289
|
---
|
|
282
290
|
|
|
291
|
+
### 11. Dashboard, Continue Watching & Next Up
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Fetch in-progress items for Continue Watching carousels
|
|
295
|
+
const resume = await client.resume.getResumeItems({
|
|
296
|
+
limit: 10,
|
|
297
|
+
mediaTypes: ['Video']
|
|
298
|
+
});
|
|
299
|
+
console.log('Continue watching:', resume.Items);
|
|
300
|
+
|
|
301
|
+
// Fetch the next unplayed episodes for followed series
|
|
302
|
+
const nextUp = await client.nextUp.getNextUp({
|
|
303
|
+
limit: 12,
|
|
304
|
+
enableTotalRecordCount: true
|
|
305
|
+
});
|
|
306
|
+
console.log('Next up episodes:', nextUp.Items);
|
|
307
|
+
|
|
308
|
+
// Query recently added media across libraries or specific types
|
|
309
|
+
const latestMovies = await client.latest.getLatestMovies({ limit: 10 });
|
|
310
|
+
const latestEpisodes = await client.latest.getLatestEpisodes({ limit: 10 });
|
|
311
|
+
const latestAlbums = await client.latest.getLatestAlbums({ limit: 10 });
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### 12. Active Transcode Session Teardown
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
const playSessionId = 'playback-session-id';
|
|
320
|
+
|
|
321
|
+
// Teardown active server transcoding to terminate lingering FFmpeg processes
|
|
322
|
+
await client.transcode.stopActiveEncoding(playSessionId);
|
|
323
|
+
|
|
324
|
+
// Or automatically teardown during playback stop reporting:
|
|
325
|
+
await client.playback.reportStopped(movieId, currentTicks, { playSessionId });
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
### 13. Universal & Scoped Search
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// Universal search across all items
|
|
334
|
+
const results = await client.search.search('Inception', {
|
|
335
|
+
includeItemTypes: ['Movie', 'Series'],
|
|
336
|
+
genres: ['Sci-Fi'],
|
|
337
|
+
limit: 20
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Search within a specific library folder
|
|
341
|
+
const musicResults = await client.search.searchInLibrary('music-library-id', 'Queen');
|
|
342
|
+
|
|
343
|
+
// Media-specific search shortcuts
|
|
344
|
+
const songs = await client.search.searchSongs('Yellow');
|
|
345
|
+
const albums = await client.search.searchAlbums('Parachutes');
|
|
346
|
+
const movies = await client.search.searchMovies('Interstellar');
|
|
347
|
+
const series = await client.search.searchSeries('Breaking Bad');
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
283
352
|
## License
|
|
284
353
|
|
|
285
354
|
MIT © [Franco Fantomius](https://github.com/francofantomius)
|
package/dist/index.cjs
CHANGED
|
@@ -702,22 +702,41 @@ var LibraryModule = class {
|
|
|
702
702
|
if (!query || !query.trim()) {
|
|
703
703
|
return { Items: [], TotalRecordCount: 0 };
|
|
704
704
|
}
|
|
705
|
-
const userId = this.requireUserId();
|
|
705
|
+
const userId = options.userId || this.requireUserId();
|
|
706
706
|
const {
|
|
707
707
|
limit = 30,
|
|
708
708
|
startIndex = 0,
|
|
709
|
-
|
|
709
|
+
parentId,
|
|
710
|
+
includeItemTypes = "Audio,MusicAlbum,MusicArtist,Playlist,Movie,Series,Episode",
|
|
711
|
+
mediaTypes,
|
|
712
|
+
genres,
|
|
713
|
+
years,
|
|
714
|
+
isFavorite,
|
|
715
|
+
sortBy,
|
|
716
|
+
sortOrder,
|
|
717
|
+
fields = COMMON_ITEM_FIELDS,
|
|
718
|
+
recursive = true
|
|
710
719
|
} = options;
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
720
|
+
const params = {
|
|
721
|
+
SearchTerm: query.trim(),
|
|
722
|
+
Limit: limit,
|
|
723
|
+
StartIndex: startIndex,
|
|
724
|
+
Recursive: recursive,
|
|
725
|
+
Fields: fields
|
|
726
|
+
};
|
|
727
|
+
if (parentId) params.ParentId = parentId;
|
|
728
|
+
if (includeItemTypes) {
|
|
729
|
+
params.IncludeItemTypes = Array.isArray(includeItemTypes) ? includeItemTypes.join(",") : includeItemTypes;
|
|
730
|
+
}
|
|
731
|
+
if (mediaTypes) {
|
|
732
|
+
params.MediaTypes = Array.isArray(mediaTypes) ? mediaTypes.join(",") : mediaTypes;
|
|
733
|
+
}
|
|
734
|
+
if (genres && genres.length > 0) params.Genres = genres.join("|");
|
|
735
|
+
if (years && years.length > 0) params.Years = years.join(",");
|
|
736
|
+
if (isFavorite) params.Filters = "IsFavorite";
|
|
737
|
+
if (sortBy) params.SortBy = sortBy;
|
|
738
|
+
if (sortOrder) params.SortOrder = sortOrder;
|
|
739
|
+
return await this.http.request(`/Users/${userId}/Items`, { params });
|
|
721
740
|
}
|
|
722
741
|
};
|
|
723
742
|
|
|
@@ -960,8 +979,9 @@ var PlaybackModule = class {
|
|
|
960
979
|
}
|
|
961
980
|
/**
|
|
962
981
|
* Report that playback has stopped for an item.
|
|
982
|
+
* Optionally teardown active transcoding session if playSessionId is provided.
|
|
963
983
|
*/
|
|
964
|
-
async reportStopped(itemId, positionTicks = 0) {
|
|
984
|
+
async reportStopped(itemId, positionTicks = 0, options) {
|
|
965
985
|
if (!itemId || !this.http.getToken()) return;
|
|
966
986
|
try {
|
|
967
987
|
await this.http.request("/Sessions/Playing/Stopped", {
|
|
@@ -971,6 +991,20 @@ var PlaybackModule = class {
|
|
|
971
991
|
PositionTicks: positionTicks
|
|
972
992
|
}
|
|
973
993
|
});
|
|
994
|
+
if (options?.playSessionId) {
|
|
995
|
+
const deviceId = this.http.getClientInfo()?.deviceId;
|
|
996
|
+
if (deviceId) {
|
|
997
|
+
await this.http.request("/Videos/ActiveEncodings", {
|
|
998
|
+
method: "DELETE",
|
|
999
|
+
params: {
|
|
1000
|
+
DeviceId: deviceId,
|
|
1001
|
+
PlaySessionId: options.playSessionId
|
|
1002
|
+
}
|
|
1003
|
+
}).catch((err) => {
|
|
1004
|
+
console.warn("[Jellyfin SDK] Teardown active encoding error:", err);
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
974
1008
|
} catch (e) {
|
|
975
1009
|
console.warn("[Jellyfin SDK] Stop playback report error:", e);
|
|
976
1010
|
}
|
|
@@ -1858,6 +1892,333 @@ var OfflineStorageManager = class {
|
|
|
1858
1892
|
}
|
|
1859
1893
|
};
|
|
1860
1894
|
|
|
1895
|
+
// src/resume.ts
|
|
1896
|
+
var RESUME_ITEM_FIELDS = "PrimaryImageAspectRatio,PrimaryImageTag,ImageTags,BackdropImageTags,AlbumPrimaryImageTag,AlbumId,AudioInfo,MediaSources,Chapters,HasLyrics,Overview,Genres,Studios,ProductionYear,OfficialRating,CommunityRating,CriticRating,PremiereDate,RunTimeTicks,IndexNumber,ParentIndexNumber,SeriesName,SeriesId,SeasonId,SeasonName,UserData";
|
|
1897
|
+
var ResumeModule = class {
|
|
1898
|
+
http;
|
|
1899
|
+
getUserId;
|
|
1900
|
+
constructor(http, getUserId) {
|
|
1901
|
+
this.http = http;
|
|
1902
|
+
this.getUserId = getUserId;
|
|
1903
|
+
}
|
|
1904
|
+
resolveUserId(explicitUserId) {
|
|
1905
|
+
const userId = explicitUserId || this.getUserId();
|
|
1906
|
+
if (!userId) {
|
|
1907
|
+
throw new JellyfinError("User ID is required for resume requests. Please authenticate or provide a userId.");
|
|
1908
|
+
}
|
|
1909
|
+
return userId;
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Fetch items currently in-progress for the user via /UserItems/Resume.
|
|
1913
|
+
*/
|
|
1914
|
+
async getResumeItems(options = {}) {
|
|
1915
|
+
const userId = this.resolveUserId(options.userId);
|
|
1916
|
+
const {
|
|
1917
|
+
limit = 20,
|
|
1918
|
+
startIndex = 0,
|
|
1919
|
+
parentId,
|
|
1920
|
+
mediaTypes,
|
|
1921
|
+
enableImages,
|
|
1922
|
+
enableUserData,
|
|
1923
|
+
imageTypeLimit,
|
|
1924
|
+
enableImageTypes,
|
|
1925
|
+
fields = RESUME_ITEM_FIELDS
|
|
1926
|
+
} = options;
|
|
1927
|
+
const params = {
|
|
1928
|
+
UserId: userId,
|
|
1929
|
+
Limit: limit,
|
|
1930
|
+
StartIndex: startIndex,
|
|
1931
|
+
Recursive: true,
|
|
1932
|
+
Fields: fields
|
|
1933
|
+
};
|
|
1934
|
+
if (parentId) params.ParentId = parentId;
|
|
1935
|
+
if (mediaTypes && mediaTypes.length > 0) params.MediaTypes = mediaTypes.join(",");
|
|
1936
|
+
if (enableImages !== void 0) params.EnableImages = enableImages;
|
|
1937
|
+
if (enableUserData !== void 0) params.EnableUserData = enableUserData;
|
|
1938
|
+
if (imageTypeLimit !== void 0) params.ImageTypeLimit = imageTypeLimit;
|
|
1939
|
+
if (enableImageTypes && enableImageTypes.length > 0) params.EnableImageTypes = enableImageTypes.join(",");
|
|
1940
|
+
return await this.http.request("/UserItems/Resume", { params });
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Convenience alias for getResumeItems.
|
|
1944
|
+
*/
|
|
1945
|
+
async getItems(options = {}) {
|
|
1946
|
+
return await this.getResumeItems(options);
|
|
1947
|
+
}
|
|
1948
|
+
};
|
|
1949
|
+
|
|
1950
|
+
// src/next-up.ts
|
|
1951
|
+
var NEXT_UP_FIELDS = "PrimaryImageAspectRatio,PrimaryImageTag,ImageTags,BackdropImageTags,AlbumPrimaryImageTag,AudioInfo,MediaSources,Chapters,Overview,Genres,ProductionYear,PremiereDate,RunTimeTicks,IndexNumber,ParentIndexNumber,SeriesName,SeriesId,SeasonId,SeasonName,UserData";
|
|
1952
|
+
var NextUpModule = class {
|
|
1953
|
+
http;
|
|
1954
|
+
getUserId;
|
|
1955
|
+
constructor(http, getUserId) {
|
|
1956
|
+
this.http = http;
|
|
1957
|
+
this.getUserId = getUserId;
|
|
1958
|
+
}
|
|
1959
|
+
resolveUserId(explicitUserId) {
|
|
1960
|
+
const userId = explicitUserId || this.getUserId();
|
|
1961
|
+
if (!userId) {
|
|
1962
|
+
throw new JellyfinError("User ID is required for next up requests. Please authenticate or provide a userId.");
|
|
1963
|
+
}
|
|
1964
|
+
return userId;
|
|
1965
|
+
}
|
|
1966
|
+
/**
|
|
1967
|
+
* Fetch the next unplayed episodes for followed series via /Shows/NextUp.
|
|
1968
|
+
*/
|
|
1969
|
+
async getNextUp(options = {}) {
|
|
1970
|
+
const userId = this.resolveUserId(options.userId);
|
|
1971
|
+
const {
|
|
1972
|
+
limit = 24,
|
|
1973
|
+
startIndex = 0,
|
|
1974
|
+
parentId,
|
|
1975
|
+
seriesId,
|
|
1976
|
+
fields = NEXT_UP_FIELDS,
|
|
1977
|
+
enableImages,
|
|
1978
|
+
enableUserData,
|
|
1979
|
+
enableTotalRecordCount,
|
|
1980
|
+
imageTypeLimit,
|
|
1981
|
+
enableImageTypes,
|
|
1982
|
+
disableFirstEpisode,
|
|
1983
|
+
nextUpDateCutoff
|
|
1984
|
+
} = options;
|
|
1985
|
+
const params = {
|
|
1986
|
+
UserId: userId,
|
|
1987
|
+
Limit: limit,
|
|
1988
|
+
StartIndex: startIndex,
|
|
1989
|
+
Fields: fields
|
|
1990
|
+
};
|
|
1991
|
+
if (parentId) params.ParentId = parentId;
|
|
1992
|
+
if (seriesId) params.SeriesId = seriesId;
|
|
1993
|
+
if (enableImages !== void 0) params.EnableImages = enableImages;
|
|
1994
|
+
if (enableUserData !== void 0) params.EnableUserData = enableUserData;
|
|
1995
|
+
if (enableTotalRecordCount !== void 0) params.EnableTotalRecordCount = enableTotalRecordCount;
|
|
1996
|
+
if (imageTypeLimit !== void 0) params.ImageTypeLimit = imageTypeLimit;
|
|
1997
|
+
if (enableImageTypes && enableImageTypes.length > 0) params.EnableImageTypes = enableImageTypes.join(",");
|
|
1998
|
+
if (disableFirstEpisode !== void 0) params.DisableFirstEpisode = disableFirstEpisode;
|
|
1999
|
+
if (nextUpDateCutoff) params.NextUpDateCutoff = nextUpDateCutoff;
|
|
2000
|
+
return await this.http.request("/Shows/NextUp", { params });
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Convenience alias for getNextUp.
|
|
2004
|
+
*/
|
|
2005
|
+
async getEpisodes(options = {}) {
|
|
2006
|
+
return await this.getNextUp(options);
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
|
|
2010
|
+
// src/latest.ts
|
|
2011
|
+
var LATEST_ITEM_FIELDS = "PrimaryImageAspectRatio,PrimaryImageTag,ImageTags,BackdropImageTags,AlbumPrimaryImageTag,AlbumId,AudioInfo,MediaSources,Chapters,Overview,Genres,Studios,ProductionYear,PremiereDate,RunTimeTicks,IndexNumber,ParentIndexNumber,SeriesName,SeriesId,SeasonId,SeasonName,UserData";
|
|
2012
|
+
var LatestModule = class {
|
|
2013
|
+
http;
|
|
2014
|
+
getUserId;
|
|
2015
|
+
constructor(http, getUserId) {
|
|
2016
|
+
this.http = http;
|
|
2017
|
+
this.getUserId = getUserId;
|
|
2018
|
+
}
|
|
2019
|
+
resolveUserId(explicitUserId) {
|
|
2020
|
+
const userId = explicitUserId || this.getUserId();
|
|
2021
|
+
if (!userId) {
|
|
2022
|
+
throw new JellyfinError("User ID is required for latest media requests. Please authenticate or provide a userId.");
|
|
2023
|
+
}
|
|
2024
|
+
return userId;
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* Fetch recently added media items for the user via /Users/{userId}/Items/Latest.
|
|
2028
|
+
*/
|
|
2029
|
+
async getLatest(options = {}) {
|
|
2030
|
+
const userId = this.resolveUserId(options.userId);
|
|
2031
|
+
const {
|
|
2032
|
+
parentId,
|
|
2033
|
+
limit = 20,
|
|
2034
|
+
fields = LATEST_ITEM_FIELDS,
|
|
2035
|
+
includeItemTypes,
|
|
2036
|
+
isPlayed,
|
|
2037
|
+
enableImages,
|
|
2038
|
+
imageTypeLimit,
|
|
2039
|
+
enableImageTypes,
|
|
2040
|
+
groupItems
|
|
2041
|
+
} = options;
|
|
2042
|
+
const params = {
|
|
2043
|
+
Limit: limit,
|
|
2044
|
+
Fields: fields
|
|
2045
|
+
};
|
|
2046
|
+
if (parentId) params.ParentId = parentId;
|
|
2047
|
+
if (includeItemTypes && includeItemTypes.length > 0) {
|
|
2048
|
+
params.IncludeItemTypes = includeItemTypes.join(",");
|
|
2049
|
+
}
|
|
2050
|
+
if (isPlayed !== void 0) params.IsPlayed = isPlayed;
|
|
2051
|
+
if (enableImages !== void 0) params.EnableImages = enableImages;
|
|
2052
|
+
if (imageTypeLimit !== void 0) params.ImageTypeLimit = imageTypeLimit;
|
|
2053
|
+
if (enableImageTypes && enableImageTypes.length > 0) {
|
|
2054
|
+
params.EnableImageTypes = enableImageTypes.join(",");
|
|
2055
|
+
}
|
|
2056
|
+
if (groupItems !== void 0) params.GroupItems = groupItems;
|
|
2057
|
+
const result = await this.http.request(`/Users/${userId}/Items/Latest`, { params });
|
|
2058
|
+
return Array.isArray(result) ? result : [];
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Convenience helper to fetch recently added movies.
|
|
2062
|
+
*/
|
|
2063
|
+
async getLatestMovies(options = {}) {
|
|
2064
|
+
return await this.getLatest({ ...options, includeItemTypes: ["Movie"] });
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Convenience helper to fetch recently added TV episodes.
|
|
2068
|
+
*/
|
|
2069
|
+
async getLatestEpisodes(options = {}) {
|
|
2070
|
+
return await this.getLatest({ ...options, includeItemTypes: ["Episode"] });
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Convenience helper to fetch recently added music albums.
|
|
2074
|
+
*/
|
|
2075
|
+
async getLatestAlbums(options = {}) {
|
|
2076
|
+
return await this.getLatest({ ...options, includeItemTypes: ["MusicAlbum"] });
|
|
2077
|
+
}
|
|
2078
|
+
};
|
|
2079
|
+
|
|
2080
|
+
// src/transcode.ts
|
|
2081
|
+
var TranscodeModule = class {
|
|
2082
|
+
http;
|
|
2083
|
+
getDeviceId;
|
|
2084
|
+
constructor(http, getDeviceId) {
|
|
2085
|
+
this.http = http;
|
|
2086
|
+
this.getDeviceId = getDeviceId;
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Explicitly stop an active server transcoding session via DELETE /Videos/ActiveEncodings.
|
|
2090
|
+
* This terminates lingering FFmpeg processes on the Jellyfin server.
|
|
2091
|
+
*
|
|
2092
|
+
* @param playSessionId The unique playback session ID.
|
|
2093
|
+
* @param deviceId The client device ID (defaults to configured client device ID).
|
|
2094
|
+
*/
|
|
2095
|
+
async stopActiveEncoding(playSessionId, deviceId) {
|
|
2096
|
+
if (!playSessionId) {
|
|
2097
|
+
throw new JellyfinError("playSessionId is required to stop an active encoding session.");
|
|
2098
|
+
}
|
|
2099
|
+
const resolvedDeviceId = deviceId || this.getDeviceId();
|
|
2100
|
+
if (!resolvedDeviceId) {
|
|
2101
|
+
throw new JellyfinError("deviceId is required to stop an active encoding session.");
|
|
2102
|
+
}
|
|
2103
|
+
await this.http.request("/Videos/ActiveEncodings", {
|
|
2104
|
+
method: "DELETE",
|
|
2105
|
+
params: {
|
|
2106
|
+
DeviceId: resolvedDeviceId,
|
|
2107
|
+
PlaySessionId: playSessionId
|
|
2108
|
+
}
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
/**
|
|
2112
|
+
* Convenience alias for stopActiveEncoding.
|
|
2113
|
+
*/
|
|
2114
|
+
async stop(playSessionId, deviceId) {
|
|
2115
|
+
return await this.stopActiveEncoding(playSessionId, deviceId);
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
|
|
2119
|
+
// src/search.ts
|
|
2120
|
+
var COMMON_SEARCH_FIELDS = "PrimaryImageAspectRatio,PrimaryImageTag,ImageTags,BackdropImageTags,AlbumPrimaryImageTag,AlbumId,AudioInfo,MediaSources,Chapters,HasLyrics,Overview,Genres,Studios,ProductionYear,OfficialRating,CommunityRating,CriticRating,PremiereDate,RunTimeTicks,IndexNumber,ParentIndexNumber,SeriesName,SeriesId,SeasonId,SeasonName,UserData";
|
|
2121
|
+
var SearchModule = class {
|
|
2122
|
+
http;
|
|
2123
|
+
getUserId;
|
|
2124
|
+
constructor(http, getUserId) {
|
|
2125
|
+
this.http = http;
|
|
2126
|
+
this.getUserId = getUserId;
|
|
2127
|
+
}
|
|
2128
|
+
resolveUserId(explicitUserId) {
|
|
2129
|
+
const userId = explicitUserId || this.getUserId();
|
|
2130
|
+
if (!userId) {
|
|
2131
|
+
throw new JellyfinError("User ID is required for search requests. Please authenticate or provide a userId.");
|
|
2132
|
+
}
|
|
2133
|
+
return userId;
|
|
2134
|
+
}
|
|
2135
|
+
/**
|
|
2136
|
+
* Universal search across Jellyfin items with comprehensive filters.
|
|
2137
|
+
*/
|
|
2138
|
+
async search(query, options = {}) {
|
|
2139
|
+
if (!query || !query.trim()) {
|
|
2140
|
+
return { Items: [], TotalRecordCount: 0 };
|
|
2141
|
+
}
|
|
2142
|
+
const userId = this.resolveUserId(options.userId);
|
|
2143
|
+
const {
|
|
2144
|
+
limit = 30,
|
|
2145
|
+
startIndex = 0,
|
|
2146
|
+
parentId,
|
|
2147
|
+
includeItemTypes = "Audio,MusicAlbum,MusicArtist,Playlist,Movie,Series,Episode",
|
|
2148
|
+
mediaTypes,
|
|
2149
|
+
genres,
|
|
2150
|
+
years,
|
|
2151
|
+
isFavorite,
|
|
2152
|
+
sortBy,
|
|
2153
|
+
sortOrder,
|
|
2154
|
+
fields = COMMON_SEARCH_FIELDS,
|
|
2155
|
+
recursive = true
|
|
2156
|
+
} = options;
|
|
2157
|
+
const params = {
|
|
2158
|
+
SearchTerm: query.trim(),
|
|
2159
|
+
Limit: limit,
|
|
2160
|
+
StartIndex: startIndex,
|
|
2161
|
+
Recursive: recursive,
|
|
2162
|
+
Fields: fields
|
|
2163
|
+
};
|
|
2164
|
+
if (parentId) params.ParentId = parentId;
|
|
2165
|
+
if (includeItemTypes) {
|
|
2166
|
+
params.IncludeItemTypes = Array.isArray(includeItemTypes) ? includeItemTypes.join(",") : includeItemTypes;
|
|
2167
|
+
}
|
|
2168
|
+
if (mediaTypes) {
|
|
2169
|
+
params.MediaTypes = Array.isArray(mediaTypes) ? mediaTypes.join(",") : mediaTypes;
|
|
2170
|
+
}
|
|
2171
|
+
if (genres && genres.length > 0) params.Genres = genres.join("|");
|
|
2172
|
+
if (years && years.length > 0) params.Years = years.join(",");
|
|
2173
|
+
if (isFavorite) params.Filters = "IsFavorite";
|
|
2174
|
+
if (sortBy) params.SortBy = sortBy;
|
|
2175
|
+
if (sortOrder) params.SortOrder = sortOrder;
|
|
2176
|
+
return await this.http.request(`/Users/${userId}/Items`, { params });
|
|
2177
|
+
}
|
|
2178
|
+
/**
|
|
2179
|
+
* Search specifically within a specific library or parent folder.
|
|
2180
|
+
*/
|
|
2181
|
+
async searchInLibrary(parentId, query, options = {}) {
|
|
2182
|
+
return await this.search(query, { ...options, parentId });
|
|
2183
|
+
}
|
|
2184
|
+
/**
|
|
2185
|
+
* Convenience helper to search audio tracks / songs.
|
|
2186
|
+
*/
|
|
2187
|
+
async searchSongs(query, options = {}) {
|
|
2188
|
+
return await this.search(query, { ...options, includeItemTypes: "Audio" });
|
|
2189
|
+
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Convenience helper to search music albums.
|
|
2192
|
+
*/
|
|
2193
|
+
async searchAlbums(query, options = {}) {
|
|
2194
|
+
return await this.search(query, { ...options, includeItemTypes: "MusicAlbum" });
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* Convenience helper to search music artists.
|
|
2198
|
+
*/
|
|
2199
|
+
async searchArtists(query, options = {}) {
|
|
2200
|
+
return await this.search(query, { ...options, includeItemTypes: "MusicArtist" });
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Convenience helper to search movies.
|
|
2204
|
+
*/
|
|
2205
|
+
async searchMovies(query, options = {}) {
|
|
2206
|
+
return await this.search(query, { ...options, includeItemTypes: "Movie" });
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
* Convenience helper to search TV series.
|
|
2210
|
+
*/
|
|
2211
|
+
async searchSeries(query, options = {}) {
|
|
2212
|
+
return await this.search(query, { ...options, includeItemTypes: "Series" });
|
|
2213
|
+
}
|
|
2214
|
+
/**
|
|
2215
|
+
* Convenience helper to search TV episodes.
|
|
2216
|
+
*/
|
|
2217
|
+
async searchEpisodes(query, options = {}) {
|
|
2218
|
+
return await this.search(query, { ...options, includeItemTypes: "Episode" });
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
|
|
1861
2222
|
// src/cache.ts
|
|
1862
2223
|
var MemoryCacheAdapter = class {
|
|
1863
2224
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -2049,12 +2410,17 @@ var JellyfinClient = class {
|
|
|
2049
2410
|
auth;
|
|
2050
2411
|
system;
|
|
2051
2412
|
library;
|
|
2413
|
+
search;
|
|
2052
2414
|
playlists;
|
|
2053
2415
|
playback;
|
|
2054
2416
|
media;
|
|
2055
2417
|
lyrics;
|
|
2056
2418
|
favorites;
|
|
2057
2419
|
offline;
|
|
2420
|
+
resume;
|
|
2421
|
+
nextUp;
|
|
2422
|
+
latest;
|
|
2423
|
+
transcode;
|
|
2058
2424
|
http;
|
|
2059
2425
|
storage;
|
|
2060
2426
|
cache;
|
|
@@ -2065,7 +2431,7 @@ var JellyfinClient = class {
|
|
|
2065
2431
|
this.cache = new MemoryCacheAdapter();
|
|
2066
2432
|
this.clientInfo = {
|
|
2067
2433
|
name: options.clientInfo?.name || "@francofantomius/jellyfin",
|
|
2068
|
-
version: options.clientInfo?.version || "0.
|
|
2434
|
+
version: options.clientInfo?.version || "0.2.0",
|
|
2069
2435
|
device: options.clientInfo?.device || (typeof window !== "undefined" ? "Web Browser" : "Node.js"),
|
|
2070
2436
|
deviceId: options.clientInfo?.deviceId || generateDeviceId()
|
|
2071
2437
|
};
|
|
@@ -2082,18 +2448,24 @@ var JellyfinClient = class {
|
|
|
2082
2448
|
}
|
|
2083
2449
|
});
|
|
2084
2450
|
const getUserId = () => this.auth.getUserId();
|
|
2451
|
+
const getDeviceId = () => this.clientInfo.deviceId;
|
|
2085
2452
|
this.system = new SystemModule(this.http, {
|
|
2086
2453
|
serverVersion: options.serverVersion,
|
|
2087
2454
|
targetVersion: options.targetVersion
|
|
2088
2455
|
});
|
|
2089
2456
|
this.auth = new AuthModule(this.http, options.userId || "");
|
|
2090
2457
|
this.library = new LibraryModule(this.http, getUserId);
|
|
2458
|
+
this.search = new SearchModule(this.http, getUserId);
|
|
2091
2459
|
this.playlists = new PlaylistsModule(this.http, getUserId);
|
|
2092
2460
|
this.playback = new PlaybackModule(this.http);
|
|
2093
2461
|
this.media = new MediaModule(this.http, getUserId);
|
|
2094
2462
|
this.lyrics = new LyricsModule(this.http);
|
|
2095
2463
|
this.favorites = new FavoritesModule(this.http, getUserId);
|
|
2096
2464
|
this.offline = new OfflineStorageManager(this.media);
|
|
2465
|
+
this.resume = new ResumeModule(this.http, getUserId);
|
|
2466
|
+
this.nextUp = new NextUpModule(this.http, getUserId);
|
|
2467
|
+
this.latest = new LatestModule(this.http, getUserId);
|
|
2468
|
+
this.transcode = new TranscodeModule(this.http, getDeviceId);
|
|
2097
2469
|
}
|
|
2098
2470
|
// --- Configuration Getters & Setters ---
|
|
2099
2471
|
get serverUrl() {
|
|
@@ -2212,6 +2584,7 @@ var JellyfinClient = class {
|
|
|
2212
2584
|
|
|
2213
2585
|
exports.AUDIO_QUALITY_PRESETS = AUDIO_QUALITY_PRESETS;
|
|
2214
2586
|
exports.AuthModule = AuthModule;
|
|
2587
|
+
exports.COMMON_SEARCH_FIELDS = COMMON_SEARCH_FIELDS;
|
|
2215
2588
|
exports.FavoritesModule = FavoritesModule;
|
|
2216
2589
|
exports.HttpTransport = HttpTransport;
|
|
2217
2590
|
exports.IndexedDBCacheAdapter = IndexedDBCacheAdapter;
|
|
@@ -2219,16 +2592,21 @@ exports.JellyfinApiError = JellyfinApiError;
|
|
|
2219
2592
|
exports.JellyfinAuthError = JellyfinAuthError;
|
|
2220
2593
|
exports.JellyfinClient = JellyfinClient;
|
|
2221
2594
|
exports.JellyfinError = JellyfinError;
|
|
2595
|
+
exports.LatestModule = LatestModule;
|
|
2222
2596
|
exports.LibraryModule = LibraryModule;
|
|
2223
2597
|
exports.LocalStorageAdapter = LocalStorageAdapter;
|
|
2224
2598
|
exports.LyricsModule = LyricsModule;
|
|
2225
2599
|
exports.MediaModule = MediaModule;
|
|
2226
2600
|
exports.MemoryCacheAdapter = MemoryCacheAdapter;
|
|
2227
2601
|
exports.MemoryStorage = MemoryStorage;
|
|
2602
|
+
exports.NextUpModule = NextUpModule;
|
|
2228
2603
|
exports.OfflineStorageManager = OfflineStorageManager;
|
|
2229
2604
|
exports.PlaybackModule = PlaybackModule;
|
|
2230
2605
|
exports.PlaylistsModule = PlaylistsModule;
|
|
2606
|
+
exports.ResumeModule = ResumeModule;
|
|
2607
|
+
exports.SearchModule = SearchModule;
|
|
2231
2608
|
exports.SystemModule = SystemModule;
|
|
2609
|
+
exports.TranscodeModule = TranscodeModule;
|
|
2232
2610
|
exports.VIDEO_QUALITY_PRESETS = VIDEO_QUALITY_PRESETS;
|
|
2233
2611
|
exports.buildAuthHeader = buildAuthHeader;
|
|
2234
2612
|
exports.cleanUrl = cleanUrl;
|