Revert "[Video] Download videos" (#4945)

This commit is contained in:
Hailey 2024-08-15 16:29:16 -07:00 committed by GitHub
parent b6e515c664
commit a5af24b53b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 3 additions and 747 deletions

View file

@ -1,35 +0,0 @@
package expo.modules.blueskyswissarmy.hlsdownload
import android.net.Uri
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class ExpoHLSDownloadModule : Module() {
override fun definition() =
ModuleDefinition {
Name("ExpoHLSDownload")
Function("isAvailable") {
return@Function true
}
View(HLSDownloadView::class) {
Events(
arrayOf(
"onStart",
"onError",
"onProgress",
"onSuccess",
),
)
Prop("downloaderUrl") { view: HLSDownloadView, downloaderUrl: Uri ->
view.downloaderUrl = downloaderUrl
}
AsyncFunction("startDownloadAsync") { view: HLSDownloadView, sourceUrl: Uri ->
view.startDownload(sourceUrl)
}
}
}
}

View file

@ -1,141 +0,0 @@
package expo.modules.blueskyswissarmy.hlsdownload
import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.util.Base64
import android.util.Log
import android.webkit.DownloadListener
import android.webkit.JavascriptInterface
import android.webkit.WebView
import expo.modules.kotlin.AppContext
import expo.modules.kotlin.viewevent.EventDispatcher
import expo.modules.kotlin.viewevent.ViewEventCallback
import expo.modules.kotlin.views.ExpoView
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.net.URI
import java.util.UUID
class HLSDownloadView(
context: Context,
appContext: AppContext,
) : ExpoView(context, appContext),
DownloadListener {
private val webView = WebView(context)
var downloaderUrl: Uri? = null
private val onStart by EventDispatcher()
private val onError by EventDispatcher()
private val onProgress by EventDispatcher()
private val onSuccess by EventDispatcher()
init {
this.setupWebView()
this.addView(this.webView, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
}
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView() {
val webSettings = this.webView.settings
webSettings.javaScriptEnabled = true
webSettings.domStorageEnabled = true
webView.setDownloadListener(this)
webView.addJavascriptInterface(WebAppInterface(this.onProgress, this.onError), "AndroidInterface")
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
this.webView.stopLoading()
this.webView.clearHistory()
this.webView.removeAllViews()
this.webView.destroy()
}
fun startDownload(sourceUrl: Uri) {
if (this.downloaderUrl == null) {
this.onError(mapOf(ERROR_KEY to "Downloader URL is not set."))
return
}
val url = URI("${this.downloaderUrl}?videoUrl=$sourceUrl")
this.webView.loadUrl(url.toString())
this.onStart(mapOf())
}
override fun onDownloadStart(
url: String?,
userAgent: String?,
contentDisposition: String?,
mimeType: String?,
contentLength: Long,
) {
if (url == null) {
this.onError(mapOf(ERROR_KEY to "Failed to retrieve download URL from webview."))
return
}
val tempDir = context.cacheDir
val fileName = "${UUID.randomUUID()}.mp4"
val file = File(tempDir, fileName)
val base64 = url.split(",")[1]
val bytes = Base64.decode(base64, Base64.DEFAULT)
val fos = FileOutputStream(file)
try {
fos.write(bytes)
} catch (e: Exception) {
Log.e("FileDownload", "Error downloading file", e)
this.onError(mapOf(ERROR_KEY to e.message.toString()))
return
} finally {
fos.close()
}
val uri = Uri.fromFile(file)
this.onSuccess(mapOf("uri" to uri.toString()))
}
companion object {
const val ERROR_KEY = "message"
}
}
public class WebAppInterface(
val onProgress: ViewEventCallback<Map<String, Any>>,
val onError: ViewEventCallback<Map<String, Any>>,
) {
@JavascriptInterface
public fun onMessage(message: String) {
val jsonObject = JSONObject(message)
val action = jsonObject.getString("action")
when (action) {
"error" -> {
val messageStr = jsonObject.get("messageStr")
if (messageStr !is String) {
this.onError(mapOf(ERROR_KEY to "Failed to decode JSON post message."))
return
}
this.onError(mapOf(ERROR_KEY to messageStr))
}
"progress" -> {
val messageFloat = jsonObject.get("messageFloat")
if (messageFloat !is Number) {
this.onError(mapOf(ERROR_KEY to "Failed to decode JSON post message."))
return
}
this.onProgress(mapOf(PROGRESS_KEY to messageFloat))
}
}
}
companion object {
const val PROGRESS_KEY = "progress"
const val ERROR_KEY = "message"
}
}

View file

@ -5,7 +5,6 @@
"ExpoBlueskySharedPrefsModule",
"ExpoBlueskyReferrerModule",
"ExpoBlueskyVisibilityViewModule",
"ExpoHLSDownloadModule",
"ExpoPlatformInfoModule"
]
},
@ -14,8 +13,7 @@
"expo.modules.blueskyswissarmy.sharedprefs.ExpoBlueskySharedPrefsModule",
"expo.modules.blueskyswissarmy.referrer.ExpoBlueskyReferrerModule",
"expo.modules.blueskyswissarmy.visibilityview.ExpoBlueskyVisibilityViewModule",
"expo.modules.blueskyswissarmy.platforminfo.ExpoPlatformInfoModule",
"expo.modules.blueskyswissarmy.hlsdownload.ExpoHLSDownloadModule"
"expo.modules.blueskyswissarmy.platforminfo.ExpoPlatformInfoModule"
]
}
}

