@francofantomius/jellyfin 0.4.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/dist/index.d.cts CHANGED
@@ -306,10 +306,11 @@ interface AudioStreamOptions {
306
306
  transcodingContainer?: string;
307
307
  audioCodec?: string;
308
308
  deviceId?: string;
309
+ mediaSourceId?: string;
309
310
  /**
310
- * Whether to include the api_key token in the URL query parameters.
311
+ * Whether to include the authentication token in the URL query parameters (ApiKey, api_key, X-Emby-Token).
311
312
  * Defaults to true for HTML5 <audio> tag compatibility.
312
- * If false, callers are expected to provide Authorization headers.
313
+ * Set to false when providing modern Authorization headers to avoid conflicting authentication credentials in Jellyfin 12+.
313
314
  */
314
315
  useQueryToken?: boolean;
315
316
  }
@@ -320,10 +321,11 @@ interface AudioHlsStreamOptions {
320
321
  audioCodec?: string;
321
322
  segmentLength?: number;
322
323
  deviceId?: string;
324
+ mediaSourceId?: string;
323
325
  /**
324
- * Whether to include the api_key token in the URL query parameters.
326
+ * Whether to include the authentication token in the URL query parameters (ApiKey, api_key, X-Emby-Token).
325
327
  * Defaults to true for legacy compatibility.
326
- * If false, players (e.g. hls.js) should be configured with Authorization headers via xhrSetup.
328
+ * Set to false when players (e.g. hls.js) are configured with Authorization headers via xhrSetup to prevent 401s from conflicting auth methods in Jellyfin 12+.
327
329
  */
328
330
  useQueryToken?: boolean;
329
331
  }
@@ -1146,8 +1148,11 @@ declare class MediaModule {
1146
1148
  /**
1147
1149
  * Returns authentication headers for media streaming requests (e.g., configuring hls.js xhrSetup).
1148
1150
  * Crucial for Jellyfin 12+ servers where query-based auth (?api_key=) is disabled.
1151
+ * Pass { legacy: false } to only send Authorization and avoid conflicting headers or reverse-proxy CORS rejections.
1149
1152
  */
1150
- getStreamHeaders(): Record<string, string>;
1153
+ getStreamHeaders(options?: {
1154
+ legacy?: boolean;
1155
+ }): Record<string, string>;
1151
1156
  }
1152
1157
 
