@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/dist/index.js CHANGED
@@ -700,22 +700,41 @@ var LibraryModule = class {
700
700
  if (!query || !query.trim()) {
701
701
  return { Items: [], TotalRecordCount: 0 };
702
702
  }
703
- const userId = this.requireUserId();
703
+ const userId = options.userId || this.requireUserId();
704
704
  const {
705
705
  limit = 30,
706
706
  startIndex = 0,
707
- includeItemTypes = "Audio,MusicAlbum,MusicArtist,Playlist,Movie,Series,Episode"
707
+ parentId,
708
+ includeItemTypes = "Audio,MusicAlbum,MusicArtist,Playlist,Movie,Series,Episode",
709
+ mediaTypes,
710
+ genres,
711
+ years,
712
+ isFavorite,
713
+ sortBy,
714
+ sortOrder,
715
+ fields = COMMON_ITEM_FIELDS,
716
+ recursive = true
708
717
  } = options;
709
- return await this.http.request(`/Users/${userId}/Items`, {
710
- params: {
711
- SearchTerm: query.trim(),
712
- IncludeItemTypes: includeItemTypes,
713
- Limit: limit,
714
- StartIndex: startIndex,
715
- Recursive: true,
716
- Fields: COMMON_ITEM_FIELDS
717
- }
718
- });
718
+ const params = {
719
+ SearchTerm: query.trim(),
720
+ Limit: limit,
721
+ StartIndex: startIndex,
722
+ Recursive: recursive,
723
+ Fields: fields
724
+ };
725
+ if (parentId) params.ParentId = parentId;
726
+ if (includeItemTypes) {
727
+ params.IncludeItemTypes = Array.isArray(includeItemTypes) ? includeItemTypes.join(",") : includeItemTypes;
728
+ }
729
+ if (mediaTypes) {
730
+ params.MediaTypes = Array.isArray(mediaTypes) ? mediaTypes.join(",") : mediaTypes;
731
+ }
732
+ if (genres && genres.length > 0) params.Genres = genres.join("|");
733
+ if (years && years.length > 0) params.Years = years.join(",");
734
+ if (isFavorite) params.Filters = "IsFavorite";
735
+ if (sortBy) params.SortBy = sortBy;
736
+ if (sortOrder) params.SortOrder = sortOrder;
737
+ return await this.http.request(`/Users/${userId}/Items`, { params });
719
738
  }
720
739
  };
721
740
 
@@ -958,8 +977,9 @@ var PlaybackModule = class {
958
977
  }
959
978
  /**
960
979
  * Report that playback has stopped for an item.
980
+ * Optionally teardown active transcoding session if playSessionId is provided.
961
981
  */
