fix: update okhttp v5 and fix android download crash issues (#1203)

This commit is contained in:
Fredrik Burmester
2025-11-15 11:07:35 +01:00
committed by GitHub
parent 9a906e6d39
commit 1439bcee0d
5 changed files with 69 additions and 40 deletions

View File

@@ -132,13 +132,15 @@ export const DownloadItems: React.FC<DownloadProps> = ({
return itemsNotDownloaded.length === 0;
}, [items, itemsNotDownloaded]);
const itemsProcesses = useMemo(
() => processes?.filter((p) => itemIds.includes(p.item.Id)),
() =>
processes?.filter((p) => p?.item?.Id && itemIds.includes(p.item.Id)) ||
[],
[processes, itemIds],
);
const progress = useMemo(() => {
if (itemIds.length === 1)
return itemsProcesses.reduce((acc, p) => acc + p.progress, 0);
return itemsProcesses.reduce((acc, p) => acc + (p.progress || 0), 0);
return (
((itemIds.length -
queue.filter((q) => itemIds.includes(q.item.Id)).length) /
@@ -262,9 +264,9 @@ export const DownloadItems: React.FC<DownloadProps> = ({
closeModal();
// Wait for modal dismiss animation to complete
requestAnimationFrame(() => {
setTimeout(() => {
initiateDownload(...itemsToDownload);
});
}, 300);
} else {
toast.error(
t("home.downloads.toasts.you_are_not_allowed_to_download_files"),

View File

@@ -9,7 +9,11 @@ interface ActiveDownloadsProps extends ViewProps {}
export default function ActiveDownloads({ ...props }: ActiveDownloadsProps) {
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 (
<View {...props} className='bg-neutral-900 p-4 rounded-2xl'>
<Text className='text-lg font-bold'>
@@ -27,8 +31,8 @@ export default function ActiveDownloads({ ...props }: ActiveDownloadsProps) {
{t("home.downloads.active_downloads")}
</Text>
<View className='gap-y-2'>
{processes?.map((p: JobStatus) => (
<DownloadCard key={p.item.Id} process={p} />
{validProcesses.map((p: JobStatus) => (
<DownloadCard key={p.id} process={p} />
))}
</View>
</View>

View File

@@ -51,7 +51,7 @@ export const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
};
const eta = useMemo(() => {
if (!process.estimatedTotalSizeBytes || !process.bytesDownloaded) {
if (!process?.estimatedTotalSizeBytes || !process?.bytesDownloaded) {
return null;
}
@@ -66,13 +66,14 @@ export const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
}
return formatTimeString(secondsRemaining, "s");
}, [process.id, process.bytesDownloaded, process.estimatedTotalSizeBytes]);
}, [process?.id, process?.bytesDownloaded, process?.estimatedTotalSizeBytes]);
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)
if (process.maxBitrate.value) {
if (process?.maxBitrate?.value && process?.item?.RunTimeTicks) {
return estimateDownloadSize(
process.maxBitrate.value,
process.item.RunTimeTicks,
@@ -81,32 +82,43 @@ export const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
return undefined;
}, [
process.maxBitrate.value,
process.item.RunTimeTicks,
process.estimatedTotalSizeBytes,
process?.maxBitrate?.value,
process?.item?.RunTimeTicks,
process?.estimatedTotalSizeBytes,
]);
const isTranscoding = process.isTranscoding || false;
const isTranscoding = process?.isTranscoding || false;
const downloadedAmount = useMemo(() => {
if (!process.bytesDownloaded) return null;
if (!process?.bytesDownloaded) return null;
return formatBytes(process.bytesDownloaded);
}, [process.bytesDownloaded]);
}, [process?.bytesDownloaded]);
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
const sanitizedProgress = useMemo(() => {
if (
typeof process.progress !== "number" ||
typeof process?.progress !== "number" ||
Number.isNaN(process.progress)
) {
return 0;
}
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 (
<TouchableOpacity

View File

@@ -35,7 +35,7 @@ android {
dependencies {
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 {

View File

@@ -58,31 +58,42 @@ function useDownloadProvider() {
| Partial<JobStatus>
| ((current: JobStatus) => Partial<JobStatus>),
) => {
setProcesses((prev) =>
prev.map((p) => {
if (p.id !== processId) return p;
const newStatus =
typeof updater === "function" ? updater(p) : updater;
return {
...p,
...newStatus,
};
}),
);
setProcesses((prev) => {
const processIndex = prev.findIndex((p) => p.id === processId);
if (processIndex === -1) return prev;
const currentProcess = prev[processIndex];
if (!currentProcess) return prev;
const newStatus =
typeof updater === "function" ? updater(currentProcess) : updater;
// Create new array with updated process
const newProcesses = [...prev];
newProcesses[processIndex] = {
...currentProcess,
...newStatus,
};
return newProcesses;
});
},
[setProcesses],
);
const removeProcess = useCallback(
(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
taskMapRef.current.forEach((processId, taskId) => {
if (processId === id) {
taskMapRef.current.delete(taskId);
}
});
// Find and remove from task map
taskMapRef.current.forEach((processId, taskId) => {
if (processId === id) {
taskMapRef.current.delete(taskId);
}
});
}, 0);
},
[setProcesses],
);