View file

@ -1,15 +1,7 @@
import HLSDownloadView from './src/HLSDownload'
import * as PlatformInfo from './src/PlatformInfo'
import {AudioCategory} from './src/PlatformInfo/types'
import * as Referrer from './src/Referrer'
import * as SharedPrefs from './src/SharedPrefs'
import VisibilityView from './src/VisibilityView'
export {
AudioCategory,
HLSDownloadView,
PlatformInfo,
Referrer,
SharedPrefs,
VisibilityView,
}
export {AudioCategory, PlatformInfo, Referrer, SharedPrefs, VisibilityView}

View file

@ -1,31 +0,0 @@
import ExpoModulesCore
public class ExpoHLSDownloadModule: Module {
public func definition() -> ModuleDefinition {
Name("ExpoHLSDownload")
Function("isAvailable") {
if #available(iOS 14.5, *) {
return true
}
return false
}
View(HLSDownloadView.self) {
Events([
"onStart",
"onError",
"onProgress",
"onSuccess"
])
Prop("downloaderUrl") { (view: HLSDownloadView, downloaderUrl: URL) in
view.downloaderUrl = downloaderUrl
}
AsyncFunction("startDownloadAsync") { (view: HLSDownloadView, sourceUrl: URL) in
view.startDownload(sourceUrl: sourceUrl)
}
}
}
}

View file

