This commit is contained in:
Fredrik Burmester
2025-02-17 08:14:27 +01:00
parent e4d2de2f8a
commit b8bebfb272
8 changed files with 82 additions and 136 deletions

View File

@@ -28,7 +28,6 @@ function downloadHLSAsset(
/**
* Checks for existing downloads.
* Returns an array of downloads with additional fields:
* id, progress, bytesDownloaded, bytesTotal, and state.
*/
async function checkForExistingDownloads(): Promise<DownloadInfo[]> {
return HlsDownloaderModule.checkForExistingDownloads();
@@ -99,91 +98,9 @@ function useDownloadError(): string | null {
return error;
}
/**
* Moves a file from a temporary URI to a permanent location in the document directory.
* @param tempFileUri The temporary file URI returned by the native module.
* @param newFilename The desired filename (with extension) for the persisted file.
* @returns A promise that resolves with the new file URI.
*/
async function persistDownloadedFile(
tempFileUri: string,
newFilename: string
): Promise<string> {
const newUri = FileSystem.documentDirectory + newFilename;
try {
await FileSystem.moveAsync({
from: tempFileUri,
to: newUri,
});
console.log("File persisted to:", newUri);
return newUri;
} catch (error) {
console.error("Error moving file:", error);
throw error;
}
}
/**
* React hook that returns the completion location of the download.
* If a destinationFileName is provided, the hook will move the downloaded file
* to the document directory under that name, then return the new URI.
*
* @param destinationFileName Optional filename (with extension) to persist the file.
* @returns The final file URI or null if not completed.
*/
function useDownloadComplete(destinationFileName?: string): string | null {
const [location, setLocation] = useState<string | null>(null);
useEffect(() => {
console.log("Setting up download complete listener");
const subscription = addCompleteListener(
async (event: OnCompleteEventPayload) => {
console.log("Download complete event received:", event);
console.log("Original download location:", event.location);
if (destinationFileName) {
console.log(
"Attempting to persist file with name:",
destinationFileName
);
try {
const newLocation = await persistDownloadedFile(
event.location,
destinationFileName
);
console.log("File successfully persisted to:", newLocation);
setLocation(newLocation);
} catch (error) {
console.error("Failed to persist file:", error);
console.error("Error details:", {
originalLocation: event.location,
destinationFileName,
error: error instanceof Error ? error.message : error,
});
}
} else {
console.log(
"No destination filename provided, using original location"
);
setLocation(event.location);
}
}
);
return () => {
console.log("Cleaning up download complete listener");
subscription.remove();
};
}, [destinationFileName]);
return location;
}
export {
downloadHLSAsset,
checkForExistingDownloads,
useDownloadComplete,
useDownloadError,
useDownloadProgress,
addCompleteListener,

View File

@@ -38,7 +38,7 @@ public class HlsDownloaderModule: Module {
url: assetURL,
options: [
"AVURLAssetOutOfBandMIMETypeKey": "application/x-mpegURL",
"AVURLAssetHTTPHeaderFieldsKey": ["User-Agent": "YourAppNameHere/1.0"],
"AVURLAssetHTTPHeaderFieldsKey": ["User-Agent": "Streamyfin/1.0"],
"AVURLAssetAllowsCellularAccessKey": true,
])
@@ -134,8 +134,8 @@ public class HlsDownloaderModule: Module {
downloads.append([
"id": delegate.providedId.isEmpty ? String(id) : delegate.providedId,
"progress": progress,
"bytesDownloaded": downloaded,
"bytesTotal": total,
"secondsDownloaded": downloaded,
"secondsTotal": total,
"state": self.mappedState(for: task),
"metadata": metadata,
"startTime": startTime,
@@ -243,8 +243,8 @@ class HLSDownloadDelegate: NSObject, AVAssetDownloadDelegate {
[
"id": providedId,
"progress": progress,
"bytesDownloaded": downloaded,
"bytesTotal": total,
"secondsDownloaded": downloaded,
"secondsTotal": total,
"state": progress >= 1.0 ? "DONE" : "DOWNLOADING",
"metadata": metadata,
"startTime": startTime,
@@ -263,6 +263,26 @@ class HLSDownloadDelegate: NSObject, AVAssetDownloadDelegate {
let newLocation = try module.persistDownloadedFolder(
originalLocation: location, folderName: folderName)
// Calculate download size
let fileManager = FileManager.default
let enumerator = fileManager.enumerator(
at: newLocation,
includingPropertiesForKeys: [.totalFileAllocatedSizeKey],
options: [.skipsHiddenFiles],
errorHandler: nil)!
var totalSize: Int64 = 0
while let filePath = enumerator.nextObject() as? URL {
do {
let resourceValues = try filePath.resourceValues(forKeys: [.totalFileAllocatedSizeKey])
if let size = resourceValues.totalFileAllocatedSize {
totalSize += Int64(size)
}
} catch {
print("Error calculating size: \(error)")
}
}
if !metadata.isEmpty {
let metadataLocation = newLocation.deletingLastPathComponent().appendingPathComponent(
"\(providedId).json")
@@ -278,6 +298,7 @@ class HLSDownloadDelegate: NSObject, AVAssetDownloadDelegate {
"state": "DONE",
"metadata": metadata,
"startTime": startTime,
"bytesDownloaded": totalSize,
])
} catch {
module?.sendEvent(

View File

@@ -21,13 +21,13 @@ export type BaseEventPayload = {
id: string;
state: DownloadState;
metadata: DownloadMetadata;
startTime?: number;
};
export type OnProgressEventPayload = BaseEventPayload & {
progress: number;
bytesDownloaded: number;
bytesTotal: number;
startTime?: number;
secondsDownloaded: number;
secondsTotal: number;
};
export type OnErrorEventPayload = BaseEventPayload & {
@@ -38,6 +38,7 @@ export type OnErrorEventPayload = BaseEventPayload & {
export type OnCompleteEventPayload = BaseEventPayload & {
location: string;
bytesDownloaded?: number;
};
export type HlsDownloaderModuleEvents = {
@@ -52,8 +53,8 @@ export interface DownloadInfo {
startTime?: number;
progress: number;
state: DownloadState;
bytesDownloaded?: number;
bytesTotal?: number;
secondsDownloaded?: number;
secondsTotal?: number;
location?: string;
error?: string;
metadata: DownloadMetadata;