diff --git a/modules/expo-bluesky-video-player/android/build.gradle b/modules/expo-bluesky-video-player/android/build.gradle
new file mode 100644
index 0000000000..6272ee9f34
--- /dev/null
+++ b/modules/expo-bluesky-video-player/android/build.gradle
@@ -0,0 +1,95 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'maven-publish'
+
+group = 'expo.modules.blueskyvideoplayer'
+version = '0.5.0'
+
+buildscript {
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
+ if (expoModulesCorePlugin.exists()) {
+ apply from: expoModulesCorePlugin
+ applyKotlinExpoModulesCorePlugin()
+ }
+
+ // Simple helper that allows the root project to override versions declared by this library.
+ ext.safeExtGet = { prop, fallback ->
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
+ }
+
+ // Ensures backward compatibility
+ ext.getKotlinVersion = {
+ if (ext.has("kotlinVersion")) {
+ ext.kotlinVersion()
+ } else {
+ ext.safeExtGet("kotlinVersion", "1.8.10")
+ }
+ }
+
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
+ }
+}
+
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ from components.release
+ }
+ }
+ repositories {
+ maven {
+ url = mavenLocal().url
+ }
+ }
+ }
+}
+
+android {
+ compileSdkVersion safeExtGet("compileSdkVersion", 33)
+
+ def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
+ if (agpVersion.tokenize('.')[0].toInteger() < 8) {
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_11.majorVersion
+ }
+ }
+
+ namespace "expo.modules.blueskyvideoplayer"
+ defaultConfig {
+ minSdkVersion safeExtGet("minSdkVersion", 21)
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
+ versionCode 1
+ versionName "0.5.0"
+ }
+ lintOptions {
+ abortOnError false
+ }
+ publishing {
+ singleVariant("release") {
+ withSourcesJar()
+ }
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation project(':expo-modules-core')
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
+ implementation 'androidx.media3:media3-exoplayer:1.3.1'
+ implementation 'androidx.media3:media3-ui:1.3.1'
+ implementation 'com.squareup.okhttp3:okhttp:3.14.9'
+}
diff --git a/modules/expo-bluesky-video-player/android/src/main/AndroidManifest.xml b/modules/expo-bluesky-video-player/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..bdae66c8f5
--- /dev/null
+++ b/modules/expo-bluesky-video-player/android/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/ExpoBlueskyVideoPlayerModule.kt b/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/ExpoBlueskyVideoPlayerModule.kt
new file mode 100644
index 0000000000..9f1e9ec01a
--- /dev/null
+++ b/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/ExpoBlueskyVideoPlayerModule.kt
@@ -0,0 +1,51 @@
+package expo.modules.blueskyvideoplayer
+
+import expo.modules.kotlin.Promise
+import expo.modules.kotlin.modules.Module
+import expo.modules.kotlin.modules.ModuleDefinition
+
+class ExpoBlueskyVideoPlayerModule : Module() {
+ override fun definition() = ModuleDefinition {
+ Name("ExpoBlueskyVideoPlayer")
+
+ AsyncFunction("prefetchAsync") {
+ { source: String, promise: Promise ->
+ MediaItemManager(appContext).saveToCache(source)
+ promise.resolve()
+ }
+ }
+
+ View(ExpoBlueskyVideoPlayerView::class) {
+ Prop("source") { view: ExpoBlueskyVideoPlayerView, source: String ->
+ view.source = source
+ }
+
+ Prop("autoplay") { view: ExpoBlueskyVideoPlayerView, autoplay: Boolean ->
+ view.autoplay = autoplay
+ }
+
+ Prop("getIsPlayingAsync") { view, promise: Promise ->
+ promise.resolve(view.isPlaying)
+ }
+
+ AsyncFunction("playAsync") { view: ExpoBlueskyVideoPlayerView ->
+ view.play()
+ }
+
+ AsyncFunction("pauseAsync") { view: ExpoBlueskyVideoPlayerView ->
+ view.pause()
+ }
+
+ AsyncFunction("toggleAsync") { view: ExpoBlueskyVideoPlayerView ->
+ view.toggle()
+ }
+
+ OnViewDidUpdateProps {
+ val source = it.source
+ if (source != null) {
+ it.updateSource(source)
+ }
+ }
+ }
+ }
+}
diff --git a/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/ExpoBlueskyVideoPlayerView.kt b/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/ExpoBlueskyVideoPlayerView.kt
new file mode 100644
index 0000000000..90f95a1447
--- /dev/null
+++ b/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/ExpoBlueskyVideoPlayerView.kt
@@ -0,0 +1,69 @@
+package expo.modules.blueskyvideoplayer
+
+import android.content.Context
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.ui.PlayerView
+import expo.modules.kotlin.AppContext
+import expo.modules.kotlin.views.ExpoView
+
+class ExpoBlueskyVideoPlayerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
+ private var playerView: PlayerView
+ private var player: ExoPlayer
+
+ var autoplay: Boolean = true
+ var source: String? = null
+ var isPlaying = true
+
+ init {
+ this.playerView = PlayerView(context).apply {
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
+ useController = false
+ }
+ this.player = ExoPlayer.Builder(context).build().also {
+ playerView.player = it
+ }.apply {
+ repeatMode = ExoPlayer.REPEAT_MODE_ONE
+ volume = 0f
+ }
+ this.addView(this.playerView)
+ }
+
+ override fun onAttachedToWindow() {
+ if (this.autoplay && this.isPlaying) {
+ this.player.play()
+ }
+ super.onAttachedToWindow()
+ }
+
+ override fun onDetachedFromWindow() {
+ this.player.pause()
+ super.onDetachedFromWindow()
+ }
+
+ fun updateSource(source: String) {
+ val mediaItem = MediaItemManager(appContext).getItem(source)
+ player.setMediaItem(mediaItem)
+ player.prepare()
+ player.playWhenReady = true
+ }
+
+ fun play() {
+ this.player.play()
+ this.isPlaying = true
+ }
+
+ fun pause() {
+ this.player.pause()
+ this.isPlaying = false
+ }
+
+ fun toggle() {
+ if (this.isPlaying) {
+ this.player.pause()
+ this.isPlaying = false
+ } else {
+ this.player.play()
+ this.isPlaying = true
+ }
+ }
+}
diff --git a/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/MediaItemManager.kt b/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/MediaItemManager.kt
new file mode 100644
index 0000000000..0feaca2fc2
--- /dev/null
+++ b/modules/expo-bluesky-video-player/android/src/main/java/expo/modules/blueskyvideoplayer/MediaItemManager.kt
@@ -0,0 +1,72 @@
+package expo.modules.blueskyvideoplayer
+
+import android.net.Uri
+import androidx.media3.common.MediaItem
+import expo.modules.kotlin.AppContext
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okio.IOException
+import java.io.File
+import java.io.FileOutputStream
+import java.security.MessageDigest
+
+class MediaItemManager(private val appContext: AppContext) {
+ companion object {
+ private val client = OkHttpClient()
+ }
+
+ fun getItem(source: String): MediaItem {
+ val path = createPath(source)
+ return if (File(path).exists()) {
+ MediaItem.fromUri(Uri.parse(path))
+ } else {
+ saveToCache(source)
+ MediaItem.fromUri(Uri.parse(source))
+ }
+ }
+
+ fun saveToCache(source: String) {
+ val request = Request.Builder()
+ .url(source)
+ .build()
+
+ client.newCall(request).enqueue(object : okhttp3.Callback {
+ override fun onFailure(call: okhttp3.Call, e: IOException) {
+ e.printStackTrace() // Handle the error
+ }
+
+ override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
+ val path = createPath(source)
+ if (response.isSuccessful) {
+ val file = File(path)
+ val fos = FileOutputStream(file)
+ val inputStream = response.body?.byteStream()
+
+ inputStream?.use { input ->
+ fos.use { fileOut ->
+ input.copyTo(fileOut)
+ }
+ }
+ }
+ }
+ })
+ }
+
+ private fun getHash(source: String): String {
+ val md = MessageDigest.getInstance("SHA-1")
+ val byteArray = md.digest(source.toByteArray())
+ return byteArray.joinToString("") { "%02x".format(it) }
+ }
+
+ private fun getGifsDirectory(): String {
+ val gifsDirectory = appContext.cacheDirectory.path + "/gifs"
+ if (!File(gifsDirectory).exists()) {
+ File(gifsDirectory).mkdir()
+ }
+ return gifsDirectory
+ }
+
+ private fun createPath(source: String): String {
+ return getGifsDirectory() + "/" + getHash(source)
+ }
+}
\ No newline at end of file
diff --git a/modules/expo-bluesky-video-player/ios/ExpoBlueskyVideoPlayerModule.swift b/modules/expo-bluesky-video-player/ios/ExpoBlueskyVideoPlayerModule.swift
index 46a38b9eb6..36a0f278fb 100644
--- a/modules/expo-bluesky-video-player/ios/ExpoBlueskyVideoPlayerModule.swift
+++ b/modules/expo-bluesky-video-player/ios/ExpoBlueskyVideoPlayerModule.swift
@@ -4,12 +4,9 @@ public class ExpoBlueskyVideoPlayerModule: Module {
public func definition() -> ModuleDefinition {
Name("ExpoBlueskyVideoPlayer")
- AsyncFunction("setShouldAutoplayAsync") { (value: Bool) in
-
- }
-
- AsyncFunction("prefetchAsync") { (source: String) in
- PlayerItemManager.shared.getOrAddItem(source: source)
+ AsyncFunction("prefetchAsync") { (source: String, promise: Promise) in
+ PlayerItemManager.shared.saveToCache(source: source)
+ promise.resolve()
}
View(ExpoBlueskyVideoPlayerView.self) {
@@ -19,16 +16,27 @@ public class ExpoBlueskyVideoPlayerModule: Module {
view.source = prop
}
+ Prop("autoplay") { (view: ExpoBlueskyVideoPlayerView, prop: Bool) in
+ view.autoplay = prop
+ }
+
AsyncFunction("getIsPlayingAsync") { (view: ExpoBlueskyVideoPlayerView, promise: Promise) in
promise.resolve(view.isPlaying)
}
- AsyncFunction("playAsync") { (view: ExpoBlueskyVideoPlayerView) in
- view.play()
+ AsyncFunction("toggleAsync") { (view: ExpoBlueskyVideoPlayerView, promise: Promise) in
+ view.toggle()
+ promise.resolve()
}
- AsyncFunction("pauseAsync") { (view: ExpoBlueskyVideoPlayerView) in
+ AsyncFunction("playAsync") { (view: ExpoBlueskyVideoPlayerView, promise: Promise) in
+ view.play()
+ promise.resolve()
+ }
+
+ AsyncFunction("pauseAsync") { (view: ExpoBlueskyVideoPlayerView, promise: Promise) in
view.pause()
+ promise.resolve()
}
}
}
diff --git a/modules/expo-bluesky-video-player/ios/ExpoBlueskyVideoPlayerView.swift b/modules/expo-bluesky-video-player/ios/ExpoBlueskyVideoPlayerView.swift
index afdec24312..ec59b027f2 100644
--- a/modules/expo-bluesky-video-player/ios/ExpoBlueskyVideoPlayerView.swift
+++ b/modules/expo-bluesky-video-player/ios/ExpoBlueskyVideoPlayerView.swift
@@ -1,8 +1,15 @@
import ExpoModulesCore
public class ExpoBlueskyVideoPlayerView: ExpoView, AVPlayerViewControllerDelegate {
- public var source: String? = nil
- public var isPlaying: Bool = true
+ public var source: String? = nil {
+ didSet {
+ if self.controller != nil, let source = source {
+ self.controller?.initForView(source, view: self)
+ }
+ }
+ }
+ public var isPlaying = true
+ public var autoplay = true
private var controller: PlayerController? = nil
@@ -25,12 +32,11 @@ public class ExpoBlueskyVideoPlayerView: ExpoView, AVPlayerViewControllerDelegat
return
}
- if let controller = PlayerControllerManager.shared.getPlayer() {
- controller.setFrame(rect: bounds)
- controller.initForView(source, view: self)
- self.addSubview(controller.view)
- self.controller = controller
- }
+ let controller = PlayerControllerManager.shared.getController()
+ controller.setFrame(rect: bounds)
+ controller.initForView(source, view: self)
+ self.addSubview(controller.view)
+ self.controller = controller
} else {
self.controller?.release()
}
@@ -45,4 +51,12 @@ public class ExpoBlueskyVideoPlayerView: ExpoView, AVPlayerViewControllerDelegat
self.isPlaying = false
self.controller?.pause()
}
+
+ func toggle() {
+ if self.isPlaying {
+ self.pause()
+ } else {
+ self.play()
+ }
+ }
}
diff --git a/modules/expo-bluesky-video-player/ios/PlayerController.swift b/modules/expo-bluesky-video-player/ios/PlayerController.swift
index 6eb9765928..3b9cda88e5 100644
--- a/modules/expo-bluesky-video-player/ios/PlayerController.swift
+++ b/modules/expo-bluesky-video-player/ios/PlayerController.swift
@@ -1,9 +1,9 @@
import AVKit
class PlayerController: AVPlayerViewController, AVPlayerViewControllerDelegate {
- private var playerLooper: AVPlayerLooper? = nil
var source: String? = nil
var _superview: ExpoBlueskyVideoPlayerView? = nil
+ var needsCaching = true
var isInUse = false
var playerItem: AVPlayerItem? {
@@ -11,13 +11,7 @@ class PlayerController: AVPlayerViewController, AVPlayerViewControllerDelegate {
return self.player?.currentItem
}
set {
- if newValue == nil {
- if let player = self.player as? AVQueuePlayer {
- player.removeAllItems()
- }
- } else {
- self.player?.replaceCurrentItem(with: newValue)
- }
+ self.player?.replaceCurrentItem(with: newValue)
}
}
var itemStatus: AVPlayerItem.Status? {
@@ -32,7 +26,11 @@ class PlayerController: AVPlayerViewController, AVPlayerViewControllerDelegate {
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nil, bundle: nil)
- self.player = AVQueuePlayer()
+
+ let player = AVPlayer()
+ player.actionAtItemEnd = .none
+ player.volume = .zero
+ self.player = player
self.delegate = self
self.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
@@ -44,63 +42,69 @@ class PlayerController: AVPlayerViewController, AVPlayerViewControllerDelegate {
if #available(iOS 16.0, *) {
self.allowsVideoFrameAnalysis = false
}
-
- self.player?.actionAtItemEnd = .pause
- self.player?.volume = .zero
+ }
+
+ deinit {
+ self.release()
}
func setFrame(rect: CGRect) {
self.view.frame = rect
}
- public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
- if keyPath == "status", let playerItem = object as? AVPlayerItem {
- if playerItem.status == .readyToPlay, let player = self.player as? AVQueuePlayer {
- // GIFs frequently have some black frames at the end of the video. To account for that, we offset the duration by 100ms,
- // which should be enough frames to prevent a flicker.
- player.play()
- self.setLooper()
+ @objc func playerItemDidReachEnd(notification: Notification) {
+ if let playerItem = notification.object as? AVPlayerItem {
+ playerItem.seek(to: CMTime.zero, completionHandler: nil)
+
+ if self.needsCaching, let asset = self.playerItem?.asset as? AVURLAsset {
+ PlayerItemManager.shared.saveToCache(source: asset.url.absoluteString)
+ self.needsCaching = false
}
}
}
- private func setLooper() {
- guard let player = self.player as? AVQueuePlayer, let playerItem = self.playerItem else {
- return
+ public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+ if keyPath == "status", let playerItem = object as? AVPlayerItem {
+ if playerItem.status == .readyToPlay, let player = self.player {
+ if _superview?.autoplay == true, _superview?.isPlaying == true {
+ player.play()
+ }
+ }
}
-
- let duration = playerItem.duration
- self.playerLooper = AVPlayerLooper(
- player: player,
- templateItem: playerItem,
- timeRange: CMTimeRange(
- start: CMTime(value: 0, timescale: duration.timescale),
- duration: CMTime(value: duration.value - 100, timescale: 1000)
- )
- )
}
func initForView(_ source: String, view: ExpoBlueskyVideoPlayerView) {
- if let playerItem = PlayerItemManager.shared.getOrAddItem(source: source) {
+ if let playerItemAndInfo = PlayerItemManager.shared.getItem(source: source) {
+ let playerItem = playerItemAndInfo.0
+ self.needsCaching = !playerItemAndInfo.1
+
playerItem.addObserver(self, forKeyPath: "status", options: [.old, .new], context: nil)
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(playerItemDidReachEnd(notification:)),
+ name: AVPlayerItem.didPlayToEndTimeNotification,
+ object: playerItem
+ )
+
self.isInUse = true
self.source = source
self.playerItem = playerItem
self._superview = view
-
- if view.isPlaying, self.itemStatus == .readyToPlay {
- self.play()
- self.setLooper()
- }
}
}
func release() {
+ NotificationCenter.default.removeObserver(
+ self,
+ name: AVPlayerItem.didPlayToEndTimeNotification,
+ object: self.playerItem
+ )
+
+ self.needsCaching = true
self.isInUse = false
self.pause()
- self.source = nil
- self.playerLooper = nil
self.playerItem = nil
+ self.removeFromParent()
}
func play() {
diff --git a/modules/expo-bluesky-video-player/ios/PlayerControllerManager.swift b/modules/expo-bluesky-video-player/ios/PlayerControllerManager.swift
index 86b26412a3..702e267d6a 100644
--- a/modules/expo-bluesky-video-player/ios/PlayerControllerManager.swift
+++ b/modules/expo-bluesky-video-player/ios/PlayerControllerManager.swift
@@ -4,29 +4,37 @@ class PlayerControllerManager {
static let shared = PlayerControllerManager()
private var controllers: [PlayerController] = []
- private let max = 10
- func getPlayer() -> PlayerController? {
+ func getController() -> PlayerController {
if let controller = self.controllers.first(where: { $0.isInUse == false }) {
return controller
- } else if controllers.count < self.max {
+ } else {
let controller = PlayerController()
- controllers.append(controller)
+ self.controllers.append(controller)
return controller
}
- return nil
}
- func releasePlayer(controller: PlayerController) {
- if let controller = controllers.first(where: { $0 === controller }) {
+ func releaseController(controller: PlayerController) {
+ if let controller = self.controllers.first(where: { $0 === controller }) {
controller.release()
}
}
- func findBySource(source: String) -> PlayerController? {
- if let controller = controllers.first(where: { $0.source == source}) {
+ func findByItem(_ item: AVPlayerItem) -> PlayerController? {
+ if let controller = self.controllers.first(where: { $0.playerItem === item}) {
return controller
}
return nil
}
+
+ func cleanupExcessiveControllers() {
+ if self.controllers.count <= 15 {
+ return
+ }
+
+ self.controllers.removeAll { c in
+ return !c.isInUse
+ }
+ }
}
diff --git a/modules/expo-bluesky-video-player/ios/PlayerItemManager.swift b/modules/expo-bluesky-video-player/ios/PlayerItemManager.swift
index bfcd6249aa..828620c241 100644
--- a/modules/expo-bluesky-video-player/ios/PlayerItemManager.swift
+++ b/modules/expo-bluesky-video-player/ios/PlayerItemManager.swift
@@ -1,44 +1,58 @@
+import Foundation
import AVKit
+import CryptoKit
class PlayerItemManager {
static let shared = PlayerItemManager()
- private var items: [(String, AVPlayerItem)] = []
- private let max = 30
+ private var prefetchQueue: [String] = []
- private func addItem(source: String) -> AVPlayerItem? {
- guard let url = URL(string: source) else {
- return nil
+ func getItem(source: String) -> (AVPlayerItem, Bool)? {
+ let path = createPath(source)
+
+ if FileManager.default.fileExists(atPath: path.path) {
+ return (AVPlayerItem(url: path), true)
+ } else if let url = URL(string: source) {
+ return (AVPlayerItem(url: url), false)
}
-
- if items.count >= self.max {
- let first = self.items.removeFirst()
- self.removeItem(source: first.0)
- }
-
- let item = AVPlayerItem(url: url)
- self.items.append((source, item))
-
- return item
+ return nil
}
- func getOrAddItem(source: String) -> AVPlayerItem? {
- if let index = items.firstIndex(where: { $0.0 == source }) {
- let item = self.items[index]
- self.items.move(fromOffsets: IndexSet(integer: index), toOffset: self.items.count - 1)
- return item.1
- }
-
- return self.addItem(source: source)
- }
-
- func removeItem(source: String?) {
- guard let source = source else {
+ func saveToCache(source: String) {
+ guard let sourceUrl = URL(string: source) else {
return
}
- if let controller = PlayerControllerManager.shared.findBySource(source: source) {
- controller.release()
+ let path = createPath(sourceUrl.absoluteString)
+ let downloadTask = URLSession.shared.downloadTask(with:sourceUrl) { tempLocalURL, response, error in
+ if let tempLocalURL = tempLocalURL, error == nil {
+ try? FileManager.default.moveItem(at: tempLocalURL, to: path)
+ }
}
+ downloadTask.resume()
+ }
+
+ private func getHash(_ source: String) -> String {
+ let data = Data(source.utf8)
+ let hash = Insecure.SHA1.hash(data: data)
+ return hash.map { String(format: "%02hhx", $0)}.joined()
+ }
+
+ private func getGifsDirectory() -> URL {
+ let paths = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
+ var cacheDir = paths[0]
+ cacheDir = cacheDir.appendingPathComponent("gifs", isDirectory: true)
+
+ if !FileManager.default.fileExists(atPath: cacheDir.path) {
+ try? FileManager.default.createDirectory(at: cacheDir, withIntermediateDirectories: false)
+ }
+
+ return cacheDir
+ }
+
+ private func createPath(_ source: String) -> URL {
+ let hash = getHash(source)
+ var gifsDir = getGifsDirectory()
+ return gifsDir.appendingPathComponent("\(hash).mp4", isDirectory: false)
}
}
diff --git a/modules/expo-bluesky-video-player/src/VideoPlayer.tsx b/modules/expo-bluesky-video-player/src/VideoPlayer.tsx
index 2a1b3d458c..e5ca515136 100644
--- a/modules/expo-bluesky-video-player/src/VideoPlayer.tsx
+++ b/modules/expo-bluesky-video-player/src/VideoPlayer.tsx
@@ -14,8 +14,8 @@ export class VideoPlayer extends React.PureComponent {
this.nativeRef = createRef()
}
- static async setShouldAutoplayAsync(shouldAutoplay: boolean): Promise {
- await VideoModule.setShouldAutoplayAsync(shouldAutoplay)
+ static async prefetchAsync(source: string): Promise {
+ await VideoModule.prefetchAsync(source)
}
async playAsync(): Promise {
@@ -26,6 +26,10 @@ export class VideoPlayer extends React.PureComponent {
await this.nativeRef.current.pauseAsync()
}
+ async toggleAsync(): Promise {
+ await this.nativeRef.current.toggleAsync()
+ }
+
render() {
return
}
diff --git a/modules/expo-bluesky-video-player/src/VideoPlayer.types.ts b/modules/expo-bluesky-video-player/src/VideoPlayer.types.ts
index 9d2f277a2e..3a8e9444b0 100644
--- a/modules/expo-bluesky-video-player/src/VideoPlayer.types.ts
+++ b/modules/expo-bluesky-video-player/src/VideoPlayer.types.ts
@@ -7,6 +7,7 @@ export interface VideoPlayerLoadEvent {
}
export interface VideoPlayerViewProps extends ViewProps {
- source: string | null
- onLoad: (event: VideoPlayerLoadEvent) => void
+ autoplay?: boolean
+ source?: string
+ onLoad?: (event: VideoPlayerLoadEvent) => void
}
diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx
index b3bde2a118..401b5935ad 100644
--- a/src/view/com/util/List.tsx
+++ b/src/view/com/util/List.tsx
@@ -87,6 +87,8 @@ function ListImpl(
contentOffset = {x: 0, y: headerOffset * -1}
}
+ console.log(props.removeClippedSubviews)
+
return (