From 57354e6b06a95c08008fde5eb176d5de840fb635 Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Sat, 12 Oct 2024 03:00:26 +1100 Subject: [PATCH] WIP --- app/(auth)/(tabs)/(home)/index.tsx | 26 ++++++- modules/vlc-player/index.ts | 22 ++++-- modules/vlc-player/ios/VlcPlayerModule.swift | 34 ++------- modules/vlc-player/ios/VlcPlayerView.swift | 80 ++++++++++++++++++-- modules/vlc-player/src/VlcPlayer.types.ts | 2 +- modules/vlc-player/src/VlcPlayerView.tsx | 8 +- 6 files changed, 123 insertions(+), 49 deletions(-) diff --git a/app/(auth)/(tabs)/(home)/index.tsx b/app/(auth)/(tabs)/(home)/index.tsx index 98bfc01e..31bff9b1 100644 --- a/app/(auth)/(tabs)/(home)/index.tsx +++ b/app/(auth)/(tabs)/(home)/index.tsx @@ -6,7 +6,7 @@ import { Loader } from "@/components/Loader"; import { MediaListSection } from "@/components/medialists/MediaListSection"; import { Colors } from "@/constants/Colors"; import { TAB_HEIGHT } from "@/constants/Values"; -import { hello } from "@/modules/vlc-player"; +import { hello, VlcPlayerView } from "@/modules/vlc-player"; import { useDownload } from "@/providers/DownloadProvider"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { useSettings } from "@/utils/atoms/settings"; @@ -383,9 +383,27 @@ export default function index() { ); return ( - - {hello()} - + + } + key={"home"} + contentContainerStyle={{ + paddingLeft: insets.left, + paddingRight: insets.right, + paddingBottom: 16, + }} + style={{ + marginBottom: TAB_HEIGHT, + }} + > + + {hello()} + + + ); } diff --git a/modules/vlc-player/index.ts b/modules/vlc-player/index.ts index 3d17dbea..30bf6931 100644 --- a/modules/vlc-player/index.ts +++ b/modules/vlc-player/index.ts @@ -1,10 +1,14 @@ -import { NativeModulesProxy, EventEmitter, Subscription } from 'expo-modules-core'; +import { + NativeModulesProxy, + EventEmitter, + Subscription, +} from "expo-modules-core"; // Import the native module. On web, it will be resolved to VlcPlayer.web.ts // and on native platforms to VlcPlayer.ts -import VlcPlayerModule from './src/VlcPlayerModule'; -import VlcPlayerView from './src/VlcPlayerView'; -import { ChangeEventPayload, VlcPlayerViewProps } from './src/VlcPlayer.types'; +import VlcPlayerModule from "./src/VlcPlayerModule"; +import VlcPlayerView from "./src/VlcPlayerView"; +import { ChangeEventPayload, VlcPlayerViewProps } from "./src/VlcPlayer.types"; // Get the native constant value. export const PI = VlcPlayerModule.PI; @@ -17,10 +21,14 @@ export async function setValueAsync(value: string) { return await VlcPlayerModule.setValueAsync(value); } -const emitter = new EventEmitter(VlcPlayerModule ?? NativeModulesProxy.VlcPlayer); +const emitter = new EventEmitter( + VlcPlayerModule ?? NativeModulesProxy.VlcPlayer +); -export function addChangeListener(listener: (event: ChangeEventPayload) => void): Subscription { - return emitter.addListener('onChange', listener); +export function addChangeListener( + listener: (event: ChangeEventPayload) => void +): Subscription { + return emitter.addListener("onChange", listener); } export { VlcPlayerView, VlcPlayerViewProps, ChangeEventPayload }; diff --git a/modules/vlc-player/ios/VlcPlayerModule.swift b/modules/vlc-player/ios/VlcPlayerModule.swift index 4097921d..756aea11 100644 --- a/modules/vlc-player/ios/VlcPlayerModule.swift +++ b/modules/vlc-player/ios/VlcPlayerModule.swift @@ -9,36 +9,14 @@ public class VlcPlayerModule: Module { // Can be inferred from module's class name, but it's recommended to set it explicitly for clarity. // The module will be accessible from `requireNativeModule('VlcPlayer')` in JavaScript. Name("VlcPlayer") - - // Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary. - Constants([ - "PI": Double.pi - ]) - - // Defines event names that the module can send to JavaScript. - Events("onChange") - - // Defines a JavaScript synchronous function that runs the native code on the JavaScript thread. - Function("hello") { - return "Hello world! 👋" - } - - // Defines a JavaScript function that always returns a Promise and whose native code - // is by default dispatched on the different thread than the JavaScript runtime runs on. - AsyncFunction("setValueAsync") { (value: String) in - // Send an event to JavaScript. - self.sendEvent("onChange", [ - "value": value - ]) - } - - // Enables the module to be used as a native view. Definition components that are accepted as part of the - // view definition: Prop, Events. View(VlcPlayerView.self) { - // Defines a setter for the `name` prop. - Prop("name") { (view: VlcPlayerView, prop: String) in - print(prop) + Prop("source") { (view: VlcPlayerView, source: String) in + view.setSource(source) } } + + Function("hello") { + return "hello from native ios" + } } } diff --git a/modules/vlc-player/ios/VlcPlayerView.swift b/modules/vlc-player/ios/VlcPlayerView.swift index 70c17d44..90ccf3ee 100644 --- a/modules/vlc-player/ios/VlcPlayerView.swift +++ b/modules/vlc-player/ios/VlcPlayerView.swift @@ -1,7 +1,77 @@ import ExpoModulesCore +import UIKit +import MobileVLCKit -// This view will be used as a native component. Make sure to inherit from `ExpoView` -// to apply the proper styling (e.g. border radius and shadows). -class VlcPlayerView: ExpoView { - -} +class VlcPlayerView: ExpoView, VLCMediaPlayerDelegate { + private var mediaPlayer: VLCMediaPlayer? + private var movieView: UIView? + + required init(appContext: AppContext? = nil) { + super.init(appContext: appContext) + DispatchQueue.main.async { + self.setupView() + self.backgroundColor = UIColor.black // Set background color to black + } + } + + private func setupView() { + DispatchQueue.main.async { + self.movieView = UIView() + self.movieView?.translatesAutoresizingMaskIntoConstraints = false + + if let movieView = self.movieView { + self.addSubview(movieView) + NSLayoutConstraint.activate([ + movieView.leadingAnchor.constraint(equalTo: self.leadingAnchor), + movieView.trailingAnchor.constraint(equalTo: self.trailingAnchor), + movieView.topAnchor.constraint(equalTo: self.topAnchor), + movieView.bottomAnchor.constraint(equalTo: self.bottomAnchor) + ]) + } + + self.setupMediaPlayer() + } + } + + private func setupMediaPlayer() { + DispatchQueue.main.async { + self.mediaPlayer = VLCMediaPlayer() + self.mediaPlayer?.delegate = self + self.mediaPlayer?.drawable = self.movieView + print("Media player setup on main thread: \(Thread.isMainThread)") + } + } + + @objc func setSource(_ source: String) { + DispatchQueue.main.async { + print("Setting media source on main thread: \(Thread.isMainThread)") + if let url = URL(string: source) { + self.mediaPlayer?.media = VLCMedia(url: url) + print("Media set, now playing...") + self.mediaPlayer?.play() + } else { + print("Invalid URL.") + } + } + } + + @objc func handlePlayPause() { + DispatchQueue.main.async { + print("Handling play/pause on main thread: \(Thread.isMainThread)") + if self.mediaPlayer?.isPlaying == true { + self.mediaPlayer?.pause() + } else { + self.mediaPlayer?.play() + } + } + } + + func mediaPlayerStateChanged(_ aNotification: Notification!) { + DispatchQueue.main.async { + print("Media player state changed on main thread: \(Thread.isMainThread)") + if self.mediaPlayer?.state == .stopped { + print("Media player stopped") + } + } + } +} \ No newline at end of file diff --git a/modules/vlc-player/src/VlcPlayer.types.ts b/modules/vlc-player/src/VlcPlayer.types.ts index 6a86dd3f..0a1dd94d 100644 --- a/modules/vlc-player/src/VlcPlayer.types.ts +++ b/modules/vlc-player/src/VlcPlayer.types.ts @@ -3,5 +3,5 @@ export type ChangeEventPayload = { }; export type VlcPlayerViewProps = { - name: string; + source: string; }; diff --git a/modules/vlc-player/src/VlcPlayerView.tsx b/modules/vlc-player/src/VlcPlayerView.tsx index 50a7547a..5f245d91 100644 --- a/modules/vlc-player/src/VlcPlayerView.tsx +++ b/modules/vlc-player/src/VlcPlayerView.tsx @@ -1,10 +1,10 @@ -import { requireNativeViewManager } from 'expo-modules-core'; -import * as React from 'react'; +import { requireNativeViewManager } from "expo-modules-core"; +import * as React from "react"; -import { VlcPlayerViewProps } from './VlcPlayer.types'; +import { VlcPlayerViewProps } from "./VlcPlayer.types"; const NativeView: React.ComponentType = - requireNativeViewManager('VlcPlayer'); + requireNativeViewManager("VlcPlayer"); export default function VlcPlayerView(props: VlcPlayerViewProps) { return ;