diff --git a/front/apps/mobile/package.json b/front/apps/mobile/package.json index c024116c..39082e60 100644 --- a/front/apps/mobile/package.json +++ b/front/apps/mobile/package.json @@ -18,7 +18,6 @@ "@formatjs/intl-displaynames": "^6.8.1", "@formatjs/intl-locale": "^4.2.1", "@gorhom/portal": "^1.0.14", - "@kesha-antonov/react-native-background-downloader": "^3.2.1", "@kyoo/ui": "workspace:^", "@material-symbols/svg-400": "^0.25.2", "@react-native-community/netinfo": "11.4.1", diff --git a/front/packages/ui/package.json b/front/packages/ui/package.json index b34f8e88..84e108a2 100644 --- a/front/packages/ui/package.json +++ b/front/packages/ui/package.json @@ -14,7 +14,6 @@ }, "peerDependencies": { "@gorhom/portal": "*", - "@kesha-antonov/react-native-background-downloader": "*", "@material-symbols/svg-400": "*", "@shopify/flash-list": "*", "@tanstack/react-query": "*", diff --git a/front/packages/ui/src/downloads/state.tsx b/front/packages/ui/src/downloads/state.tsx index c1d8b503..b8a6e959 100644 --- a/front/packages/ui/src/downloads/state.tsx +++ b/front/packages/ui/src/downloads/state.tsx @@ -18,9 +18,6 @@ * along with Kyoo. If not, see . */ -import RNBackgroundDownloader, { - type DownloadTask, -} from "@kesha-antonov/react-native-background-downloader"; import { type Account, type Episode, @@ -80,75 +77,75 @@ const query = (query: QueryIdentifier, info: Account): Promise => info.token.access_token, ); -const setupDownloadTask = ( - state: { data: Episode | Movie; info: WatchInfo; path: string }, - task: DownloadTask, - store: ReturnType, - queryClient: QueryClient, - stateAtom?: PrimitiveAtom, -) => { - if (!stateAtom) stateAtom = atom({} as State); - store.set(stateAtom, { - status: task.state, - progress: task.bytesTotal ? (task.bytesDownloaded / task.bytesTotal) * 100 : null, - size: task.bytesTotal, - availableSize: task.bytesDownloaded, - pause: () => { - task.pause(); - store.set(stateAtom!, (x) => ({ ...x, state: "PAUSED" })); - }, - resume: () => { - task.resume(); - store.set(stateAtom!, (x) => ({ ...x, state: "DOWNLOADING" })); - }, - remove: () => { - task.stop(); - store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== task.id)); - }, - play: () => { - ToastAndroid.show("The file has not finished downloading", ToastAndroid.LONG); - }, - retry: () => { - const [newTask, path] = download( - { - type: state.data.kind, - id: state.data.id, - slug: state.data.slug, - extension: state.info.extension, - }, - getCurrentAccount()!, - ); - setupDownloadTask({ ...state, path }, newTask, store, queryClient, stateAtom); - }, - }); - - // we use the store instead of the onMount because we want to update the state to cache it even if it was not - // used during this launch of the app. - const update = updater(store, stateAtom); - - task - .begin(({ expectedBytes }) => update((x) => ({ ...x, size: expectedBytes }))) - .progress(({ bytesDownloaded, bytesTotal }) => { - update((x) => ({ - ...x, - progress: Math.round((bytesDownloaded / bytesTotal) * 100), - size: bytesTotal, - availableSize: bytesDownloaded, - status: "DOWNLOADING", - })); - }) - .done(() => { - update((x) => ({ ...x, progress: 100, status: "DONE", play: playFn(state, queryClient) })); - RNBackgroundDownloader.completeHandler(task.id); - }) - .error(({ error }) => { - update((x) => ({ ...x, status: "FAILED", error })); - console.error(`Error downloading ${state.data.slug}`, error); - ToastAndroid.show(`Error downloading ${state.data.slug}`, ToastAndroid.LONG); - }); - - return { data: state.data, info: state.info, path: state.path, state: stateAtom }; -}; +// const setupDownloadTask = ( +// state: { data: Episode | Movie; info: WatchInfo; path: string }, +// task: DownloadTask, +// store: ReturnType, +// queryClient: QueryClient, +// stateAtom?: PrimitiveAtom, +// ) => { +// if (!stateAtom) stateAtom = atom({} as State); +// store.set(stateAtom, { +// status: task.state, +// progress: task.bytesTotal ? (task.bytesDownloaded / task.bytesTotal) * 100 : null, +// size: task.bytesTotal, +// availableSize: task.bytesDownloaded, +// pause: () => { +// task.pause(); +// store.set(stateAtom!, (x) => ({ ...x, state: "PAUSED" })); +// }, +// resume: () => { +// task.resume(); +// store.set(stateAtom!, (x) => ({ ...x, state: "DOWNLOADING" })); +// }, +// remove: () => { +// task.stop(); +// store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== task.id)); +// }, +// play: () => { +// ToastAndroid.show("The file has not finished downloading", ToastAndroid.LONG); +// }, +// retry: () => { +// const [newTask, path] = download( +// { +// type: state.data.kind, +// id: state.data.id, +// slug: state.data.slug, +// extension: state.info.extension, +// }, +// getCurrentAccount()!, +// ); +// setupDownloadTask({ ...state, path }, newTask, store, queryClient, stateAtom); +// }, +// }); +// +// // we use the store instead of the onMount because we want to update the state to cache it even if it was not +// // used during this launch of the app. +// const update = updater(store, stateAtom); +// +// task +// .begin(({ expectedBytes }) => update((x) => ({ ...x, size: expectedBytes }))) +// .progress(({ bytesDownloaded, bytesTotal }) => { +// update((x) => ({ +// ...x, +// progress: Math.round((bytesDownloaded / bytesTotal) * 100), +// size: bytesTotal, +// availableSize: bytesDownloaded, +// status: "DOWNLOADING", +// })); +// }) +// .done(() => { +// update((x) => ({ ...x, progress: 100, status: "DONE", play: playFn(state, queryClient) })); +// RNBackgroundDownloader.completeHandler(task.id); +// }) +// .error(({ error }) => { +// update((x) => ({ ...x, status: "FAILED", error })); +// console.error(`Error downloading ${state.data.slug}`, error); +// ToastAndroid.show(`Error downloading ${state.data.slug}`, ToastAndroid.LONG); +// }); +// +// return { data: state.data, info: state.info, path: state.path, state: stateAtom }; +// }; const updater = ( store: ReturnType, @@ -173,32 +170,32 @@ const updater = ( }; }; -const download = ( - { - type, - id, - slug, - extension, - }: { slug: string; id: string; type: "episode" | "movie"; extension: string }, - account: Account, -) => { - // TODO: support custom paths - const path = `${RNBackgroundDownloader.directories.documents}/${slug}-${id}.${extension}`; - const task = RNBackgroundDownloader.download({ - id: id, - // TODO: support variant qualities - url: `${account.apiUrl}/${type}/${slug}/direct`, - destination: path, - headers: { - Authorization: account.token.access_token, - }, - isNotificationVisible: true, - // TODO: Implement only wifi - // network: Network.ALL, - }); - console.log("Starting download", path); - return [task, path] as const; -}; +// const download = ( +// { +// type, +// id, +// slug, +// extension, +// }: { slug: string; id: string; type: "episode" | "movie"; extension: string }, +// account: Account, +// ) => { +// // TODO: support custom paths +// const path = `${RNBackgroundDownloader.directories.documents}/${slug}-${id}.${extension}`; +// const task = RNBackgroundDownloader.download({ +// id: id, +// // TODO: support variant qualities +// url: `${account.apiUrl}/${type}/${slug}/direct`, +// destination: path, +// headers: { +// Authorization: account.token.access_token, +// }, +// isNotificationVisible: true, +// // TODO: Implement only wifi +// // network: Network.ALL, +// }); +// console.log("Starting download", path); +// return [task, path] as const; +// }; export const useDownloader = () => { const setDownloads = useSetAtom(downloadAtom); @@ -206,30 +203,30 @@ export const useDownloader = () => { const queryClient = useQueryClient(); return async (type: "episode" | "movie", slug: string) => { - try { - const account = getCurrentAccount()!; - const [data, info] = await Promise.all([ - query(Player.query(type, slug), account), - query(Player.infoQuery(type, slug), account), - ]); - - if (store.get(downloadAtom).find((x) => x.data.id === data.id)) { - ToastAndroid.show(`${slug} is already downloaded, skipping`, ToastAndroid.LONG); - return; - } - - const [task, path] = download( - { type, slug, id: data.id, extension: info.extension }, - account, - ); - setDownloads((x) => [ - ...x, - setupDownloadTask({ data, info, path }, task, store, queryClient), - ]); - } catch (e) { - console.error("download error", e); - ToastAndroid.show(`Error downloading ${slug}`, ToastAndroid.LONG); - } + // try { + // const account = getCurrentAccount()!; + // const [data, info] = await Promise.all([ + // query(Player.query(type, slug), account), + // query(Player.infoQuery(type, slug), account), + // ]); + // + // if (store.get(downloadAtom).find((x) => x.data.id === data.id)) { + // ToastAndroid.show(`${slug} is already downloaded, skipping`, ToastAndroid.LONG); + // return; + // } + // + // const [task, path] = download( + // { type, slug, id: data.id, extension: info.extension }, + // account, + // ); + // setDownloads((x) => [ + // ...x, + // setupDownloadTask({ data, info, path }, task, store, queryClient), + // ]); + // } catch (e) { + // console.error("download error", e); + // ToastAndroid.show(`Error downloading ${slug}`, ToastAndroid.LONG); + // } }; }; @@ -248,61 +245,61 @@ const playFn = }; export const DownloadProvider = ({ children }: { children: ReactNode }) => { - const store = useStore(); - const queryClient = useQueryClient(); - - useEffect(() => { - async function run() { - if (store.get(downloadAtom).length) return; - - const tasks = await RNBackgroundDownloader.checkForExistingDownloads(); - const dls: { data: Episode | Movie; info: WatchInfo; path: string; state: State }[] = - JSON.parse(storage.getString("downloads") ?? "[]"); - const downloads = dls.map((dl) => { - const t = tasks.find((x) => x.id === dl.data.id); - if (t) return setupDownloadTask(dl, t, store, queryClient); - - const stateAtom = atom({ - status: dl.state.status === "DONE" ? "DONE" : "FAILED", - progress: dl.state.progress, - size: dl.state.size, - availableSize: dl.state.availableSize, - pause: null, - resume: null, - play: playFn(dl, queryClient), - remove: () => { - deleteAsync(dl.path); - store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== dl.data.id)); - }, - retry: () => { - const [newTask, path] = download( - { - type: dl.data.kind, - id: dl.data.id, - slug: dl.data.slug, - extension: dl.info.extension, - }, - getCurrentAccount()!, - ); - setupDownloadTask({ ...dl, path }, newTask, store, queryClient, stateAtom); - }, - } as State); - return { - data: z.union([EpisodeP, MovieP]).parse(dl.data), - info: WatchInfoP.parse(dl.info), - path: dl.path, - state: stateAtom, - }; - }); - store.set(downloadAtom, downloads); - - for (const t of tasks) { - if (!downloads.find((x) => x.data.id === t.id)) t.stop(); - } - RNBackgroundDownloader.ensureDownloadsAreRunning(); - } - run(); - }, [store, queryClient]); + // const store = useStore(); + // const queryClient = useQueryClient(); + // + // useEffect(() => { + // async function run() { + // if (store.get(downloadAtom).length) return; + // + // const tasks = await RNBackgroundDownloader.checkForExistingDownloads(); + // const dls: { data: Episode | Movie; info: WatchInfo; path: string; state: State }[] = + // JSON.parse(storage.getString("downloads") ?? "[]"); + // const downloads = dls.map((dl) => { + // const t = tasks.find((x) => x.id === dl.data.id); + // if (t) return setupDownloadTask(dl, t, store, queryClient); + // + // const stateAtom = atom({ + // status: dl.state.status === "DONE" ? "DONE" : "FAILED", + // progress: dl.state.progress, + // size: dl.state.size, + // availableSize: dl.state.availableSize, + // pause: null, + // resume: null, + // play: playFn(dl, queryClient), + // remove: () => { + // deleteAsync(dl.path); + // store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== dl.data.id)); + // }, + // retry: () => { + // const [newTask, path] = download( + // { + // type: dl.data.kind, + // id: dl.data.id, + // slug: dl.data.slug, + // extension: dl.info.extension, + // }, + // getCurrentAccount()!, + // ); + // setupDownloadTask({ ...dl, path }, newTask, store, queryClient, stateAtom); + // }, + // } as State); + // return { + // data: z.union([EpisodeP, MovieP]).parse(dl.data), + // info: WatchInfoP.parse(dl.info), + // path: dl.path, + // state: stateAtom, + // }; + // }); + // store.set(downloadAtom, downloads); + // + // for (const t of tasks) { + // if (!downloads.find((x) => x.data.id === t.id)) t.stop(); + // } + // RNBackgroundDownloader.ensureDownloadsAreRunning(); + // } + // run(); + // }, [store, queryClient]); return children; };