@ -1,148 +0,0 @@
import ExpoModulesCore
import WebKit
class HLSDownloadView: ExpoView, WKScriptMessageHandler, WKNavigationDelegate, WKDownloadDelegate {
var webView: WKWebView!
var downloaderUrl: URL?
private var onStart = EventDispatcher()
private var onError = EventDispatcher()
private var onProgress = EventDispatcher()
private var onSuccess = EventDispatcher()
private var outputUrl: URL?
public required init(appContext: AppContext? = nil) {
super.init(appContext: appContext)
// controller for post message api
let contentController = WKUserContentController()
contentController.add(self, name: "onMessage")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
// create webview
let webView = WKWebView(frame: .zero, configuration: configuration)
// Use these for debugging, to see the webview itself
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.layer.masksToBounds = false
webView.backgroundColor = .clear
webView.contentMode = .scaleToFill
webView.navigationDelegate = self
self.addSubview(webView)
self.webView = webView
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - view functions
func startDownload(sourceUrl: URL) {
guard let downloaderUrl = self.downloaderUrl,
let url = URL(string: "\(downloaderUrl.absoluteString)?videoUrl=\(sourceUrl.absoluteString)") else {
self.onError([
"message": "Downloader URL is not set."
])
return
}
self.onStart()
self.webView.load(URLRequest(url: url))
}
// webview message handling
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let response = message.body as? String,
let data = response.data(using: .utf8),
let payload = try? JSONDecoder().decode(WebViewActionPayload.self, from: data) else {
self.onError([
"message": "Failed to decode JSON post message."
])
return
}
switch payload.action {
case .progress:
guard let progress = payload.messageFloat else {
self.onError([
"message": "Failed to decode JSON post message."
])
return
}
self.onProgress([
"progress": progress
])
case .error:
guard let messageStr = payload.messageStr else {
self.onError([
"message": "Failed to decode JSON post message."
])
return
}
self.onError([
"message": messageStr
])
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async -> WKNavigationActionPolicy {
guard #available(iOS 14.5, *) else {
return .cancel
}
if navigationAction.shouldPerformDownload {
return .download
} else {
return .allow
}
}
// MARK: - wkdownloaddelegate
@available(iOS 14.5, *)
func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
download.delegate = self
}
@available(iOS 14.5, *)
func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
download.delegate = self
}
@available(iOS 14.5, *)
func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) {
let directory = NSTemporaryDirectory()
let fileName = "\(NSUUID().uuidString).mp4"
let url = NSURL.fileURL(withPathComponents: [directory, fileName])
self.outputUrl = url
completionHandler(url)
}
@available(iOS 14.5, *)
func downloadDidFinish(_ download: WKDownload) {
guard let url = self.outputUrl else {
return
}
self.onSuccess([
"uri": url.absoluteString
])
self.outputUrl = nil
}
}
struct WebViewActionPayload: Decodable {
enum Action: String, Decodable {
case progress, error
}
let action: Action
let messageStr: String?
let messageFloat: Float?
}

View file

@ -1,39 +0,0 @@
import React from 'react'
import {StyleProp, ViewStyle} from 'react-native'
import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core'
import {HLSDownloadViewProps} from './types'
const NativeModule = requireNativeModule('ExpoHLSDownload')
const NativeView: React.ComponentType<
HLSDownloadViewProps & {
ref: React.RefObject<any>
style: StyleProp<ViewStyle>
}
> = requireNativeViewManager('ExpoHLSDownload')
export default class HLSDownloadView extends React.PureComponent<HLSDownloadViewProps> {
private nativeRef: React.RefObject<any> = React.createRef()
constructor(props: HLSDownloadViewProps) {
super(props)
}
static isAvailable(): boolean {
return NativeModule.isAvailable()
}
async startDownloadAsync(sourceUrl: string): Promise<void> {
return await this.nativeRef.current.startDownloadAsync(sourceUrl)
}
render() {
return (
<NativeView
ref={this.nativeRef}
style={{height: 0, width: 0}}
{...this.props}
/>
)
}
}

View file

@ -1,22 +0,0 @@
import React from 'react'
import {NotImplementedError} from '../NotImplemented'
import {HLSDownloadViewProps} from './types'
export default class HLSDownloadView extends React.PureComponent<HLSDownloadViewProps> {
constructor(props: HLSDownloadViewProps) {
super(props)
}
static isAvailable(): boolean {
return false
}
async startDownloadAsync(sourceUrl: string): Promise<void> {
throw new NotImplementedError({sourceUrl})
}
render() {
return null
}
}

View file

@ -1,10 +0,0 @@
import {NativeSyntheticEvent} from 'react-native'
export interface HLSDownloadViewProps {
downloaderUrl: string
onSuccess: (e: NativeSyntheticEvent<{uri: string}>) => void
onStart?: () => void
onError?: (e: NativeSyntheticEvent<{message: string}>) => void
onProgress?: (e: NativeSyntheticEvent<{progress: number}>) => void
}