Comment downloader code to try newarch

This commit is contained in:
Zoe Roux 2024-10-30 19:29:03 +01:00
parent ab2201a19a
commit 18f7bda090
No known key found for this signature in database
3 changed files with 174 additions and 179 deletions

View File

@ -18,7 +18,6 @@
"@formatjs/intl-displaynames": "^6.8.1", "@formatjs/intl-displaynames": "^6.8.1",
"@formatjs/intl-locale": "^4.2.1", "@formatjs/intl-locale": "^4.2.1",
"@gorhom/portal": "^1.0.14", "@gorhom/portal": "^1.0.14",
"@kesha-antonov/react-native-background-downloader": "^3.2.1",
"@kyoo/ui": "workspace:^", "@kyoo/ui": "workspace:^",
"@material-symbols/svg-400": "^0.25.2", "@material-symbols/svg-400": "^0.25.2",
"@react-native-community/netinfo": "11.4.1", "@react-native-community/netinfo": "11.4.1",

View File

@ -14,7 +14,6 @@
}, },
"peerDependencies": { "peerDependencies": {
"@gorhom/portal": "*", "@gorhom/portal": "*",
"@kesha-antonov/react-native-background-downloader": "*",
"@material-symbols/svg-400": "*", "@material-symbols/svg-400": "*",
"@shopify/flash-list": "*", "@shopify/flash-list": "*",
"@tanstack/react-query": "*", "@tanstack/react-query": "*",

View File

@ -18,9 +18,6 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>. * along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/ */
import RNBackgroundDownloader, {
type DownloadTask,
} from "@kesha-antonov/react-native-background-downloader";
import { import {
type Account, type Account,
type Episode, type Episode,
@ -80,75 +77,75 @@ const query = <T,>(query: QueryIdentifier<T>, info: Account): Promise<T> =>
info.token.access_token, info.token.access_token,
); );
const setupDownloadTask = ( // const setupDownloadTask = (
state: { data: Episode | Movie; info: WatchInfo; path: string }, // state: { data: Episode | Movie; info: WatchInfo; path: string },
task: DownloadTask, // task: DownloadTask,
store: ReturnType<typeof useStore>, // store: ReturnType<typeof useStore>,
queryClient: QueryClient, // queryClient: QueryClient,
stateAtom?: PrimitiveAtom<State>, // stateAtom?: PrimitiveAtom<State>,
) => { // ) => {
if (!stateAtom) stateAtom = atom({} as State); // if (!stateAtom) stateAtom = atom({} as State);
store.set(stateAtom, { // store.set(stateAtom, {
status: task.state, // status: task.state,
progress: task.bytesTotal ? (task.bytesDownloaded / task.bytesTotal) * 100 : null, // progress: task.bytesTotal ? (task.bytesDownloaded / task.bytesTotal) * 100 : null,
size: task.bytesTotal, // size: task.bytesTotal,
availableSize: task.bytesDownloaded, // availableSize: task.bytesDownloaded,
pause: () => { // pause: () => {
task.pause(); // task.pause();
store.set(stateAtom!, (x) => ({ ...x, state: "PAUSED" })); // store.set(stateAtom!, (x) => ({ ...x, state: "PAUSED" }));
}, // },
resume: () => { // resume: () => {
task.resume(); // task.resume();
store.set(stateAtom!, (x) => ({ ...x, state: "DOWNLOADING" })); // store.set(stateAtom!, (x) => ({ ...x, state: "DOWNLOADING" }));
}, // },
remove: () => { // remove: () => {
task.stop(); // task.stop();
store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== task.id)); // store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== task.id));
}, // },
play: () => { // play: () => {
ToastAndroid.show("The file has not finished downloading", ToastAndroid.LONG); // ToastAndroid.show("The file has not finished downloading", ToastAndroid.LONG);
}, // },
retry: () => { // retry: () => {
const [newTask, path] = download( // const [newTask, path] = download(
{ // {
type: state.data.kind, // type: state.data.kind,
id: state.data.id, // id: state.data.id,
slug: state.data.slug, // slug: state.data.slug,
extension: state.info.extension, // extension: state.info.extension,
}, // },
getCurrentAccount()!, // getCurrentAccount()!,
); // );
setupDownloadTask({ ...state, path }, newTask, store, queryClient, stateAtom); // 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 // // 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. // // used during this launch of the app.
const update = updater(store, stateAtom); // const update = updater(store, stateAtom);
//
task // task
.begin(({ expectedBytes }) => update((x) => ({ ...x, size: expectedBytes }))) // .begin(({ expectedBytes }) => update((x) => ({ ...x, size: expectedBytes })))
.progress(({ bytesDownloaded, bytesTotal }) => { // .progress(({ bytesDownloaded, bytesTotal }) => {
update((x) => ({ // update((x) => ({
...x, // ...x,
progress: Math.round((bytesDownloaded / bytesTotal) * 100), // progress: Math.round((bytesDownloaded / bytesTotal) * 100),
size: bytesTotal, // size: bytesTotal,
availableSize: bytesDownloaded, // availableSize: bytesDownloaded,
status: "DOWNLOADING", // status: "DOWNLOADING",
})); // }));
}) // })
.done(() => { // .done(() => {
update((x) => ({ ...x, progress: 100, status: "DONE", play: playFn(state, queryClient) })); // update((x) => ({ ...x, progress: 100, status: "DONE", play: playFn(state, queryClient) }));
RNBackgroundDownloader.completeHandler(task.id); // RNBackgroundDownloader.completeHandler(task.id);
}) // })
.error(({ error }) => { // .error(({ error }) => {
update((x) => ({ ...x, status: "FAILED", error })); // update((x) => ({ ...x, status: "FAILED", error }));
console.error(`Error downloading ${state.data.slug}`, error); // console.error(`Error downloading ${state.data.slug}`, error);
ToastAndroid.show(`Error downloading ${state.data.slug}`, ToastAndroid.LONG); // ToastAndroid.show(`Error downloading ${state.data.slug}`, ToastAndroid.LONG);
}); // });
//
return { data: state.data, info: state.info, path: state.path, state: stateAtom }; // return { data: state.data, info: state.info, path: state.path, state: stateAtom };
}; // };
const updater = ( const updater = (
store: ReturnType<typeof useStore>, store: ReturnType<typeof useStore>,
@ -173,32 +170,32 @@ const updater = (
}; };
}; };
const download = ( // const download = (
{ // {
type, // type,
id, // id,
slug, // slug,
extension, // extension,
}: { slug: string; id: string; type: "episode" | "movie"; extension: string }, // }: { slug: string; id: string; type: "episode" | "movie"; extension: string },
account: Account, // account: Account,
) => { // ) => {
// TODO: support custom paths // // TODO: support custom paths
const path = `${RNBackgroundDownloader.directories.documents}/${slug}-${id}.${extension}`; // const path = `${RNBackgroundDownloader.directories.documents}/${slug}-${id}.${extension}`;
const task = RNBackgroundDownloader.download({ // const task = RNBackgroundDownloader.download({
id: id, // id: id,
// TODO: support variant qualities // // TODO: support variant qualities
url: `${account.apiUrl}/${type}/${slug}/direct`, // url: `${account.apiUrl}/${type}/${slug}/direct`,
destination: path, // destination: path,
headers: { // headers: {
Authorization: account.token.access_token, // Authorization: account.token.access_token,
}, // },
isNotificationVisible: true, // isNotificationVisible: true,
// TODO: Implement only wifi // // TODO: Implement only wifi
// network: Network.ALL, // // network: Network.ALL,
}); // });
console.log("Starting download", path); // console.log("Starting download", path);
return [task, path] as const; // return [task, path] as const;
}; // };
export const useDownloader = () => { export const useDownloader = () => {
const setDownloads = useSetAtom(downloadAtom); const setDownloads = useSetAtom(downloadAtom);
@ -206,30 +203,30 @@ export const useDownloader = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return async (type: "episode" | "movie", slug: string) => { return async (type: "episode" | "movie", slug: string) => {
try { // try {
const account = getCurrentAccount()!; // const account = getCurrentAccount()!;
const [data, info] = await Promise.all([ // const [data, info] = await Promise.all([
query(Player.query(type, slug), account), // query(Player.query(type, slug), account),
query(Player.infoQuery(type, slug), account), // query(Player.infoQuery(type, slug), account),
]); // ]);
//
if (store.get(downloadAtom).find((x) => x.data.id === data.id)) { // if (store.get(downloadAtom).find((x) => x.data.id === data.id)) {
ToastAndroid.show(`${slug} is already downloaded, skipping`, ToastAndroid.LONG); // ToastAndroid.show(`${slug} is already downloaded, skipping`, ToastAndroid.LONG);
return; // return;
} // }
//
const [task, path] = download( // const [task, path] = download(
{ type, slug, id: data.id, extension: info.extension }, // { type, slug, id: data.id, extension: info.extension },
account, // account,
); // );
setDownloads((x) => [ // setDownloads((x) => [
...x, // ...x,
setupDownloadTask({ data, info, path }, task, store, queryClient), // setupDownloadTask({ data, info, path }, task, store, queryClient),
]); // ]);
} catch (e) { // } catch (e) {
console.error("download error", e); // console.error("download error", e);
ToastAndroid.show(`Error downloading ${slug}`, ToastAndroid.LONG); // ToastAndroid.show(`Error downloading ${slug}`, ToastAndroid.LONG);
} // }
}; };
}; };
@ -248,61 +245,61 @@ const playFn =
}; };
export const DownloadProvider = ({ children }: { children: ReactNode }) => { export const DownloadProvider = ({ children }: { children: ReactNode }) => {
const store = useStore(); // const store = useStore();
const queryClient = useQueryClient(); // const queryClient = useQueryClient();
//
useEffect(() => { // useEffect(() => {
async function run() { // async function run() {
if (store.get(downloadAtom).length) return; // if (store.get(downloadAtom).length) return;
//
const tasks = await RNBackgroundDownloader.checkForExistingDownloads(); // const tasks = await RNBackgroundDownloader.checkForExistingDownloads();
const dls: { data: Episode | Movie; info: WatchInfo; path: string; state: State }[] = // const dls: { data: Episode | Movie; info: WatchInfo; path: string; state: State }[] =
JSON.parse(storage.getString("downloads") ?? "[]"); // JSON.parse(storage.getString("downloads") ?? "[]");
const downloads = dls.map((dl) => { // const downloads = dls.map((dl) => {
const t = tasks.find((x) => x.id === dl.data.id); // const t = tasks.find((x) => x.id === dl.data.id);
if (t) return setupDownloadTask(dl, t, store, queryClient); // if (t) return setupDownloadTask(dl, t, store, queryClient);
//
const stateAtom = atom({ // const stateAtom = atom({
status: dl.state.status === "DONE" ? "DONE" : "FAILED", // status: dl.state.status === "DONE" ? "DONE" : "FAILED",
progress: dl.state.progress, // progress: dl.state.progress,
size: dl.state.size, // size: dl.state.size,
availableSize: dl.state.availableSize, // availableSize: dl.state.availableSize,
pause: null, // pause: null,
resume: null, // resume: null,
play: playFn(dl, queryClient), // play: playFn(dl, queryClient),
remove: () => { // remove: () => {
deleteAsync(dl.path); // deleteAsync(dl.path);
store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== dl.data.id)); // store.set(downloadAtom, (x) => x.filter((y) => y.data.id !== dl.data.id));
}, // },
retry: () => { // retry: () => {
const [newTask, path] = download( // const [newTask, path] = download(
{ // {
type: dl.data.kind, // type: dl.data.kind,
id: dl.data.id, // id: dl.data.id,
slug: dl.data.slug, // slug: dl.data.slug,
extension: dl.info.extension, // extension: dl.info.extension,
}, // },
getCurrentAccount()!, // getCurrentAccount()!,
); // );
setupDownloadTask({ ...dl, path }, newTask, store, queryClient, stateAtom); // setupDownloadTask({ ...dl, path }, newTask, store, queryClient, stateAtom);
}, // },
} as State); // } as State);
return { // return {
data: z.union([EpisodeP, MovieP]).parse(dl.data), // data: z.union([EpisodeP, MovieP]).parse(dl.data),
info: WatchInfoP.parse(dl.info), // info: WatchInfoP.parse(dl.info),
path: dl.path, // path: dl.path,
state: stateAtom, // state: stateAtom,
}; // };
}); // });
store.set(downloadAtom, downloads); // store.set(downloadAtom, downloads);
//
for (const t of tasks) { // for (const t of tasks) {
if (!downloads.find((x) => x.data.id === t.id)) t.stop(); // if (!downloads.find((x) => x.data.id === t.id)) t.stop();
} // }
RNBackgroundDownloader.ensureDownloadsAreRunning(); // RNBackgroundDownloader.ensureDownloadsAreRunning();
} // }
run(); // run();
}, [store, queryClient]); // }, [store, queryClient]);
return children; return children;
}; };