962
- async reportStopped(itemId, positionTicks = 0) {
982
+ async reportStopped(itemId, positionTicks = 0, options) {
963
983
  if (!itemId || !this.http.getToken()) return;
964
984
  try {
965
985
  await this.http.request("/Sessions/Playing/Stopped", {
@@ -969,6 +989,20 @@ var PlaybackModule = class {
969
989
  PositionTicks: positionTicks
970
990
  }
971
991
  });
992
+ if (options?.playSessionId) {
993
+ const deviceId = this.http.getClientInfo()?.deviceId;
994
+ if (deviceId) {
995
+ await this.http.request("/Videos/ActiveEncodings", {
996
+ method: "DELETE",
997
+ params: {
998
+ DeviceId: deviceId,
999
+ PlaySessionId: options.playSessionId
1000
+ }
1001
+ }).catch((err) => {
1002
+ console.warn("[Jellyfin SDK] Teardown active encoding error:", err);
1003
+ });
1004
+ }
1005
+ }
972
1006
  } catch (e) {
973
1007
  console.warn("[Jellyfin SDK] Stop playback report error:", e);
974
1008
  }
@@ -1856,6 +1890,333 @@ var OfflineStorageManager = class {
1856
1890
  }
1857
1891
  };
1858
1892
 
1893
+ // src/resume.ts
1894
+ 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";
1895
+ var ResumeModule = class {
1896
+ http;
1897
+ getUserId;
1898
+ constructor(http, getUserId) {
1899
+ this.http = http;
1900
+ this.getUserId = getUserId;
1901
+ }
1902
+ resolveUserId(explicitUserId) {
1903
+ const userId = explicitUserId || this.getUserId();
1904
+ if (!userId) {
1905
+ throw new JellyfinError("User ID is required for resume requests. Please authenticate or provide a userId.");
1906
+ }
1907
+ return userId;
1908
+ }
1909
+ /**
1910
+ * Fetch items currently in-progress for the user via /UserItems/Resume.
1911
+ */
1912
+ async getResumeItems(options = {}) {
1913
+ const userId = this.resolveUserId(options.userId);
1914
+ const {
1915
+ limit = 20,
1916
+ startIndex = 0,
1917
+ parentId,
1918
+ mediaTypes,
1919
+ enableImages,
1920
+ enableUserData,
1921
+ imageTypeLimit,
1922
+ enableImageTypes,
1923
+ fields = RESUME_ITEM_FIELDS
1924
+ } = options;
1925
+ const params = {
1926
+ UserId: userId,
1927
+ Limit: limit,
1928
+ StartIndex: startIndex,
1929
+ Recursive: true,
1930
+ Fields: fields
1931
+ };
1932
+ if (parentId) params.ParentId = parentId;
1933
+ if (mediaTypes && mediaTypes.length > 0) params.MediaTypes = mediaTypes.join(",");
1934
+ if (enableImages !== void 0) params.EnableImages = enableImages;
1935
+ if (enableUserData !== void 0) params.EnableUserData = enableUserData;
1936
+ if (imageTypeLimit !== void 0) params.ImageTypeLimit = imageTypeLimit;
1937
+ if (enableImageTypes && enableImageTypes.length > 0) params.EnableImageTypes = enableImageTypes.join(",");
1938
+ return await this.http.request("/UserItems/Resume", { params });
1939
+ }
1940
+ /**
1941
+ * Convenience alias for getResumeItems.
1942
+ */
1943
+ async getItems(options = {}) {
1944
+ return await this.getResumeItems(options);
1945
+ }
1946
+ };
1947
+
1948
+ // src/next-up.ts
1949
+ var NEXT_UP_FIELDS = "PrimaryImageAspectRatio,PrimaryImageTag,ImageTags,BackdropImageTags,AlbumPrimaryImageTag,AudioInfo,MediaSources,Chapters,Overview,Genres,ProductionYear,PremiereDate,RunTimeTicks,IndexNumber,ParentIndexNumber,SeriesName,SeriesId,SeasonId,SeasonName,UserData";
1950
+ var NextUpModule = class {
1951
+ http;
1952
+ getUserId;
1953
+ constructor(http, getUserId) {
1954
+ this.http = http;
1955
+ this.getUserId = getUserId;
1956
+ }
1957
+ resolveUserId(explicitUserId) {
1958
+ const userId = explicitUserId || this.getUserId();
1959
+ if (!userId) {
1960
+ throw new JellyfinError("User ID is required for next up requests. Please authenticate or provide a userId.");
1961
+ }
1962
+ return userId;
1963
+ }
1964
+ /**
1965
+ * Fetch the next unplayed episodes for followed series via /Shows/NextUp.
1966
+ */
1967
+ async getNextUp(options = {}) {
1968
+ const userId = this.resolveUserId(options.userId);
1969
+ const {
1970
+ limit = 24,
1971
+ startIndex = 0,
1972
+ parentId,
1973
+ seriesId,
1974
+ fields = NEXT_UP_FIELDS,
1975
+ enableImages,
1976
+ enableUserData,
1977
+ enableTotalRecordCount,
1978
+ imageTypeLimit,
1979
+ enableImageTypes,
1980
+ disableFirstEpisode,
1981
+ nextUpDateCutoff
1982
+ } = options;
1983
+ const params = {
1984
+ UserId: userId,
1985
+ Limit: limit,
1986
+ StartIndex: startIndex,
1987
+ Fields: fields
1988
+ };
1989
+ if (parentId) params.ParentId = parentId;
1990
+ if (seriesId) params.SeriesId = seriesId;
1991
+ if (enableImages !== void 0) params.EnableImages = enableImages;
1992
+ if (enableUserData !== void 0) params.EnableUserData = enableUserData;
1993
+ if (enableTotalRecordCount !== void 0) params.EnableTotalRecordCount = enableTotalRecordCount;
1994
+ if (imageTypeLimit !== void 0) params.ImageTypeLimit = imageTypeLimit;
1995
+ if (enableImageTypes && enableImageTypes.length > 0) params.EnableImageTypes = enableImageTypes.join(",");
1996
+ if (disableFirstEpisode !== void 0) params.DisableFirstEpisode = disableFirstEpisode;
1997
+ if (nextUpDateCutoff) params.NextUpDateCutoff = nextUpDateCutoff;
1998
+ return await this.http.request("/Shows/NextUp", { params });
1999
+ }
2000
+ /**
2001
+ * Convenience alias for getNextUp.
2002
+ */
2003
+ async getEpisodes(options = {}) {
2004
+ return await this.getNextUp(options);
2005
+ }
2006
+ };
2007
+
2008
+ // src/latest.ts
2009
+ 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";
2010
+ var LatestModule = class {
2011
+ http;
2012
+ getUserId;
2013
+ constructor(http, getUserId) {
2014
+ this.http = http;
2015
+ this.getUserId = getUserId;
2016
+ }
2017
+ resolveUserId(explicitUserId) {
2018
+ const userId = explicitUserId || this.getUserId();
2019
+ if (!userId) {
2020
+ throw new JellyfinError("User ID is required for latest media requests. Please authenticate or provide a userId.");
2021
+ }
2022
+ return userId;
2023
+ }
2024
+ /**
2025
+ * Fetch recently added media items for the user via /Users/{userId}/Items/Latest.
2026
+ */
2027
+ async getLatest(options = {}) {
2028
+ const userId = this.resolveUserId(options.userId);
2029
+ const {
2030
+ parentId,
2031
+ limit = 20,
2032
+ fields = LATEST_ITEM_FIELDS,
2033
+ includeItemTypes,
2034
+ isPlayed,
2035
+ enableImages,
2036
+ imageTypeLimit,
2037
+ enableImageTypes,
2038
+ groupItems
2039
+ } = options;
2040
+ const params = {
2041
+ Limit: limit,
2042
+ Fields: fields
2043
+ };
2044
+ if (parentId) params.ParentId = parentId;
2045
+ if (includeItemTypes && includeItemTypes.length > 0) {
2046
+ params.IncludeItemTypes = includeItemTypes.join(",");
2047
+ }
2048
+ if (isPlayed !== void 0) params.IsPlayed = isPlayed;
2049
+ if (enableImages !== void 0) params.EnableImages = enableImages;
2050
+ if (imageTypeLimit !== void 0) params.ImageTypeLimit = imageTypeLimit;
2051
+ if (enableImageTypes && enableImageTypes.length > 0) {
2052
+ params.EnableImageTypes = enableImageTypes.join(",");
2053
+ }
2054
+ if (groupItems !== void 0) params.GroupItems = groupItems;
2055
+ const result = await this.http.request(`/Users/${userId}/Items/Latest`, { params });
2056
+ return Array.isArray(result) ? result : [];
2057
+ }
2058
+ /**
2059
+ * Convenience helper to fetch recently added movies.
2060
+ */
2061
+ async getLatestMovies(options = {}) {
2062
+ return await this.getLatest({ ...options, includeItemTypes: ["Movie"] });
2063
+ }
2064
+ /**
2065
+ * Convenience helper to fetch recently added TV episodes.
2066
+ */
2067
+ async getLatestEpisodes(options = {}) {
2068
+ return await this.getLatest({ ...options, includeItemTypes: ["Episode"] });
2069
+ }
2070
+ /**
2071
+ * Convenience helper to fetch recently added music albums.
2072
+ */
2073
+ async getLatestAlbums(options = {}) {
2074
+ return await this.getLatest({ ...options, includeItemTypes: ["MusicAlbum"] });
2075
+ }
2076
+ };
2077
+
2078
+ // src/transcode.ts
2079
+ var TranscodeModule = class {
2080
+ http;
2081
+ getDeviceId;
2082
+ constructor(http, getDeviceId) {
2083
+ this.http = http;
2084
+ this.getDeviceId = getDeviceId;
2085
+ }
2086
+ /**
2087
+ * Explicitly stop an active server transcoding session via DELETE /Videos/ActiveEncodings.
2088
+ * This terminates lingering FFmpeg processes on the Jellyfin server.
2089
+ *
2090
+ * @param playSessionId The unique playback session ID.
2091
+ * @param deviceId The client device ID (defaults to configured client device ID).
2092
+ */
2093
+ async stopActiveEncoding(playSessionId, deviceId) {
2094
+ if (!playSessionId) {
2095
+ throw new JellyfinError("playSessionId is required to stop an active encoding session.");
2096
+ }
2097
+ const resolvedDeviceId = deviceId || this.getDeviceId();
2098
+ if (!resolvedDeviceId) {
2099
+ throw new JellyfinError("deviceId is required to stop an active encoding session.");
2100
+ }
2101
+ await this.http.request("/Videos/ActiveEncodings", {
2102
+ method: "DELETE",
2103
+ params: {
2104
+ DeviceId: resolvedDeviceId,
2105
+ PlaySessionId: playSessionId
2106
+ }
2107
+ });
2108
+ }
2109
+ /**
2110
+ * Convenience alias for stopActiveEncoding.
2111
+ */
2112
+ async stop(playSessionId, deviceId) {
2113
+ return await this.stopActiveEncoding(playSessionId, deviceId);
2114
+ }
2115
+ };
2116
+
2117
+ // src/search.ts
2118
+ 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";
2119
+ var SearchModule = class {
2120
+ http;
2121
+ getUserId;
2122
+ constructor(http, getUserId) {
2123
+ this.http = http;
2124
+ this.getUserId = getUserId;
2125
+ }
2126
+ resolveUserId(explicitUserId) {
2127
+ const userId = explicitUserId || this.getUserId();
2128
+ if (!userId) {
2129
+ throw new JellyfinError("User ID is required for search requests. Please authenticate or provide a userId.");
2130
+ }
2131
+ return userId;
2132
+ }
2133
+ /**
2134
+ * Universal search across Jellyfin items with comprehensive filters.
2135
+ */
2136
+ async search(query, options = {}) {
2137
+ if (!query || !query.trim()) {
2138
+ return { Items: [], TotalRecordCount: 0 };
2139
+ }
2140
+ const userId = this.resolveUserId(options.userId);
2141
+ const {
2142
+ limit = 30,
2143
+ startIndex = 0,
2144
+ parentId,
2145
+ includeItemTypes = "Audio,MusicAlbum,MusicArtist,Playlist,Movie,Series,Episode",
2146
+ mediaTypes,
2147
+ genres,
2148
+ years,
2149
+ isFavorite,
2150
+ sortBy,
2151
+ sortOrder,
2152
+ fields = COMMON_SEARCH_FIELDS,
2153
+ recursive = true
2154
+ } = options;
2155
+ const params = {
2156
+ SearchTerm: query.trim(),
2157
+ Limit: limit,
2158
+ StartIndex: startIndex,
2159
+ Recursive: recursive,
2160
+ Fields: fields
2161
+ };
2162
+ if (parentId) params.ParentId = parentId;
2163
+ if (includeItemTypes) {
2164
+ params.IncludeItemTypes = Array.isArray(includeItemTypes) ? includeItemTypes.join(",") : includeItemTypes;
2165
+ }
2166
+ if (mediaTypes) {
2167
+ params.MediaTypes = Array.isArray(mediaTypes) ? mediaTypes.join(",") : mediaTypes;
2168
+ }
2169
+ if (genres && genres.length > 0) params.Genres = genres.join("|");
2170
+ if (years && years.length > 0) params.Years = years.join(",");
2171
+ if (isFavorite) params.Filters = "IsFavorite";
2172
+ if (sortBy) params.SortBy = sortBy;
2173
+ if (sortOrder) params.SortOrder = sortOrder;
2174
+ return await this.http.request(`/Users/${userId}/Items`, { params });
2175
+ }
2176
+ /**
2177
+ * Search specifically within a specific library or parent folder.
2178
+ */
2179
+ async searchInLibrary(parentId, query, options = {}) {
2180
+ return await this.search(query, { ...options, parentId });
2181
+ }
2182
+ /**
2183
+ * Convenience helper to search audio tracks / songs.
2184
+ */
2185
+ async searchSongs(query, options = {}) {
2186
+ return await this.search(query, { ...options, includeItemTypes: "Audio" });
2187
+ }
2188
+ /**
2189
+ * Convenience helper to search music albums.
2190
+ */
2191
+ async searchAlbums(query, options = {}) {
2192
+ return await this.search(query, { ...options, includeItemTypes: "MusicAlbum" });
2193
+ }
2194
+ /**
2195
+ * Convenience helper to search music artists.
2196
+ */
2197
+ async searchArtists(query, options = {}) {
2198
+ return await this.search(query, { ...options, includeItemTypes: "MusicArtist" });
2199
+ }
2200
+ /**
2201
+ * Convenience helper to search movies.
2202
+ */
2203
+ async searchMovies(query, options = {}) {
2204
+ return await this.search(query, { ...options, includeItemTypes: "Movie" });
2205
+ }
2206
+ /**
2207
+ * Convenience helper to search TV series.
2208
+ */
2209
+ async searchSeries(query, options = {}) {
2210
+ return await this.search(query, { ...options, includeItemTypes: "Series" });
2211
+ }
2212
+ /**
2213
+ * Convenience helper to search TV episodes.
2214
+ */
2215
+ async searchEpisodes(query, options = {}) {
2216
+ return await this.search(query, { ...options, includeItemTypes: "Episode" });
2217
+ }
2218
+ };
2219
+
1859
2220
  // src/cache.ts
1860
2221
  var MemoryCacheAdapter = class {
1861
2222
  cache = /* @__PURE__ */ new Map();
@@ -2047,12 +2408,17 @@ var JellyfinClient = class {
2047
2408
  auth;
2048
2409
  system;
2049
2410
  library;
2411
+ search;
2050
2412
  playlists;
2051
2413
  playback;
2052
2414
  media;
2053
2415
  lyrics;
2054
2416
  favorites;
2055
2417
  offline;
2418
+ resume;
2419
+ nextUp;
2420
+ latest;
2421
+ transcode;
2056
2422
  http;
2057
2423
  storage;
2058
2424
  cache;
@@ -2063,7 +2429,7 @@ var JellyfinClient = class {
2063
2429
  this.cache = new MemoryCacheAdapter();
2064
2430
  this.clientInfo = {
2065
2431
  name: options.clientInfo?.name || "@francofantomius/jellyfin",
2066
- version: options.clientInfo?.version || "0.1.0",
2432
+ version: options.clientInfo?.version || "0.2.0",
2067
2433
  device: options.clientInfo?.device || (typeof window !== "undefined" ? "Web Browser" : "Node.js"),
2068
2434
  deviceId: options.clientInfo?.deviceId || generateDeviceId()
2069
2435
  };
@@ -2080,18 +2446,24 @@ var JellyfinClient = class {
2080
2446
  }
2081
2447
  });
2082
2448
  const getUserId = () => this.auth.getUserId();
2449
+ const getDeviceId = () => this.clientInfo.deviceId;
2083
2450
  this.system = new SystemModule(this.http, {
2084
2451
  serverVersion: options.serverVersion,
2085
2452
  targetVersion: options.targetVersion
2086
2453
  });
2087
2454
  this.auth = new AuthModule(this.http, options.userId || "");
2088
2455
  this.library = new LibraryModule(this.http, getUserId);
2456
+ this.search = new SearchModule(this.http, getUserId);
2089
2457
  this.playlists = new PlaylistsModule(this.http, getUserId);
2090
2458
  this.playback = new PlaybackModule(this.http);
2091
2459
  this.media = new MediaModule(this.http, getUserId);
2092
2460
  this.lyrics = new LyricsModule(this.http);
2093
2461
  this.favorites = new FavoritesModule(this.http, getUserId);
2094
2462
  this.offline = new OfflineStorageManager(this.media);
2463
+ this.resume = new ResumeModule(this.http, getUserId);
2464
+ this.nextUp = new NextUpModule(this.http, getUserId);
2465
+ this.latest = new LatestModule(this.http, getUserId);
2466
+ this.transcode = new TranscodeModule(this.http, getDeviceId);
2095
2467
  }
2096
2468
  // --- Configuration Getters & Setters ---
2097
2469
  get serverUrl() {
@@ -2208,6 +2580,6 @@ var JellyfinClient = class {
2208
2580
  }
2209
2581
  };
2210
2582
 
2211
- export { AUDIO_QUALITY_PRESETS, AuthModule, FavoritesModule, HttpTransport, IndexedDBCacheAdapter, JellyfinApiError, JellyfinAuthError, JellyfinClient, JellyfinError, LibraryModule, LocalStorageAdapter, LyricsModule, MediaModule, MemoryCacheAdapter, MemoryStorage, OfflineStorageManager, PlaybackModule, PlaylistsModule, SystemModule, VIDEO_QUALITY_PRESETS, buildAuthHeader, cleanUrl, fetchWithCache, formatBitrate, generateDeviceId, getAudioQualityList, getDefaultStorage, getVideoQualityList, isV12OrHigher, isVersionAtLeast, parseSemVer, resolveAudioQuality, resolveVideoQuality };
2583
+ export { AUDIO_QUALITY_PRESETS, AuthModule, COMMON_SEARCH_FIELDS, FavoritesModule, HttpTransport, IndexedDBCacheAdapter, JellyfinApiError, JellyfinAuthError, JellyfinClient, JellyfinError, LatestModule, LibraryModule, LocalStorageAdapter, LyricsModule, MediaModule, MemoryCacheAdapter, MemoryStorage, NextUpModule, OfflineStorageManager, PlaybackModule, PlaylistsModule, ResumeModule, SearchModule, SystemModule, TranscodeModule, VIDEO_QUALITY_PRESETS, buildAuthHeader, cleanUrl, fetchWithCache, formatBitrate, generateDeviceId, getAudioQualityList, getDefaultStorage, getVideoQualityList, isV12OrHigher, isVersionAtLeast, parseSemVer, resolveAudioQuality, resolveVideoQuality };
2212
2584
  //# sourceMappingURL=index.js.map
2213
2585
  //# sourceMappingURL=index.js.map