mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 15:48:05 +00:00
fix: update okhttp v5 and fix android download crash issues (#1203)
This commit is contained in:
committed by
GitHub
parent
9a906e6d39
commit
1439bcee0d
@@ -132,13 +132,15 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
return itemsNotDownloaded.length === 0;
|
return itemsNotDownloaded.length === 0;
|
||||||
}, [items, itemsNotDownloaded]);
|
}, [items, itemsNotDownloaded]);
|
||||||
const itemsProcesses = useMemo(
|
const itemsProcesses = useMemo(
|
||||||
() => processes?.filter((p) => itemIds.includes(p.item.Id)),
|
() =>
|
||||||
|
processes?.filter((p) => p?.item?.Id && itemIds.includes(p.item.Id)) ||
|
||||||
|
[],
|
||||||
[processes, itemIds],
|
[processes, itemIds],
|
||||||
);
|
);
|
||||||
|
|
||||||
const progress = useMemo(() => {
|
const progress = useMemo(() => {
|
||||||
if (itemIds.length === 1)
|
if (itemIds.length === 1)
|
||||||
return itemsProcesses.reduce((acc, p) => acc + p.progress, 0);
|
return itemsProcesses.reduce((acc, p) => acc + (p.progress || 0), 0);
|
||||||
return (
|
return (
|
||||||
((itemIds.length -
|
((itemIds.length -
|
||||||
queue.filter((q) => itemIds.includes(q.item.Id)).length) /
|
queue.filter((q) => itemIds.includes(q.item.Id)).length) /
|
||||||
@@ -262,9 +264,9 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
closeModal();
|
closeModal();
|
||||||
|
|
||||||
// Wait for modal dismiss animation to complete
|
// Wait for modal dismiss animation to complete
|
||||||
requestAnimationFrame(() => {
|
setTimeout(() => {
|
||||||
initiateDownload(...itemsToDownload);
|
initiateDownload(...itemsToDownload);
|
||||||
});
|
}, 300);
|
||||||
} else {
|
} else {
|
||||||
toast.error(
|
toast.error(
|
||||||
t("home.downloads.toasts.you_are_not_allowed_to_download_files"),
|
t("home.downloads.toasts.you_are_not_allowed_to_download_files"),
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ interface ActiveDownloadsProps extends ViewProps {}
|
|||||||
|
|
||||||
export default function ActiveDownloads({ ...props }: ActiveDownloadsProps) {
|
export default function ActiveDownloads({ ...props }: ActiveDownloadsProps) {
|
||||||
const { processes } = useDownload();
|
const { processes } = useDownload();
|
||||||
if (processes?.length === 0)
|
|
||||||
|
// Filter out any invalid processes before rendering
|
||||||
|
const validProcesses = processes?.filter((p) => p?.item?.Id) || [];
|
||||||
|
|
||||||
|
if (validProcesses.length === 0)
|
||||||
return (
|
return (
|
||||||
<View {...props} className='bg-neutral-900 p-4 rounded-2xl'>
|
<View {...props} className='bg-neutral-900 p-4 rounded-2xl'>
|
||||||
<Text className='text-lg font-bold'>
|
<Text className='text-lg font-bold'>
|
||||||
@@ -27,8 +31,8 @@ export default function ActiveDownloads({ ...props }: ActiveDownloadsProps) {
|
|||||||
{t("home.downloads.active_downloads")}
|
{t("home.downloads.active_downloads")}
|
||||||
</Text>
|
</Text>
|
||||||
<View className='gap-y-2'>
|
<View className='gap-y-2'>
|
||||||
{processes?.map((p: JobStatus) => (
|
{validProcesses.map((p: JobStatus) => (
|
||||||
<DownloadCard key={p.item.Id} process={p} />
|
<DownloadCard key={p.id} process={p} />
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const eta = useMemo(() => {
|
const eta = useMemo(() => {
|
||||||
if (!process.estimatedTotalSizeBytes || !process.bytesDownloaded) {
|
if (!process?.estimatedTotalSizeBytes || !process?.bytesDownloaded) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,13 +66,14 @@ export const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return formatTimeString(secondsRemaining, "s");
|
return formatTimeString(secondsRemaining, "s");
|
||||||
}, [process.id, process.bytesDownloaded, process.estimatedTotalSizeBytes]);
|
}, [process?.id, process?.bytesDownloaded, process?.estimatedTotalSizeBytes]);
|
||||||
|
|
||||||
const estimatedSize = useMemo(() => {
|
const estimatedSize = useMemo(() => {
|
||||||
if (process.estimatedTotalSizeBytes) return process.estimatedTotalSizeBytes;
|
if (process?.estimatedTotalSizeBytes)
|
||||||
|
return process.estimatedTotalSizeBytes;
|
||||||
|
|
||||||
// Calculate from bitrate + duration (only if bitrate value is defined)
|
// Calculate from bitrate + duration (only if bitrate value is defined)
|
||||||
if (process.maxBitrate.value) {
|
if (process?.maxBitrate?.value && process?.item?.RunTimeTicks) {
|
||||||
return estimateDownloadSize(
|
return estimateDownloadSize(
|
||||||
process.maxBitrate.value,
|
process.maxBitrate.value,
|
||||||
process.item.RunTimeTicks,
|
process.item.RunTimeTicks,
|
||||||
@@ -81,32 +82,43 @@ export const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [
|
}, [
|
||||||
process.maxBitrate.value,
|
process?.maxBitrate?.value,
|
||||||
process.item.RunTimeTicks,
|
process?.item?.RunTimeTicks,
|
||||||
process.estimatedTotalSizeBytes,
|
process?.estimatedTotalSizeBytes,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isTranscoding = process.isTranscoding || false;
|
const isTranscoding = process?.isTranscoding || false;
|
||||||
|
|
||||||
const downloadedAmount = useMemo(() => {
|
const downloadedAmount = useMemo(() => {
|
||||||
if (!process.bytesDownloaded) return null;
|
if (!process?.bytesDownloaded) return null;
|
||||||
return formatBytes(process.bytesDownloaded);
|
return formatBytes(process.bytesDownloaded);
|
||||||
}, [process.bytesDownloaded]);
|
}, [process?.bytesDownloaded]);
|
||||||
|
|
||||||
const base64Image = useMemo(() => {
|
const base64Image = useMemo(() => {
|
||||||
return storage.getString(process.item.Id!);
|
try {
|
||||||
}, []);
|
const itemId = process?.item?.Id;
|
||||||
|
if (!itemId) return undefined;
|
||||||
|
return storage.getString(itemId);
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}, [process?.item?.Id]);
|
||||||
|
|
||||||
// Sanitize progress to ensure it's within valid bounds
|
// Sanitize progress to ensure it's within valid bounds
|
||||||
const sanitizedProgress = useMemo(() => {
|
const sanitizedProgress = useMemo(() => {
|
||||||
if (
|
if (
|
||||||
typeof process.progress !== "number" ||
|
typeof process?.progress !== "number" ||
|
||||||
Number.isNaN(process.progress)
|
Number.isNaN(process.progress)
|
||||||
) {
|
) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return Math.max(0, Math.min(100, process.progress));
|
return Math.max(0, Math.min(100, process.progress));
|
||||||
}, [process.progress]);
|
}, [process?.progress]);
|
||||||
|
|
||||||
|
// Return null after all hooks have been called
|
||||||
|
if (!process || !process.item || !process.item.Id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||||
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
implementation "com.squareup.okhttp3:okhttp:5.3.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
||||||
|
|||||||
@@ -58,31 +58,42 @@ function useDownloadProvider() {
|
|||||||
| Partial<JobStatus>
|
| Partial<JobStatus>
|
||||||
| ((current: JobStatus) => Partial<JobStatus>),
|
| ((current: JobStatus) => Partial<JobStatus>),
|
||||||
) => {
|
) => {
|
||||||
setProcesses((prev) =>
|
setProcesses((prev) => {
|
||||||
prev.map((p) => {
|
const processIndex = prev.findIndex((p) => p.id === processId);
|
||||||
if (p.id !== processId) return p;
|
if (processIndex === -1) return prev;
|
||||||
const newStatus =
|
|
||||||
typeof updater === "function" ? updater(p) : updater;
|
const currentProcess = prev[processIndex];
|
||||||
return {
|
if (!currentProcess) return prev;
|
||||||
...p,
|
|
||||||
...newStatus,
|
const newStatus =
|
||||||
};
|
typeof updater === "function" ? updater(currentProcess) : updater;
|
||||||
}),
|
|
||||||
);
|
// Create new array with updated process
|
||||||
|
const newProcesses = [...prev];
|
||||||
|
newProcesses[processIndex] = {
|
||||||
|
...currentProcess,
|
||||||
|
...newStatus,
|
||||||
|
};
|
||||||
|
|
||||||
|
return newProcesses;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setProcesses],
|
[setProcesses],
|
||||||
);
|
);
|
||||||
|
|
||||||
const removeProcess = useCallback(
|
const removeProcess = useCallback(
|
||||||
(id: string) => {
|
(id: string) => {
|
||||||
setProcesses((prev) => prev.filter((process) => process.id !== id));
|
// Use setTimeout to defer removal and avoid race conditions during rendering
|
||||||
|
setTimeout(() => {
|
||||||
|
setProcesses((prev) => prev.filter((process) => process.id !== id));
|
||||||
|
|
||||||
// Find and remove from task map
|
// Find and remove from task map
|
||||||
taskMapRef.current.forEach((processId, taskId) => {
|
taskMapRef.current.forEach((processId, taskId) => {
|
||||||
if (processId === id) {
|
if (processId === id) {
|
||||||
taskMapRef.current.delete(taskId);
|
taskMapRef.current.delete(taskId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}, 0);
|
||||||
},
|
},
|
||||||
[setProcesses],
|
[setProcesses],
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user