From 42e66b39cc6a88af341e0b187099a62d06c49579 Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Sun, 5 Jan 2025 03:11:36 +1100 Subject: [PATCH] WIP --- app/_layout.tsx | 17 +- hooks/useDownloadedFileOpener.ts | 9 +- hooks/useRemuxHlsToMp4.ts | 25 +- providers/DownloadItem.tsx | 673 +++++++++++++++++++++++++++++++ providers/DownloadProvider.tsx | 42 +- utils/mmkv.ts | 29 +- 6 files changed, 748 insertions(+), 47 deletions(-) create mode 100644 providers/DownloadItem.tsx diff --git a/app/_layout.tsx b/app/_layout.tsx index bf779be5..10f4e033 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -13,7 +13,7 @@ import { orientationAtom } from "@/utils/atoms/orientation"; import { Settings, useSettings } from "@/utils/atoms/settings"; import { BACKGROUND_FETCH_TASK } from "@/utils/background-tasks"; import { LogProvider, writeToLog } from "@/utils/log"; -import { storage } from "@/utils/mmkv"; +import { formatItemName, storage } from "@/utils/mmkv"; import { cancelJobById, getAllJobsByDeviceId } from "@/utils/optimize-server"; import { ActionSheetProvider } from "@expo/react-native-action-sheet"; import { BottomSheetModalProvider } from "@gorhom/bottom-sheet"; @@ -385,18 +385,15 @@ function Layout() { function saveDownloadedItemInfo(item: BaseItemDto) { try { const downloadedItems = storage.getString("downloadedItems"); - let items: BaseItemDto[] = downloadedItems + let items: { [key: string]: BaseItemDto } = downloadedItems ? JSON.parse(downloadedItems) - : []; + : {}; - const existingItemIndex = items.findIndex((i) => i.Id === item.Id); - if (existingItemIndex !== -1) { - items[existingItemIndex] = item; - } else { - items.push(item); + if (item.Id) { + item.Path = `${FileSystem.documentDirectory}${formatItemName(item)}.mp4`; + items[item.Id] = item; + storage.set("downloadedItems", JSON.stringify(items)); } - - storage.set("downloadedItems", JSON.stringify(items)); } catch (error) { writeToLog("ERROR", "Failed to save downloaded item information:", error); console.error("Failed to save downloaded item information:", error); diff --git a/hooks/useDownloadedFileOpener.ts b/hooks/useDownloadedFileOpener.ts index 4c630710..587a1ae0 100644 --- a/hooks/useDownloadedFileOpener.ts +++ b/hooks/useDownloadedFileOpener.ts @@ -1,5 +1,6 @@ import { usePlaySettings } from "@/providers/PlaySettingsProvider"; import { writeToLog } from "@/utils/log"; +import { getFilePathFromItemId } from "@/utils/mmkv"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; import * as FileSystem from "expo-file-system"; import { useRouter } from "expo-router"; @@ -17,13 +18,13 @@ export const getDownloadedFileUrl = async (itemId: string): Promise => { } const files = await FileSystem.readDirectoryAsync(directory); - const path = itemId!; - const matchingFile = files.find((file) => file.startsWith(path)); + const filePath = getFilePathFromItemId(itemId); + + const matchingFile = files.find((file) => file === filePath); if (!matchingFile) { - throw new Error(`No file found for item ${path}`); + throw new Error(`No file found for item ${filePath}`); } - return `${directory}${matchingFile}`; }; diff --git a/hooks/useRemuxHlsToMp4.ts b/hooks/useRemuxHlsToMp4.ts index 25492e33..4085cd04 100644 --- a/hooks/useRemuxHlsToMp4.ts +++ b/hooks/useRemuxHlsToMp4.ts @@ -18,6 +18,7 @@ import useDownloadHelper from "@/utils/download"; import { Api } from "@jellyfin/sdk"; import { useSettings } from "@/utils/atoms/settings"; import { JobStatus } from "@/utils/optimize-server"; +import { formatItemName } from "@/utils/mmkv"; const createFFmpegCommand = (url: string, output: string) => [ "-y", // overwrite output files without asking @@ -53,7 +54,12 @@ export const useRemuxHlsToMp4 = () => { const [settings] = useSettings(); const { saveImage } = useImageStorage(); const { saveSeriesPrimaryImage } = useDownloadHelper(); - const { saveDownloadedItemInfo, setProcesses, processes, APP_CACHE_DOWNLOAD_DIRECTORY } = useDownload(); + const { + saveDownloadedItemInfo, + setProcesses, + processes, + APP_CACHE_DOWNLOAD_DIRECTORY, + } = useDownload(); const onSaveAssets = async (api: Api, item: BaseItemDto) => { await saveSeriesPrimaryImage(item); @@ -73,13 +79,12 @@ export const useRemuxHlsToMp4 = () => { try { console.log("completeCallback"); const returnCode = await session.getReturnCode(); - if (returnCode.isValueSuccess()) { const stat = await session.getLastReceivedStatistics(); await FileSystem.moveAsync({ - from: `${APP_CACHE_DOWNLOAD_DIRECTORY}${item.Id}.mp4`, - to: `${FileSystem.documentDirectory}${item.Id}.mp4` - }) + from: `${APP_CACHE_DOWNLOAD_DIRECTORY}${item.Id}.mp4`, + to: `${FileSystem.documentDirectory}${formatItemName(item)}.mp4`, + }); await queryClient.invalidateQueries({ queryKey: ["downloadedItems"], }); @@ -131,12 +136,16 @@ export const useRemuxHlsToMp4 = () => { const startRemuxing = useCallback( async (item: BaseItemDto, url: string, mediaSource: MediaSourceInfo) => { - const cacheDir = await FileSystem.getInfoAsync(APP_CACHE_DOWNLOAD_DIRECTORY); + const cacheDir = await FileSystem.getInfoAsync( + APP_CACHE_DOWNLOAD_DIRECTORY + ); if (!cacheDir.exists) { - await FileSystem.makeDirectoryAsync(APP_CACHE_DOWNLOAD_DIRECTORY, {intermediates: true}) + await FileSystem.makeDirectoryAsync(APP_CACHE_DOWNLOAD_DIRECTORY, { + intermediates: true, + }); } - const output = APP_CACHE_DOWNLOAD_DIRECTORY + `${item.Id}.mp4` + const output = APP_CACHE_DOWNLOAD_DIRECTORY + `${item.Id}.mp4`; if (!api) throw new Error("API is not defined"); if (!item.Id) throw new Error("Item must have an Id"); diff --git a/providers/DownloadItem.tsx b/providers/DownloadItem.tsx new file mode 100644 index 00000000..9dccdae1 --- /dev/null +++ b/providers/DownloadItem.tsx @@ -0,0 +1,673 @@ +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit the class manually. + * + * Jellyfin API + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { ExtraType } from "@jellyfin/sdk/lib/generated-client"; + +/** + * This is strictly used as a data transfer object from the api layer. This holds information about a BaseItem in a format that is convenient for the client. + * @export + * @interface BaseItemDto + */ +export interface BaseItemDto { + /** + * Gets or sets the name. + * @type {string} + * @memberof BaseItemDto + */ + Name?: string | null; + /** + * Gets or sets the id. + * @type {string} + * @memberof BaseItemDto + */ + Id?: string; + /** + * Gets or sets the playlist item identifier. + * @type {string} + * @memberof BaseItemDto + */ + PlaylistItemId?: string | null; + + /** + * + * @type {ExtraType} + * @memberof BaseItemDto + */ + ExtraType?: ExtraType; + /** + * + * @type {string} + * @memberof BaseItemDto + */ + Container?: string | null; + /** + * Gets or sets the name of the sort. + * @type {string} + * @memberof BaseItemDto + */ + SortName?: string | null; + Path?: string | null; + /** + * Gets or sets the overview. + * @type {string} + * @memberof BaseItemDto + */ + Overview?: string | null; + /** + * Gets or sets the cumulative run time ticks. + * @type {number} + * @memberof BaseItemDto + */ + CumulativeRunTimeTicks?: number | null; + /** + * Gets or sets the run time ticks. + * @type {number} + * @memberof BaseItemDto + */ + RunTimeTicks?: number | null; + /** + * + * @type {PlayAccess} + * @memberof BaseItemDto + */ + PlayAccess?: PlayAccess; + /** + * Gets or sets the aspect ratio. + * @type {string} + * @memberof BaseItemDto + */ + AspectRatio?: string | null; + /** + * Gets or sets the production year. + * @type {number} + * @memberof BaseItemDto + */ + ProductionYear?: number | null; + /** + * Gets or sets a value indicating whether this instance is place holder. + * @type {boolean} + * @memberof BaseItemDto + */ + IsPlaceHolder?: boolean | null; + /** + * Gets or sets the number. + * @type {string} + * @memberof BaseItemDto + */ + Number?: string | null; + /** + * + * @type {string} + * @memberof BaseItemDto + */ + ChannelNumber?: string | null; + /** + * Gets or sets the index number. + * @type {number} + * @memberof BaseItemDto + */ + IndexNumber?: number | null; + /** + * Gets or sets the index number end. + * @type {number} + * @memberof BaseItemDto + */ + IndexNumberEnd?: number | null; + /** + * Gets or sets the parent index number. + * @type {number} + * @memberof BaseItemDto + */ + ParentIndexNumber?: number | null; + /** + * Gets or sets the trailer urls. + * @type {Array} + * @memberof BaseItemDto + */ + RemoteTrailers?: Array | null; + /** + * Gets or sets the provider ids. + * @type {{ [key: string]: string | null; }} + * @memberof BaseItemDto + */ + ProviderIds?: { + [key: string]: string | null; + } | null; + /** + * Gets or sets a value indicating whether this instance is HD. + * @type {boolean} + * @memberof BaseItemDto + */ + IsHD?: boolean | null; + /** + * Gets or sets a value indicating whether this instance is folder. + * @type {boolean} + * @memberof BaseItemDto + */ + IsFolder?: boolean | null; + /** + * Gets or sets the parent id. + * @type {string} + * @memberof BaseItemDto + */ + ParentId?: string | null; + /** + * + * @type {BaseItemKind} + * @memberof BaseItemDto + */ + Type?: BaseItemKind; + /** + * Gets or sets the people. + * @type {Array} + * @memberof BaseItemDto + */ + People?: Array | null; + /** + * Gets or sets the studios. + * @type {Array} + * @memberof BaseItemDto + */ + Studios?: Array | null; + /** + * + * @type {Array} + * @memberof BaseItemDto + */ + GenreItems?: Array | null; + /** + * Gets or sets whether the item has a logo, this will hold the Id of the Parent that has one. + * @type {string} + * @memberof BaseItemDto + */ + ParentLogoItemId?: string | null; + /** + * Gets or sets whether the item has any backdrops, this will hold the Id of the Parent that has one. + * @type {string} + * @memberof BaseItemDto + */ + ParentBackdropItemId?: string | null; + /** + * Gets or sets the parent backdrop image tags. + * @type {Array} + * @memberof BaseItemDto + */ + ParentBackdropImageTags?: Array | null; + /** + * Gets or sets the local trailer count. + * @type {number} + * @memberof BaseItemDto + */ + LocalTrailerCount?: number | null; + /** + * + * @type {UserItemDataDto} + * @memberof BaseItemDto + */ + UserData?: UserItemDataDto; + /** + * Gets or sets the recursive item count. + * @type {number} + * @memberof BaseItemDto + */ + RecursiveItemCount?: number | null; + /** + * Gets or sets the child count. + * @type {number} + * @memberof BaseItemDto + */ + ChildCount?: number | null; + /** + * Gets or sets the name of the series. + * @type {string} + * @memberof BaseItemDto + */ + SeriesName?: string | null; + /** + * Gets or sets the series id. + * @type {string} + * @memberof BaseItemDto + */ + SeriesId?: string | null; + /** + * Gets or sets the season identifier. + * @type {string} + * @memberof BaseItemDto + */ + SeasonId?: string | null; + /** + * Gets or sets the special feature count. + * @type {number} + * @memberof BaseItemDto + */ + SpecialFeatureCount?: number | null; + /** + * Gets or sets the display preferences id. + * @type {string} + * @memberof BaseItemDto + */ + DisplayPreferencesId?: string | null; + /** + * Gets or sets the status. + * @type {string} + * @memberof BaseItemDto + */ + Status?: string | null; + /** + * Gets or sets the air time. + * @type {string} + * @memberof BaseItemDto + */ + AirTime?: string | null; + /** + * Gets or sets the air days. + * @type {Array} + * @memberof BaseItemDto + */ + AirDays?: Array | null; + /** + * Gets or sets the tags. + * @type {Array} + * @memberof BaseItemDto + */ + Tags?: Array | null; + /** + * Gets or sets the primary image aspect ratio, after image enhancements. + * @type {number} + * @memberof BaseItemDto + */ + PrimaryImageAspectRatio?: number | null; + /** + * Gets or sets the artists. + * @type {Array} + * @memberof BaseItemDto + */ + Artists?: Array | null; + /** + * Gets or sets the artist items. + * @type {Array} + * @memberof BaseItemDto + */ + ArtistItems?: Array | null; + /** + * Gets or sets the album. + * @type {string} + * @memberof BaseItemDto + */ + Album?: string | null; + /** + * + * @type {CollectionType} + * @memberof BaseItemDto + */ + CollectionType?: CollectionType; + /** + * Gets or sets the display order. + * @type {string} + * @memberof BaseItemDto + */ + DisplayOrder?: string | null; + /** + * Gets or sets the album id. + * @type {string} + * @memberof BaseItemDto + */ + AlbumId?: string | null; + /** + * Gets or sets the album image tag. + * @type {string} + * @memberof BaseItemDto + */ + AlbumPrimaryImageTag?: string | null; + /** + * Gets or sets the series primary image tag. + * @type {string} + * @memberof BaseItemDto + */ + SeriesPrimaryImageTag?: string | null; + /** + * Gets or sets the album artist. + * @type {string} + * @memberof BaseItemDto + */ + AlbumArtist?: string | null; + /** + * Gets or sets the album artists. + * @type {Array} + * @memberof BaseItemDto + */ + AlbumArtists?: Array | null; + /** + * Gets or sets the name of the season. + * @type {string} + * @memberof BaseItemDto + */ + SeasonName?: string | null; + /** + * Gets or sets the media streams. + * @type {Array} + * @memberof BaseItemDto + */ + MediaStreams?: Array | null; + /** + * + * @type {VideoType} + * @memberof BaseItemDto + */ + VideoType?: VideoType; + /** + * Gets or sets the part count. + * @type {number} + * @memberof BaseItemDto + */ + PartCount?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + MediaSourceCount?: number | null; + /** + * Gets or sets the image tags. + * @type {{ [key: string]: string; }} + * @memberof BaseItemDto + */ + ImageTags?: { + [key: string]: string; + } | null; + /** + * Gets or sets the backdrop image tags. + * @type {Array} + * @memberof BaseItemDto + */ + BackdropImageTags?: Array | null; + /** + * Gets or sets the screenshot image tags. + * @type {Array} + * @memberof BaseItemDto + */ + ScreenshotImageTags?: Array | null; + /** + * Gets or sets the parent logo image tag. + * @type {string} + * @memberof BaseItemDto + */ + ParentLogoImageTag?: string | null; + /** + * Gets or sets whether the item has fan art, this will hold the Id of the Parent that has one. + * @type {string} + * @memberof BaseItemDto + */ + ParentArtItemId?: string | null; + /** + * Gets or sets the parent art image tag. + * @type {string} + * @memberof BaseItemDto + */ + ParentArtImageTag?: string | null; + /** + * Gets or sets the series thumb image tag. + * @type {string} + * @memberof BaseItemDto + */ + SeriesThumbImageTag?: string | null; + /** + * + * @type {BaseItemDtoImageBlurHashes} + * @memberof BaseItemDto + */ + ImageBlurHashes?: BaseItemDtoImageBlurHashes | null; + /** + * Gets or sets the series studio. + * @type {string} + * @memberof BaseItemDto + */ + SeriesStudio?: string | null; + /** + * Gets or sets the parent thumb item id. + * @type {string} + * @memberof BaseItemDto + */ + ParentThumbItemId?: string | null; + /** + * Gets or sets the parent thumb image tag. + * @type {string} + * @memberof BaseItemDto + */ + ParentThumbImageTag?: string | null; + /** + * Gets or sets the parent primary image item identifier. + * @type {string} + * @memberof BaseItemDto + */ + ParentPrimaryImageItemId?: string | null; + /** + * Gets or sets the parent primary image tag. + * @type {string} + * @memberof BaseItemDto + */ + ParentPrimaryImageTag?: string | null; + /** + * Gets or sets the chapters. + * @type {Array} + * @memberof BaseItemDto + */ + Chapters?: Array | null; + /** + * Gets or sets the trickplay manifest. + * @type {{ [key: string]: { [key: string]: TrickplayInfo; }; }} + * @memberof BaseItemDto + */ + Trickplay?: { + [key: string]: { + [key: string]: TrickplayInfo; + }; + } | null; + /** + * + * @type {LocationType} + * @memberof BaseItemDto + */ + LocationType?: LocationType; + /** + * + * @type {IsoType} + * @memberof BaseItemDto + */ + IsoType?: IsoType; + /** + * + * @type {MediaType} + * @memberof BaseItemDto + */ + MediaType?: MediaType; + /** + * Gets or sets the end date. + * @type {string} + * @memberof BaseItemDto + */ + EndDate?: string | null; + /** + * Gets or sets the locked fields. + * @type {Array} + * @memberof BaseItemDto + */ + LockedFields?: Array | null; + /** + * Gets or sets the trailer count. + * @type {number} + * @memberof BaseItemDto + */ + TrailerCount?: number | null; + /** + * Gets or sets the movie count. + * @type {number} + * @memberof BaseItemDto + */ + MovieCount?: number | null; + /** + * Gets or sets the series count. + * @type {number} + * @memberof BaseItemDto + */ + SeriesCount?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + ProgramCount?: number | null; + /** + * Gets or sets the episode count. + * @type {number} + * @memberof BaseItemDto + */ + EpisodeCount?: number | null; + /** + * Gets or sets the song count. + * @type {number} + * @memberof BaseItemDto + */ + SongCount?: number | null; + /** + * Gets or sets the album count. + * @type {number} + * @memberof BaseItemDto + */ + AlbumCount?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + ArtistCount?: number | null; + /** + * Gets or sets the music video count. + * @type {number} + * @memberof BaseItemDto + */ + MusicVideoCount?: number | null; + /** + * Gets or sets a value indicating whether [enable internet providers]. + * @type {boolean} + * @memberof BaseItemDto + */ + LockData?: boolean | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + Width?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + Height?: number | null; + /** + * + * @type {string} + * @memberof BaseItemDto + */ + CameraMake?: string | null; + /** + * + * @type {string} + * @memberof BaseItemDto + */ + CameraModel?: string | null; + /** + * + * @type {string} + * @memberof BaseItemDto + */ + Software?: string | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + ExposureTime?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + FocalLength?: number | null; + /** + * + * @type {ImageOrientation} + * @memberof BaseItemDto + */ + ImageOrientation?: ImageOrientation; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + Aperture?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + ShutterSpeed?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + Latitude?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + Longitude?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + Altitude?: number | null; + /** + * + * @type {number} + * @memberof BaseItemDto + */ + IsoSpeedRating?: number | null; + /** + * Gets or sets the series timer identifier. + * @type {string} + * @memberof BaseItemDto + */ + SeriesTimerId?: string | null; + /** + * Gets or sets the program identifier. + * @type {string} + * @memberof BaseItemDto + */ + ProgramId?: string | null; + + /** + * Gets or sets the completion percentage. + * @type {number} + * @memberof BaseItemDto + */ + CompletionPercentage?: number | null; + /** + * Gets or sets a value indicating whether this instance is repeat. + * @type {boolean} + * @memberof BaseItemDto + */ + IsRepeat?: boolean | null; +} diff --git a/providers/DownloadProvider.tsx b/providers/DownloadProvider.tsx index 78fbbe6f..49a3643a 100644 --- a/providers/DownloadProvider.tsx +++ b/providers/DownloadProvider.tsx @@ -19,14 +19,7 @@ import { download, setConfig, } from "@kesha-antonov/react-native-background-downloader"; -import MMKV from "react-native-mmkv"; -import { - focusManager, - QueryClient, - QueryClientProvider, - useQuery, - useQueryClient, -} from "@tanstack/react-query"; +import { focusManager, useQuery, useQueryClient } from "@tanstack/react-query"; import axios from "axios"; import * as FileSystem from "expo-file-system"; import { useRouter } from "expo-router"; @@ -45,7 +38,7 @@ import { apiAtom } from "./JellyfinProvider"; import * as Notifications from "expo-notifications"; import { getItemImage } from "@/utils/getItemImage"; import useImageStorage from "@/hooks/useImageStorage"; -import { storage } from "@/utils/mmkv"; +import { formatItemName, saveItemMapping, storage } from "@/utils/mmkv"; import useDownloadHelper from "@/utils/download"; import { FileInfo } from "expo-file-system"; import * as Haptics from "expo-haptics"; @@ -54,8 +47,11 @@ import * as Application from "expo-application"; export type DownloadedItem = { item: Partial; mediaSource: MediaSourceInfo; + fileSize: number; }; +export type DownloadedItem2 = {}; + export const processesAtom = atom([]); function onAppStateChange(status: AppStateStatus) { @@ -512,8 +508,10 @@ function useDownloadProvider() { const downloadedItems = storage.getString("downloadedItems"); if (downloadedItems) { - let items = JSON.parse(downloadedItems) as DownloadedItem[]; - items = items.filter((item) => item.item.Id !== id); + let items: { [key: string]: BaseItemDto } = downloadedItems + ? JSON.parse(downloadedItems) + : {}; + delete items[id]; storage.set("downloadedItems", JSON.stringify(items)); } @@ -586,7 +584,7 @@ function useDownloadProvider() { const appSizeUsage = useMemo(async () => { const sizes: number[] = downloadedFiles?.map((d) => { - return getDownloadedItemSize(d.item.Id!!); + return d.fileSize; }) || []; await forEveryDocumentDirFile( @@ -608,8 +606,10 @@ function useDownloadProvider() { try { const downloadedItems = storage.getString("downloadedItems"); if (downloadedItems) { - const items: DownloadedItem[] = JSON.parse(downloadedItems); - const item = items.find((i) => i.item.Id === itemId); + const items: { [key: string]: BaseItemDto } = downloadedItems + ? JSON.parse(downloadedItems) + : {}; + const item = items[itemId] as DownloadedItem; return item || null; } return null; @@ -623,7 +623,7 @@ function useDownloadProvider() { try { const downloadedItems = storage.getString("downloadedItems"); if (downloadedItems) { - return JSON.parse(downloadedItems) as DownloadedItem[]; + return Object.values(JSON.parse(downloadedItems)) as DownloadedItem[]; } else { return []; } @@ -636,11 +636,11 @@ function useDownloadProvider() { function saveDownloadedItemInfo(item: BaseItemDto, size: number = 0) { try { const downloadedItems = storage.getString("downloadedItems"); - let items: DownloadedItem[] = downloadedItems + const items: { [key: string]: BaseItemDto } = downloadedItems ? JSON.parse(downloadedItems) - : []; + : {}; - const existingItemIndex = items.findIndex((i) => i.item.Id === item.Id); + const chosenItem = items[item.Id!] || {}; const data = getDownloadItemInfoFromDiskTmp(item.Id!); @@ -651,12 +651,6 @@ function useDownloadProvider() { const newItem = { item, mediaSource: data.mediaSource }; - if (existingItemIndex !== -1) { - items[existingItemIndex] = newItem; - } else { - items.push(newItem); - } - deleteDownloadItemInfoFromDiskTmp(item.Id!); storage.set("downloadedItems", JSON.stringify(items)); diff --git a/utils/mmkv.ts b/utils/mmkv.ts index 25706d78..94066bfa 100644 --- a/utils/mmkv.ts +++ b/utils/mmkv.ts @@ -1,3 +1,30 @@ +import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; import { MMKV } from "react-native-mmkv"; -export const storage = new MMKV(); +const storage = new MMKV(); + +const saveItemMapping = (itemId: string | undefined, fileName: string) => { + if (!itemId) return; + storage.set(itemId, fileName); +}; + +const getFilePathFromItemId = (itemId: string): string | undefined => { + return storage.getString(itemId); +}; + +const formatItemName = (item: BaseItemDto) => { + if (item.Type === "Episode") { + const formattedParentIndexNumber = (item.ParentIndexNumber ?? 0) + .toString() + .padStart(2, "0"); + const formattedIndexNumber = (item.IndexNumber ?? 0) + .toString() + .padStart(2, "0"); + + const formattedString = `S${formattedParentIndexNumber}E${formattedIndexNumber}`; + return `${item.SeriesName} - ${formattedString} - ${item.Name}`; + } + return item.Name; +}; + +export { saveItemMapping, getFilePathFromItemId, storage, formatItemName };