a little something here...
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"platforms": ["ios", "tvos", "android", "web"],
|
||||
"ios": {
|
||||
"modules": ["ExpoBlueskySharedPrefsModule", "ExpoBlueskyReferrerModule"]
|
||||
"modules": ["ExpoBlueskySharedPrefsModule", "ExpoBlueskyReferrerModule", "ExpoHLSDownloadModule"]
|
||||
},
|
||||
"android": {
|
||||
"modules": [
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// DownloadManager.swift
|
||||
// ExpoBlueskySwissArmy
|
||||
//
|
||||
// Created by Hailey on 7/29/24.
|
||||
//
|
||||
|
||||
class DownloadManager {
|
||||
static let shared = DownloadManager()
|
||||
|
||||
private let downloads: NSMapTable<NSString, HLSDownload> = NSMapTable(keyOptions: .weakMemory, valueOptions: .weakMemory)
|
||||
|
||||
func add(_ hlsDownload: HLSDownload) {
|
||||
let key = hlsDownload.sourceUrl.absoluteString as NSString
|
||||
downloads.setObject(hlsDownload, forKey: key)
|
||||
}
|
||||
|
||||
func remove(string: String) {
|
||||
let key = string as NSString
|
||||
downloads.removeObject(forKey: key)
|
||||
}
|
||||
|
||||
func remove(hlsDownload: HLSDownload) {
|
||||
let key = hlsDownload.sourceUrl.absoluteString as NSString
|
||||
downloads.setObject(hlsDownload, forKey: key)
|
||||
}
|
||||
|
||||
func getForSource(url: URL) -> HLSDownload? {
|
||||
let key = url.absoluteString as NSString
|
||||
return downloads.object(forKey: key)
|
||||
}
|
||||
|
||||
func getForSource(string: String) -> HLSDownload? {
|
||||
let key = string as NSString
|
||||
return downloads.object(forKey: key)
|
||||
}
|
||||
|
||||
func cancel(url: URL) {
|
||||
let download = self.getForSource(url: url)
|
||||
download?.cancel()
|
||||
}
|
||||
|
||||
func cancelAll() {
|
||||
let downloadsEnumerator = downloads.objectEnumerator()
|
||||
while let download = downloadsEnumerator?.nextObject() {
|
||||
let download = download as? HLSDownload
|
||||
download?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// ExpoHLSDownloadModule.swift
|
||||
// DoubleConversion
|
||||
//
|
||||
// Created by Hailey on 7/29/24.
|
||||
//
|
||||
|
||||
import ExpoModulesCore
|
||||
|
||||
class ExpoHLSDownloadModule: Module {
|
||||
public func definition() -> ModuleDefinition {
|
||||
Name("ExpoHLSDownloadModule")
|
||||
|
||||
AsyncFunction("downloadAsync") { (sourceUrl: URL, progressCb: JavaScriptFunction<Void>, promise: Promise) in
|
||||
let hlsDownload = HLSDownload(sourceUrl: sourceUrl)
|
||||
let downloadTask = Task {
|
||||
let outUrl = await hlsDownload.download { (progress: Float) in
|
||||
try? progressCb.call(progress)
|
||||
}
|
||||
promise.resolve(outUrl)
|
||||
}
|
||||
}
|
||||
|
||||
AsyncFunction("cancelDownloadAsync") { (sourceUrl: URL) in
|
||||
DownloadManager.shared.cancel(url: sourceUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// HLSDownload.swift
|
||||
// ExpoBlueskySwissArmy
|
||||
//
|
||||
// Created by Hailey on 7/29/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVKit
|
||||
|
||||
class HLSDownload {
|
||||
private let avAsset: AVAsset
|
||||
private var exportSession: AVAssetExportSession?
|
||||
let sourceUrl: URL
|
||||
|
||||
init (sourceUrl: URL) {
|
||||
self.sourceUrl = sourceUrl
|
||||
self.avAsset = AVAsset(url: sourceUrl)
|
||||
}
|
||||
|
||||
func download(progress: @escaping(Float) -> Void) async -> URL? {
|
||||
guard let exportSession = AVAssetExportSession(asset: self.avAsset, presetName: AVAssetExportPresetHighestQuality) else {
|
||||
// @TODO return an error for the user here
|
||||
print("Export session error")
|
||||
return nil
|
||||
}
|
||||
|
||||
let outDir = FileManager().urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
||||
guard let outUrl = URL(string: "\(outDir.absoluteString)\(ProcessInfo.processInfo.globallyUniqueString).mp4") else {
|
||||
print("oops failed to make a url")
|
||||
return nil
|
||||
}
|
||||
|
||||
print(outUrl.absoluteString)
|
||||
|
||||
exportSession.outputFileType = AVFileType.mp4
|
||||
exportSession.outputURL = outUrl
|
||||
|
||||
let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (_: Timer) in
|
||||
// Do something here
|
||||
}
|
||||
|
||||
self.exportSession = exportSession
|
||||
await exportSession.export()
|
||||
|
||||
// Update with final progress
|
||||
progress(1)
|
||||
// Stop calling the callback
|
||||
timer.invalidate()
|
||||
|
||||
return outUrl
|
||||
}
|
||||
|
||||
func progress() -> Float {
|
||||
guard let exportSession = self.exportSession else {
|
||||
return 0
|
||||
}
|
||||
return exportSession.progress
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
// Cancel the download
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import {requireNativeModule} from 'expo'
|
||||
|
||||
const NativeModule = requireNativeModule('ExpoHLSDownload')
|
||||
|
||||
export async function downloadAsync(sourceUrl: string): Promise<string> {
|
||||
return NativeModule.downloadAsync(sourceUrl)
|
||||
}
|
||||
|
||||
export async function cancelAsync(sourceUrl: string): Promise<void> {
|
||||
return NativeModule.cancelAsync(sourceUrl)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import {NotImplementedError} from '../NotImplemented'
|
||||
|
||||
export function downloadAsync(sourceUrl: string): Promise<string> {
|
||||
throw new NotImplementedError({sourceUrl})
|
||||
}
|
||||
|
||||
export function cancelAsync(sourceUrl: string): Promise<void> {
|
||||
throw new NotImplementedError({sourceUrl})
|
||||
}
|
||||
Reference in New Issue
Block a user