@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 CHANGED
@@ -5,6 +5,9 @@ A modern, lightweight, modular, and type-safe Jellyfin SDK for Node.js and the B
5
5
  [![npm version](https://img.shields.io/npm/v/@francofantomius/jellyfin.svg)](https://www.npmjs.com/package/@francofantomius/jellyfin)
6
6
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](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
- includeItemTypes = "Audio,MusicAlbum,MusicArtist,Playlist,Movie,Series,Episode"
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
- return await this.http.request(`/Users/${userId}/Items`, {
712
- params: {
713
- SearchTerm: query.trim(),
714
- IncludeItemTypes: includeItemTypes,
715
- Limit: limit,
716
- StartIndex: startIndex,
717
- Recursive: true,
718
- Fields: COMMON_ITEM_FIELDS
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.1.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;