From b5c6403e2d262e55b3a6ee080eb4e73eb08aa282 Mon Sep 17 00:00:00 2001 From: herrrta <@> Date: Sat, 30 Nov 2024 16:36:21 -0500 Subject: [PATCH] # Add download size to offline media downloads - Added getDownloadSize helper function to display media size in MB or GB when appropriate --- components/downloads/DownloadSize.tsx | 31 +++++++++++++++++++++++++++ components/downloads/EpisodeCard.tsx | 6 ++++-- components/downloads/MovieCard.tsx | 4 +++- components/downloads/SeriesCard.tsx | 4 +++- utils/download.ts | 27 ++++++++++++++++++++++- 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 components/downloads/DownloadSize.tsx diff --git a/components/downloads/DownloadSize.tsx b/components/downloads/DownloadSize.tsx new file mode 100644 index 00000000..cc94306b --- /dev/null +++ b/components/downloads/DownloadSize.tsx @@ -0,0 +1,31 @@ +import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; +import React, {useEffect, useMemo, useState} from "react"; +import {Text} from "@/components/common/Text"; +import useDownloadHelper from "@/utils/download"; + +interface DownloadSizeProps { + items: BaseItemDto[]; +} + +export const DownloadSize: React.FC = ({ items }) => { + const { getDownloadSize } = useDownloadHelper(); + const [size, setSize] = useState(); + + useEffect(() => { + getDownloadSize(...items).then(setSize) + }, + [items] + ); + + const sizeText = useMemo(() => { + if (!size) + return "reading size..." + return size + }, [size]) + + return ( + <> + {sizeText} + + ); +}; \ No newline at end of file diff --git a/components/downloads/EpisodeCard.tsx b/components/downloads/EpisodeCard.tsx index 9b821df7..bbe9358b 100644 --- a/components/downloads/EpisodeCard.tsx +++ b/components/downloads/EpisodeCard.tsx @@ -1,6 +1,6 @@ import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import * as Haptics from "expo-haptics"; -import React, { useCallback, useMemo } from "react"; +import React, {useCallback, useMemo} from "react"; import { TouchableOpacity, View } from "react-native"; import { ActionSheetProvider, @@ -8,12 +8,13 @@ import { } from "@expo/react-native-action-sheet"; import { useDownloadedFileOpener } from "@/hooks/useDownloadedFileOpener"; -import { useDownload } from "@/providers/DownloadProvider"; +import {useDownload} from "@/providers/DownloadProvider"; import { storage } from "@/utils/mmkv"; import { Image } from "expo-image"; import { Ionicons } from "@expo/vector-icons"; import {Text} from "@/components/common/Text"; import {runtimeTicksToSeconds} from "@/utils/time"; +import {DownloadSize} from "@/components/downloads/DownloadSize"; interface EpisodeCardProps { item: BaseItemDto; @@ -114,6 +115,7 @@ export const EpisodeCard: React.FC = ({ item }) => { {runtimeTicksToSeconds(item.RunTimeTicks)} + {item.Overview} diff --git a/components/downloads/MovieCard.tsx b/components/downloads/MovieCard.tsx index 8b275d54..2feb5fda 100644 --- a/components/downloads/MovieCard.tsx +++ b/components/downloads/MovieCard.tsx @@ -4,7 +4,7 @@ import { } from "@expo/react-native-action-sheet"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import * as Haptics from "expo-haptics"; -import React, { useCallback, useMemo } from "react"; +import React, {useCallback, useEffect, useMemo, useState} from "react"; import { TouchableOpacity, View } from "react-native"; import { useDownloadedFileOpener } from "@/hooks/useDownloadedFileOpener"; @@ -13,6 +13,7 @@ import { storage } from "@/utils/mmkv"; import { Ionicons } from "@expo/vector-icons"; import { Image } from "expo-image"; import { ItemCardText } from "../ItemCardText"; +import {DownloadSize} from "@/components/downloads/DownloadSize"; interface MovieCardProps { item: BaseItemDto; @@ -97,6 +98,7 @@ export const MovieCard: React.FC = ({ item }) => { )} + ); }; diff --git a/components/downloads/SeriesCard.tsx b/components/downloads/SeriesCard.tsx index c02d3ecd..1b665829 100644 --- a/components/downloads/SeriesCard.tsx +++ b/components/downloads/SeriesCard.tsx @@ -1,11 +1,12 @@ import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import {TouchableOpacity, View} from "react-native"; import { Text } from "../common/Text"; -import React, {useMemo} from "react"; +import React, {useEffect, useMemo, useState} from "react"; import {storage} from "@/utils/mmkv"; import {Image} from "expo-image"; import {Ionicons} from "@expo/vector-icons"; import {router} from "expo-router"; +import {DownloadSize} from "@/components/downloads/DownloadSize"; export const SeriesCard: React.FC<{ items: BaseItemDto[] }> = ({items}) => { const base64Image = useMemo(() => { @@ -45,6 +46,7 @@ export const SeriesCard: React.FC<{ items: BaseItemDto[] }> = ({items}) => { {items[0].SeriesName} {items[0].ProductionYear} + ); diff --git a/utils/download.ts b/utils/download.ts index 4d7b9ea0..1c0dc9eb 100644 --- a/utils/download.ts +++ b/utils/download.ts @@ -4,6 +4,9 @@ import useImageStorage from "@/hooks/useImageStorage"; import {apiAtom} from "@/providers/JellyfinProvider"; import {useAtom} from "jotai"; import {storage} from "@/utils/mmkv"; +import {getDownloadedFileUrl} from "@/hooks/useDownloadedFileOpener"; +import * as FileSystem from 'expo-file-system' +import {FileInfo} from "expo-file-system"; const useDownloadHelper = () => { const [api] = useAtom(apiAtom); @@ -15,7 +18,29 @@ const useDownloadHelper = () => { } } - return { saveSeriesPrimaryImage } + const getDownloadSize = async (...items: BaseItemDto[]) => { + const sizes: number[] = []; + + await Promise.all(items.map(item => { + return new Promise(async (resolve, reject) => { + const url = await getDownloadedFileUrl(item.Id!); + if (url) { + const fileInfo: FileInfo = await FileSystem.getInfoAsync(url); + sizes.push(fileInfo.size); + resolve(sizes); + } else reject(); + }) + })); + + const size = sizes.reduce((sum, size) => sum + size, 0); + const gb = size / 1e+9; + + if (gb >= 1) + return `${gb.toFixed(2)} GB`; + return `${(size / 1024 / 1024).toFixed(2)} MB`; + } + + return { saveSeriesPrimaryImage, getDownloadSize } } export default useDownloadHelper; \ No newline at end of file