1153
1158
  declare class LyricsModule {
package/dist/index.d.ts CHANGED
@@ -306,10 +306,11 @@ interface AudioStreamOptions {
306
306
  transcodingContainer?: string;
307
307
  audioCodec?: string;
308
308
  deviceId?: string;
309
+ mediaSourceId?: string;
309
310
  /**
310
- * Whether to include the api_key token in the URL query parameters.
311
+ * Whether to include the authentication token in the URL query parameters (ApiKey, api_key, X-Emby-Token).
311
312
  * Defaults to true for HTML5 <audio> tag compatibility.
312
- * If false, callers are expected to provide Authorization headers.
313
+ * Set to false when providing modern Authorization headers to avoid conflicting authentication credentials in Jellyfin 12+.
313
314
  */
314
315
  useQueryToken?: boolean;
315
316
  }
@@ -320,10 +321,11 @@ interface AudioHlsStreamOptions {
320
321
  audioCodec?: string;
321
322
  segmentLength?: number;
322
323
  deviceId?: string;
324
+ mediaSourceId?: string;
323
325
  /**
324
- * Whether to include the api_key token in the URL query parameters.
326
+ * Whether to include the authentication token in the URL query parameters (ApiKey, api_key, X-Emby-Token).
325
327
  * Defaults to true for legacy compatibility.
326
- * If false, players (e.g. hls.js) should be configured with Authorization headers via xhrSetup.
328
+ * Set to false when players (e.g. hls.js) are configured with Authorization headers via xhrSetup to prevent 401s from conflicting auth methods in Jellyfin 12+.
327
329
  */
328
330
  useQueryToken?: boolean;
329
331
  }
@@ -1146,8 +1148,11 @@ declare class MediaModule {
1146
1148
  /**
1147
1149
  * Returns authentication headers for media streaming requests (e.g., configuring hls.js xhrSetup).
1148
1150
  * Crucial for Jellyfin 12+ servers where query-based auth (?api_key=) is disabled.
1151
+ * Pass { legacy: false } to only send Authorization and avoid conflicting headers or reverse-proxy CORS rejections.
1149
1152
  */
1150
- getStreamHeaders(): Record<string, string>;
1153
+ getStreamHeaders(options?: {
1154
+ legacy?: boolean;
1155
+ }): Record<string, string>;
1151
1156
  }
1152
1157
 
1153
1158
  declare class LyricsModule {
package/dist/index.js CHANGED
@@ -1186,6 +1186,7 @@ var MediaModule = class {
1186
1186
  const qualityResolved = resolveAudioQuality(options.quality);
1187
1187
  const maxStreamingBitrate = options.maxStreamingBitrate ?? qualityResolved.maxStreamingBitrate;
1188
1188
  const {
1189
+ mediaSourceId,
1189
1190
  static: isStatic = false,
1190
1191
  startTimeTicks = 0,
1191
1192
  container = "opus,mp3|mp3,aac,m4a,m4b,flac,wav,ogg",
@@ -1196,12 +1197,14 @@ var MediaModule = class {
1196
1197
  const endpoint = isStatic ? `/Audio/${itemId}/stream` : `/Audio/${itemId}/universal`;
1197
1198
  const url = new URL(`${serverUrl}${endpoint}`);
1198
1199
  if (token && useQueryToken) {
1200
+ url.searchParams.append("ApiKey", token);
1199
1201
  url.searchParams.append("api_key", token);
1200
1202
  url.searchParams.append("X-Emby-Token", token);
1201
1203
  }
1202
1204
  if (userId) url.searchParams.append("UserId", userId);
1203
1205
  const targetDeviceId = options.deviceId !== void 0 ? options.deviceId : clientInfo.deviceId;
1204
1206
  if (targetDeviceId) url.searchParams.append("DeviceId", targetDeviceId);
1207
+ if (mediaSourceId) url.searchParams.append("MediaSourceId", mediaSourceId);
1205
1208
  if (isStatic) {
1206
1209
  url.searchParams.append("static", "true");
1207
1210
  } else {
@@ -1232,6 +1235,7 @@ var MediaModule = class {
1232
1235
  const qualityResolved = resolveAudioQuality(options.quality);
1233
1236
  const maxStreamingBitrate = options.maxStreamingBitrate ?? qualityResolved.maxStreamingBitrate;
1234
1237
  const {
1238
+ mediaSourceId,
1235
1239
  startTimeTicks = 0,
1236
1240
  audioCodec = "aac,mp3",
1237
1241
  segmentLength = 3,
@@ -1239,13 +1243,16 @@ var MediaModule = class {
1239
1243
  } = options;
1240
1244
  const url = new URL(`${serverUrl}/Audio/${itemId}/master.m3u8`);
1241
1245
  if (token && useQueryToken) {
1246
+ url.searchParams.append("ApiKey", token);
1242
1247
  url.searchParams.append("api_key", token);
1243
1248
  url.searchParams.append("X-Emby-Token", token);
1244
1249
  }
1245
1250
  if (userId) url.searchParams.append("UserId", userId);
1246
1251
  const targetDeviceId = options.deviceId !== void 0 ? options.deviceId : clientInfo.deviceId;
1247
1252
  if (targetDeviceId) url.searchParams.append("DeviceId", targetDeviceId);
1248
- url.searchParams.append("MediaSourceId", itemId);
1253
+ if (mediaSourceId) {
1254
+ url.searchParams.append("MediaSourceId", mediaSourceId);
1255
+ }
1249
1256
  url.searchParams.append("AudioCodec", audioCodec);
1250
1257
  url.searchParams.append("TranscodingContainer", "ts");
1251
1258
  url.searchParams.append("TranscodingProtocol", "hls");
@@ -1291,6 +1298,7 @@ var MediaModule = class {
1291
1298
  const endpoint = container && !container.includes(",") ? `/Videos/${itemId}/stream.${container}` : `/Videos/${itemId}/stream`;
1292
1299
  const url = new URL(`${serverUrl}${endpoint}`);
1293
1300
  if (token && useQueryToken) {
1301
+ url.searchParams.append("ApiKey", token);
1294
1302
  url.searchParams.append("api_key", token);
1295
1303
  url.searchParams.append("X-Emby-Token", token);
1296
1304
  }
@@ -1356,6 +1364,7 @@ var MediaModule = class {
1356
1364
  } = options;
1357
1365
  const url = new URL(`${serverUrl}/Videos/${itemId}/master.m3u8`);
1358
1366
  if (token && useQueryToken) {
1367
+ url.searchParams.append("ApiKey", token);
1359
1368
  url.searchParams.append("api_key", token);
1360
1369
  url.searchParams.append("X-Emby-Token", token);
1361
1370
  }
@@ -1398,6 +1407,7 @@ var MediaModule = class {
1398
1407
  const sourceId = mediaSourceId || itemId;
1399
1408
  const url = new URL(`${serverUrl}/Videos/${itemId}/${sourceId}/Subtitles/${subtitleStreamIndex}/Stream.${format}`);
1400
1409
  if (token && useQueryToken) {
1410
+ url.searchParams.append("ApiKey", token);
1401
1411
  url.searchParams.append("api_key", token);
1402
1412
  }
1403
1413
  if (startTimeTicks > 0) {
@@ -1471,6 +1481,7 @@ var MediaModule = class {
1471
1481
  if (!serverUrl || !itemId) return "";
1472
1482
  const url = new URL(`${serverUrl}/Items/${itemId}/Download`);
1473
1483
  if (token && useQueryToken) {
1484
+ url.searchParams.append("ApiKey", token);
1474
1485
  url.searchParams.append("api_key", token);
1475
1486
  }
1476
1487
  if (filename) {
@@ -1517,13 +1528,17 @@ var MediaModule = class {
1517
1528
  /**
1518
1529
  * Returns authentication headers for media streaming requests (e.g., configuring hls.js xhrSetup).
1519
1530
  * Crucial for Jellyfin 12+ servers where query-based auth (?api_key=) is disabled.
1531
+ * Pass { legacy: false } to only send Authorization and avoid conflicting headers or reverse-proxy CORS rejections.
1520
1532
  */
1521
- getStreamHeaders() {
1533
+ getStreamHeaders(options = {}) {
1522
1534
  const authHeader = this.http.getAuthHeader();
1523
- return {
1524
- Authorization: authHeader,
1525
- "X-Emby-Authorization": authHeader
1535
+ const headers = {
1536
+ Authorization: authHeader
1526
1537
  };
1538
+ if (options.legacy ?? true) {
1539
+ headers["X-Emby-Authorization"] = authHeader;
1540
+ }
1541
+ return headers;
1527
1542
  }
1528
1543
  };
1529
1544
 
@@ -2855,6 +2870,7 @@ var TrickplayModule = class {
2855
2870
  const { mediaSourceId, useQueryToken = true } = options;
2856
2871
  const url = new URL(`${serverUrl}/Videos/${itemId}/Trickplay/${width}/tiles.m3u8`);
2857
2872
  if (token && useQueryToken) {
2873
+ url.searchParams.append("ApiKey", token);
2858
2874
  url.searchParams.append("api_key", token);
2859
2875
  url.searchParams.append("X-Emby-Token", token);
2860
2876
  }
@@ -2873,6 +2889,7 @@ var TrickplayModule = class {
2873
2889
  const { mediaSourceId, useQueryToken = true } = options;
2874
2890
  const url = new URL(`${serverUrl}/Videos/${itemId}/Trickplay/${width}/${index}.jpg`);
2875
2891
  if (token && useQueryToken) {
2892
+ url.searchParams.append("ApiKey", token);
2876
2893
  url.searchParams.append("api_key", token);
2877
2894
  url.searchParams.append("X-Emby-Token", token);
2878
2895
  }
@@ -3063,6 +3080,7 @@ var ChaptersModule = class {
3063
3080
  } = options;
3064
3081
  const url = new URL(`${serverUrl}/Items/${itemId}/Images/Chapter/${chapterIndex}`);
3065
3082
  if (token && useQueryToken) {
3083
+ url.searchParams.append("ApiKey", token);
3066
3084
  url.searchParams.append("api_key", token);
3067
3085
  url.searchParams.append("X-Emby-Token", token);
3068
3086
  }
@@ -3747,7 +3765,7 @@ var JellyfinClient = class {
3747
3765
  this.cache = new MemoryCacheAdapter();
3748
3766
  this.clientInfo = {
3749
3767
  name: options.clientInfo?.name || "@francofantomius/jellyfin",
3750
- version: options.clientInfo?.version || "0.4.0",
3768
+ version: options.clientInfo?.version || "0.4.1",
3751
3769
  device: options.clientInfo?.device || (typeof window !== "undefined" ? "Web Browser" : "Node.js"),
3752
3770
  deviceId: options.clientInfo?.deviceId || generateDeviceId()
3753
3771
  };