mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-05-28 09:38:25 +01:00
fix(downloads): refresh BaseItemDto for Chapters before queuing offline
Some screens fetch items with a fields filter (e.g. series detail uses ["MediaSources", "MediaStreams", "Overview", "Trickplay"]) and pass the resulting DTO straight into the download flow. Jellyfin only returns Chapters in that case when explicitly listed, so the snapshot we store under DownloadedItem.item carries Chapters: undefined and the offline player renders no ticks / list / current-chapter label. initiateDownload now re-fetches the item via getUserLibraryApi.getItem (no fields filter -> full DTO incl. Chapters) when Chapters is missing, and uses the enriched item for both getDownloadUrl and the snapshot. If the refresh call fails we log and fall back to the original item — the download itself still proceeds. Trickplay offline already worked (useTrickplay reads trickPlayData.path from the downloaded sheets).
This commit is contained in:
@@ -9,6 +9,7 @@ import type {
|
|||||||
BaseItemDto,
|
BaseItemDto,
|
||||||
MediaSourceInfo,
|
MediaSourceInfo,
|
||||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
|
import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api/user-library-api";
|
||||||
import { type Href } from "expo-router";
|
import { type Href } from "expo-router";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
@@ -195,9 +196,30 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const downloadDetailsPromises = items.map(async (item) => {
|
const downloadDetailsPromises = items.map(async (item) => {
|
||||||
|
// Ensure the snapshot we store offline carries the Chapters array.
|
||||||
|
// Page-level fetches sometimes use a fields filter that omits it; the
|
||||||
|
// offline player would then render no chapter ticks / list.
|
||||||
|
let itemForDownload = item;
|
||||||
|
if (!itemForDownload.Chapters && itemForDownload.Id) {
|
||||||
|
try {
|
||||||
|
const enriched = await getUserLibraryApi(api).getItem({
|
||||||
|
itemId: itemForDownload.Id,
|
||||||
|
userId: user.Id!,
|
||||||
|
});
|
||||||
|
if (enriched.data) {
|
||||||
|
itemForDownload = enriched.data;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(
|
||||||
|
"[DownloadItem] failed to refresh item for Chapters, falling back to original",
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { mediaSource, audioIndex, subtitleIndex } =
|
const { mediaSource, audioIndex, subtitleIndex } =
|
||||||
itemsNotDownloaded.length > 1
|
itemsNotDownloaded.length > 1
|
||||||
? getDefaultPlaySettings(item, settings!)
|
? getDefaultPlaySettings(itemForDownload, settings!)
|
||||||
: {
|
: {
|
||||||
mediaSource: selectedOptions?.mediaSource,
|
mediaSource: selectedOptions?.mediaSource,
|
||||||
audioIndex: selectedOptions?.audioIndex,
|
audioIndex: selectedOptions?.audioIndex,
|
||||||
@@ -206,7 +228,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
|
|
||||||
const downloadDetails = await getDownloadUrl({
|
const downloadDetails = await getDownloadUrl({
|
||||||
api,
|
api,
|
||||||
item,
|
item: itemForDownload,
|
||||||
userId: user.Id!,
|
userId: user.Id!,
|
||||||
mediaSource: mediaSource!,
|
mediaSource: mediaSource!,
|
||||||
audioStreamIndex: audioIndex ?? -1,
|
audioStreamIndex: audioIndex ?? -1,
|
||||||
@@ -218,7 +240,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
url: downloadDetails?.url,
|
url: downloadDetails?.url,
|
||||||
item,
|
item: itemForDownload,
|
||||||
mediaSource: downloadDetails?.mediaSource,
|
mediaSource: downloadDetails?.mediaSource,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user