mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
fix: downloads working on android
This commit is contained in:
@@ -101,7 +101,7 @@ export default function Index() {
|
||||
const downloadsDir = FileSystem.documentDirectory + "downloads/";
|
||||
await FileSystem.deleteAsync(downloadsDir + id + ".json");
|
||||
await FileSystem.deleteAsync(downloadsDir + id);
|
||||
refetchDownloadedFiles()
|
||||
refetchDownloadedFiles();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -11,12 +11,12 @@ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.ab
|
||||
def kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.25'
|
||||
|
||||
apply from: expoModulesCorePlugin
|
||||
|
||||
applyKotlinExpoModulesCorePlugin()
|
||||
useCoreDependencies()
|
||||
useExpoPublishing()
|
||||
|
||||
def useManagedAndroidSdkVersions = false
|
||||
|
||||
if (useManagedAndroidSdkVersions) {
|
||||
useDefaultAndroidSdkVersions()
|
||||
} else {
|
||||
@@ -29,6 +29,7 @@ if (useManagedAndroidSdkVersions) {
|
||||
classpath "com.android.tools.build:gradle:7.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
project.android {
|
||||
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
||||
defaultConfig {
|
||||
@@ -42,39 +43,42 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
|
||||
// Media3 dependencies
|
||||
def media3Version = "1.2.0"
|
||||
implementation "androidx.media3:media3-exoplayer:$media3Version"
|
||||
implementation "androidx.media3:media3-datasource:$media3Version"
|
||||
implementation "androidx.media3:media3-common:$media3Version"
|
||||
implementation "androidx.media3:media3-database:$media3Version"
|
||||
implementation "androidx.media3:media3-decoder:$media3Version"
|
||||
implementation "androidx.media3:media3-ui:$media3Version"
|
||||
|
||||
// Coroutines for background processing
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
|
||||
def media3_version = "1.2.1"
|
||||
implementation "androidx.media3:media3-exoplayer:$media3_version"
|
||||
implementation "androidx.media3:media3-exoplayer-hls:$media3_version"
|
||||
implementation "androidx.media3:media3-database:$media3_version"
|
||||
implementation "androidx.media3:media3-datasource:$media3_version"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "expo.modules.hlsdownloader"
|
||||
compileSdkVersion 34
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
versionCode 1
|
||||
versionName "0.1.0"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += ["-Xshow-kotlin-compiler-errors"]
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
o/classes
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
o/classes
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
o/classes
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
o/classes
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
o/classes
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +1,2 @@
|
||||
2
|
||||
1
|
||||
0
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,6 +17,7 @@ import java.io.File
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class HlsDownloaderModule : Module() {
|
||||
private val TAG = "HlsDownloaderModule"
|
||||
private var activeDownloads = mutableMapOf<String, DownloadMetadata>()
|
||||
private lateinit var downloadManager: DownloadManager
|
||||
private lateinit var downloadCache: SimpleCache
|
||||
@@ -38,6 +39,7 @@ class HlsDownloaderModule : Module() {
|
||||
)
|
||||
|
||||
OnCreate {
|
||||
android.util.Log.d(TAG, "Creating HLS Downloader module")
|
||||
val context = appContext.reactContext as Context
|
||||
val cacheDir = File(context.getExternalFilesDir(null), "downloads")
|
||||
if (!cacheDir.exists()) {
|
||||
@@ -65,10 +67,13 @@ class HlsDownloaderModule : Module() {
|
||||
download: Download,
|
||||
finalException: Exception?
|
||||
) {
|
||||
android.util.Log.d(TAG, "Download changed - State: ${download.state}, Progress: ${download.percentDownloaded}%")
|
||||
|
||||
val metadata = activeDownloads[download.request.id]
|
||||
if (metadata != null) {
|
||||
when (download.state) {
|
||||
Download.STATE_COMPLETED -> {
|
||||
android.util.Log.d(TAG, "Download completed for ${metadata.providedId}")
|
||||
sendEvent(
|
||||
"onComplete",
|
||||
mapOf(
|
||||
@@ -83,6 +88,7 @@ class HlsDownloaderModule : Module() {
|
||||
saveMetadataFile(metadata)
|
||||
}
|
||||
Download.STATE_FAILED -> {
|
||||
android.util.Log.e(TAG, "Download failed for ${metadata.providedId}", finalException)
|
||||
sendEvent(
|
||||
"onError",
|
||||
mapOf(
|
||||
@@ -100,6 +106,8 @@ class HlsDownloaderModule : Module() {
|
||||
download.bytesDownloaded.toFloat() / download.contentLength
|
||||
} else 0f
|
||||
|
||||
android.util.Log.d(TAG, "Download progress for ${metadata.providedId}: $progress")
|
||||
|
||||
sendEvent(
|
||||
"onProgress",
|
||||
mapOf(
|
||||
@@ -108,7 +116,10 @@ class HlsDownloaderModule : Module() {
|
||||
"state" to when (download.state) {
|
||||
Download.STATE_DOWNLOADING -> "DOWNLOADING"
|
||||
Download.STATE_QUEUED -> "PENDING"
|
||||
else -> "DOWNLOADING"
|
||||
Download.STATE_STOPPED -> "STOPPED"
|
||||
Download.STATE_REMOVING -> "REMOVING"
|
||||
Download.STATE_RESTARTING -> "RESTARTING"
|
||||
else -> "UNKNOWN"
|
||||
},
|
||||
"metadata" to metadata.metadata,
|
||||
"startTime" to metadata.startTime,
|
||||
@@ -117,9 +128,24 @@ class HlsDownloaderModule : Module() {
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
android.util.Log.w(TAG, "Received download update for unknown download id: ${download.request.id}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDownloadsPausedChanged(
|
||||
downloadManager: DownloadManager,
|
||||
downloadsPaused: Boolean
|
||||
) {
|
||||
android.util.Log.d(TAG, "Downloads paused changed: $downloadsPaused")
|
||||
}
|
||||
|
||||
override fun onIdle(downloadManager: DownloadManager) {
|
||||
android.util.Log.d(TAG, "Download manager is idle")
|
||||
}
|
||||
})
|
||||
|
||||
downloadManager.resumeDownloads()
|
||||
}
|
||||
|
||||
Function("getActiveDownloads") {
|
||||
@@ -135,6 +161,7 @@ class HlsDownloaderModule : Module() {
|
||||
}
|
||||
|
||||
Function("downloadHLSAsset") { providedId: String, url: String, metadata: Map<String, Any>? ->
|
||||
android.util.Log.d(TAG, "Starting download for $providedId from $url")
|
||||
val startTime = System.currentTimeMillis()
|
||||
val context = appContext.reactContext as Context
|
||||
|
||||
@@ -158,10 +185,11 @@ class HlsDownloaderModule : Module() {
|
||||
providedId,
|
||||
Uri.parse(url)
|
||||
)
|
||||
.setCustomCacheKey(providedId)
|
||||
.setStreamKeys(emptyList())
|
||||
.build()
|
||||
|
||||
downloadManager.addDownload(downloadRequest)
|
||||
android.util.Log.d(TAG, "Download request added for $providedId")
|
||||
|
||||
activeDownloads[providedId] = DownloadMetadata(
|
||||
providedId = providedId,
|
||||
@@ -181,6 +209,7 @@ class HlsDownloaderModule : Module() {
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e(TAG, "Error starting download for $providedId", e)
|
||||
sendEvent(
|
||||
"onError",
|
||||
mapOf(
|
||||
@@ -224,4 +253,5 @@ class HlsDownloaderModule : Module() {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
patches/@expo+react-native-action-sheet+4.1.0.patch
Normal file
18
patches/@expo+react-native-action-sheet+4.1.0.patch
Normal file
File diff suppressed because one or more lines are too long
@@ -30,7 +30,7 @@ import {
|
||||
import { toast } from "sonner-native";
|
||||
import { apiAtom, userAtom } from "./JellyfinProvider";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import { AppState, AppStateStatus } from "react-native";
|
||||
import { AppState, AppStateStatus, Platform } from "react-native";
|
||||
|
||||
type DownloadOptionsData = {
|
||||
selectedAudioStream: number;
|
||||
@@ -69,34 +69,55 @@ export type DownloadedFileInfo = {
|
||||
};
|
||||
|
||||
const getDownloadedFiles = async (): Promise<DownloadedFileInfo[]> => {
|
||||
const downloaded: DownloadedFileInfo[] = [];
|
||||
console.log("getDownloadedFiles ~");
|
||||
|
||||
const downloadsDir = FileSystem.documentDirectory + "downloads/";
|
||||
const dirInfo = await FileSystem.getInfoAsync(downloadsDir);
|
||||
const files = await FileSystem.readDirectoryAsync(
|
||||
FileSystem.documentDirectory!
|
||||
);
|
||||
console.log(files);
|
||||
|
||||
if (!dirInfo.exists) return [];
|
||||
return [];
|
||||
|
||||
const files = await FileSystem.readDirectoryAsync(downloadsDir);
|
||||
// const downloaded: DownloadedFileInfo[] = [];
|
||||
|
||||
for (let file of files) {
|
||||
const fileInfo = await FileSystem.getInfoAsync(downloadsDir + file);
|
||||
if (fileInfo.isDirectory) continue;
|
||||
if (!file.endsWith(".json")) continue;
|
||||
// const downloadsDir = Platform.select({
|
||||
// ios: FileSystem.documentDirectory + "downloads/",
|
||||
// android: FileSystem.cacheDirectory + "../files/downloads/",
|
||||
// });
|
||||
|
||||
const fileContent = await FileSystem.readAsStringAsync(downloadsDir + file);
|
||||
// if (!downloadsDir) throw new Error("Downloads directory not found");
|
||||
|
||||
// Check that fileContent is actually DownloadMetadata
|
||||
if (!fileContent) continue;
|
||||
if (!fileContent.includes("mediaSource")) continue;
|
||||
if (!fileContent.includes("item")) continue;
|
||||
// const dirInfo = await FileSystem.getInfoAsync(downloadsDir);
|
||||
|
||||
downloaded.push({
|
||||
id: file.replace(".json", ""),
|
||||
path: downloadsDir + file.replace(".json", ""),
|
||||
metadata: JSON.parse(fileContent) as DownloadMetadata,
|
||||
});
|
||||
}
|
||||
return downloaded;
|
||||
// if (!dirInfo.exists) {
|
||||
// console.warn("Downloads directory does not exist");
|
||||
// return [];
|
||||
// }
|
||||
|
||||
// const files = await FileSystem.readDirectoryAsync(downloadsDir);
|
||||
|
||||
// console.log("getDownloadedFiles ~", files.length);
|
||||
|
||||
// for (let file of files) {
|
||||
// console.log(file);
|
||||
// const fileInfo = await FileSystem.getInfoAsync(downloadsDir + file);
|
||||
// if (fileInfo.isDirectory) continue;
|
||||
// if (!file.endsWith(".json")) continue;
|
||||
|
||||
// const fileContent = await FileSystem.readAsStringAsync(downloadsDir + file);
|
||||
|
||||
// // Check that fileContent is actually DownloadMetadata
|
||||
// if (!fileContent) continue;
|
||||
// if (!fileContent.includes("mediaSource")) continue;
|
||||
// if (!fileContent.includes("item")) continue;
|
||||
|
||||
// downloaded.push({
|
||||
// id: file.replace(".json", ""),
|
||||
// path: downloadsDir + file.replace(".json", ""),
|
||||
// metadata: JSON.parse(fileContent) as DownloadMetadata,
|
||||
// });
|
||||
// }
|
||||
// return downloaded;
|
||||
};
|
||||
|
||||
const getDownloadedFile = async (id: string) => {
|
||||
@@ -167,6 +188,12 @@ export const NativeDownloadProvider: React.FC<{
|
||||
|
||||
useEffect(() => {
|
||||
const progressListener = addProgressListener((download) => {
|
||||
console.log("p ~", {
|
||||
id: download.id,
|
||||
progress: download.progress,
|
||||
state: download.state,
|
||||
taskId: download.taskId,
|
||||
});
|
||||
if (!download.metadata) throw new Error("No metadata found in download");
|
||||
|
||||
setDownloads((prev) => ({
|
||||
|
||||
Reference in New Issue
Block a user