diff --git a/app.json b/app.json
index f173d0d3..3fef0c7c 100644
--- a/app.json
+++ b/app.json
@@ -19,7 +19,7 @@
"infoPlist": {
"NSCameraUsageDescription": "The app needs access to your camera to scan barcodes.",
"NSMicrophoneUsageDescription": "The app needs access to your microphone.",
- "UIBackgroundModes": ["audio"],
+ "UIBackgroundModes": ["audio", "fetch"],
"NSLocalNetworkUsageDescription": "The app needs access to your local network to connect to your Jellyfin server.",
"NSAppTransportSecurity": {
"NSAllowsArbitraryLoads": true
diff --git a/app/(auth)/(tabs)/(home)/settings.tsx b/app/(auth)/(tabs)/(home)/settings.tsx
index 86fb08b5..a75ca44c 100644
--- a/app/(auth)/(tabs)/(home)/settings.tsx
+++ b/app/(auth)/(tabs)/(home)/settings.tsx
@@ -2,7 +2,10 @@ import { Button } from "@/components/Button";
import { Text } from "@/components/common/Text";
import { ListItem } from "@/components/ListItem";
import { SettingToggles } from "@/components/settings/SettingToggles";
-import { useDownload } from "@/providers/DownloadProvider";
+import {
+ registerBackgroundFetchAsync,
+ useDownload,
+} from "@/providers/DownloadProvider";
import { apiAtom, useJellyfin, userAtom } from "@/providers/JellyfinProvider";
import { clearLogs, readFromLog } from "@/utils/log";
import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api";
@@ -66,6 +69,13 @@ export default function settings() {
}}
>
+ {/* */}
Information
diff --git a/app/_layout.tsx b/app/_layout.tsx
index 1ed76bf2..abbfbd9c 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -1,28 +1,27 @@
-import { FullScreenVideoPlayer } from "@/components/FullScreenVideoPlayer";
+import { DownloadProvider } from "@/providers/DownloadProvider";
import { JellyfinProvider } from "@/providers/JellyfinProvider";
import { JobQueueProvider } from "@/providers/JobQueueProvider";
import { PlaybackProvider } from "@/providers/PlaybackProvider";
+import { orientationAtom } from "@/utils/atoms/orientation";
import { useSettings } from "@/utils/atoms/settings";
import { ActionSheetProvider } from "@expo/react-native-action-sheet";
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
+import { checkForExistingDownloads } from "@kesha-antonov/react-native-background-downloader";
import { DarkTheme, ThemeProvider } from "@react-navigation/native";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useFonts } from "expo-font";
import { useKeepAwake } from "expo-keep-awake";
-import { Stack, useRouter } from "expo-router";
+import * as Linking from "expo-linking";
+import { Stack } from "expo-router";
import * as ScreenOrientation from "expo-screen-orientation";
import * as SplashScreen from "expo-splash-screen";
import { StatusBar } from "expo-status-bar";
import { Provider as JotaiProvider, useAtom } from "jotai";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useRef } from "react";
+import { AppState } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import "react-native-reanimated";
-import * as Linking from "expo-linking";
-import { orientationAtom } from "@/utils/atoms/orientation";
import { Toaster } from "sonner-native";
-import { checkForExistingDownloads } from "@kesha-antonov/react-native-background-downloader";
-import { AppState } from "react-native";
-import { DownloadProvider } from "@/providers/DownloadProvider";
SplashScreen.preventAutoHideAsync();
diff --git a/bun.lockb b/bun.lockb
index 1f870f83..0438c6be 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/components/settings/SettingToggles.tsx b/components/settings/SettingToggles.tsx
index b0b0b557..7646a7b6 100644
--- a/components/settings/SettingToggles.tsx
+++ b/components/settings/SettingToggles.tsx
@@ -36,8 +36,6 @@ export const SettingToggles: React.FC = ({ ...props }) => {
const [marlinUrl, setMarlinUrl] = useState("");
const [optimizedVersionsServerUrl, setOptimizedVersionsServerUrl] =
useState("");
- const [optimizedVersionsAuthHeader, setOptimizedVersionsAuthHeader] =
- useState("");
const queryClient = useQueryClient();
@@ -571,65 +569,6 @@ export const SettingToggles: React.FC = ({ ...props }) => {
)}
-
-
-
-
- Optimized versions auth header
-
-
- The auth header for the optimized versions server.
-
-
-
- setOptimizedVersionsAuthHeader(text)}
- className="w-full"
- />
-
-
-
- {settings.optimizedVersionsAuthHeader && (
-
-
- {settings.optimizedVersionsAuthHeader}
-
-
- )}
-
-
diff --git a/package.json b/package.json
index 8a7f82da..3d1a93f9 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"@types/uuid": "^10.0.0",
"axios": "^1.7.7",
"expo": "~51.0.34",
+ "expo-background-fetch": "~12.0.1",
"expo-blur": "~13.0.2",
"expo-build-properties": "~0.12.5",
"expo-constants": "~16.0.2",
@@ -50,6 +51,7 @@
"expo-splash-screen": "~0.27.6",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
+ "expo-task-manager": "~11.8.2",
"expo-updates": "~0.25.25",
"expo-web-browser": "~13.0.3",
"ffmpeg-kit-react-native": "^6.0.2",
diff --git a/providers/DownloadProvider.tsx b/providers/DownloadProvider.tsx
index 67ca0aa8..10d94da3 100644
--- a/providers/DownloadProvider.tsx
+++ b/providers/DownloadProvider.tsx
@@ -26,6 +26,8 @@ import React, {
} from "react";
import { toast } from "sonner-native";
import { apiAtom } from "./JellyfinProvider";
+import * as BackgroundFetch from "expo-background-fetch";
+import * as TaskManager from "expo-task-manager";
export type ProcessItem = {
id: string;
@@ -42,8 +44,33 @@ export type ProcessItem = {
| "queued";
};
+export const BACKGROUND_FETCH_TASK = "background-fetch";
+
+TaskManager.defineTask(BACKGROUND_FETCH_TASK, async () => {
+ const now = Date.now();
+
+ console.log(
+ `Got background fetch call at date: ${new Date(now).toISOString()}`
+ );
+
+ // Be sure to return the successful result type!
+ return BackgroundFetch.BackgroundFetchResult.NewData;
+});
+
const STORAGE_KEY = "runningProcesses";
+export async function registerBackgroundFetchAsync() {
+ return BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, {
+ minimumInterval: 60 * 15, // 1 minutes
+ stopOnTerminate: false, // android only,
+ startOnBoot: true, // android only
+ });
+}
+
+export async function unregisterBackgroundFetchAsync() {
+ return BackgroundFetch.unregisterTaskAsync(BACKGROUND_FETCH_TASK);
+}
+
const DownloadContext = createContext | null>(null);
@@ -66,6 +93,9 @@ function useDownloadProvider() {
});
useEffect(() => {
+ // Check background task status
+ checkStatusAsync();
+
// Load initial processes state from AsyncStorage
const loadInitialProcesses = async () => {
const storedProcesses = await readProcesses();
@@ -74,6 +104,40 @@ function useDownloadProvider() {
loadInitialProcesses();
}, []);
+ /********************
+ * Background task
+ *******************/
+ const [isRegistered, setIsRegistered] = useState(false);
+ const [status, setStatus] =
+ useState(null);
+
+ const checkStatusAsync = async () => {
+ const status = await BackgroundFetch.getStatusAsync();
+ const isRegistered = await TaskManager.isTaskRegisteredAsync(
+ BACKGROUND_FETCH_TASK
+ );
+ setStatus(status);
+ setIsRegistered(isRegistered);
+
+ console.log("Background fetch status:", status);
+ console.log("Background fetch task registered:", isRegistered);
+ };
+
+ const toggleFetchTask = async () => {
+ if (isRegistered) {
+ console.log("Unregistering background fetch task");
+ await unregisterBackgroundFetchAsync();
+ } else {
+ console.log("Registering background fetch task");
+ await registerBackgroundFetchAsync();
+ }
+
+ checkStatusAsync();
+ };
+ /**********************
+ **********************
+ *********************/
+
const clearProcesses = useCallback(async () => {
await AsyncStorage.removeItem(STORAGE_KEY);
setProcesses([]);
@@ -129,7 +193,7 @@ function useDownloadProvider() {
})
.begin(() => {
toast.info(`Download started for ${process.item.Name}`);
- updateProcess(process.id, { state: "downloading" });
+ updateProcess(process.id, { state: "downloading", progress: 0 });
})
.progress((data) => {
const percent = (data.bytesDownloaded / data.bytesTotal) * 100;
diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts
index b5e687a1..bf03502d 100644
--- a/utils/atoms/settings.ts
+++ b/utils/atoms/settings.ts
@@ -73,7 +73,6 @@ export type Settings = {
forwardSkipTime: number;
rewindSkipTime: number;
optimizedVersionsServerUrl?: string | null;
- optimizedVersionsAuthHeader?: string | null;
downloadMethod?: "optimized" | "remux";
};
/**
@@ -110,7 +109,6 @@ const loadSettings = async (): Promise => {
forwardSkipTime: 30,
rewindSkipTime: 10,
optimizedVersionsServerUrl: null,
- optimizedVersionsAuthHeader: null,
downloadMethod: "remux",
};