import { useCallback, useMemo, useState } from "react"; import type React from "react"; import { Pressable, SafeAreaView, ScrollView, StyleSheet, Text, View, } from "react-native"; import { OmniProvider, OmniView, useEvent, usePlayer, usePlayerState, } from "react-native-omni"; const PLAYLIST = [ { title: "Big Buck Bunny (HLS)", uri: "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8", }, { title: "Sintel Trailer (MP4)", uri: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4", }, ] as const; function formatTime(seconds: number): string { if (!Number.isFinite(seconds) || seconds < 0) { return "00:00"; } const total = Math.floor(seconds); const mins = Math.floor(total / 60) .toString() .padStart(2, "0"); const secs = (total % 60).toString().padStart(2, "0"); return `${mins}:${secs}`; } function PlayerExample({ onPrev, onNext, trackLabel, }: { onPrev: () => void; onNext: () => void; trackLabel: string; }): React.JSX.Element { const player = usePlayer(); const status = usePlayerState("status"); const isPlaying = usePlayerState("isPlaying"); const currentTime = usePlayerState("currentTime"); const duration = usePlayerState("duration"); const playbackRate = usePlayerState("playbackRate"); const muted = usePlayerState("muted"); const volume = usePlayerState("volume"); const [logs, setLogs] = useState([]); const pushLog = useCallback((message: string) => { setLogs((prev) => { const next = [message, ...prev]; return next.slice(0, 8); }); }, []); const handlePrev = useCallback(() => { pushLog("Prev selected"); onPrev(); }, [onPrev, pushLog]); const handleNext = useCallback(() => { pushLog("Next selected"); onNext(); }, [onNext, pushLog]); useEvent( "error", useCallback( (type, message) => { pushLog(`Error (${type}): ${message}`); }, [pushLog], ), ); useEvent( "audioFocusChange", useCallback( (focus) => { pushLog(`Audio focus: ${focus}`); }, [pushLog], ), ); useEvent("prev", handlePrev); useEvent("next", handleNext); useEvent( "end", useCallback(() => { pushLog("Playback ended"); handleNext(); }, [handleNext, pushLog]), ); const togglePlayback = () => { if (isPlaying) { player.pause(); return; } player.play(); }; const toggleMute = () => { player.muted = !muted; }; const changeVolume = (delta: number) => { const nextVolume = Math.max(0, Math.min(1, volume + delta)); player.volume = Number(nextVolume.toFixed(2)); }; const cyclePlaybackRate = () => { const rates = [0.75, 1, 1.25, 1.5, 2]; const index = rates.findIndex((rate) => rate === playbackRate); const nextIndex = (index + 1) % rates.length; player.playbackRate = rates[nextIndex]; }; return ( react-native-omni {trackLabel} {isPlaying ? "Pause" : "Play"} player.seekBy(-10)}> -10s player.seekBy(10)}> +10s player.playPrev()}> Prev player.playNext()}> Next {muted ? "Unmute" : "Mute"} changeVolume(-0.1)}> Vol - changeVolume(0.1)}> Vol + {playbackRate.toFixed(2)}x Status: {status} Time: {formatTime(currentTime)} / {formatTime(duration)} Volume: {(volume * 100).toFixed(0)}% {logs.length === 0 ? ( Event log will appear here. ) : ( logs.map((entry, index) => ( {entry} )) )} ); } function App(): React.JSX.Element { const [currentIndex, setCurrentIndex] = useState(0); const handlePrev = useCallback(() => { setCurrentIndex((index) => (index === 0 ? PLAYLIST.length - 1 : index - 1)); }, []); const handleNext = useCallback(() => { setCurrentIndex((index) => (index + 1) % PLAYLIST.length); }, []); const source = useMemo( () => ({ src: [ { uri: PLAYLIST[currentIndex].uri, headers: {}, }, ], subtitles: [], metadata: { title: PLAYLIST[currentIndex].title, hasPrev: true, hasNext: true, }, }), [currentIndex], ); return ( ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: "#0b1020", }, container: { flex: 1, paddingHorizontal: 16, paddingVertical: 12, gap: 12, }, heading: { fontSize: 24, fontWeight: "700", color: "#e8ecff", }, subheading: { fontSize: 14, color: "#a7b4df", }, video: { width: "100%", aspectRatio: 16 / 9, borderRadius: 14, overflow: "hidden", backgroundColor: "#000000", }, row: { flexDirection: "row", gap: 10, }, button: { flex: 1, backgroundColor: "#1a2442", paddingVertical: 12, borderRadius: 10, alignItems: "center", borderWidth: 1, borderColor: "#2d3f74", }, buttonText: { color: "#f2f4ff", fontSize: 14, fontWeight: "600", }, statsCard: { backgroundColor: "#101833", borderRadius: 10, padding: 12, gap: 4, }, statText: { color: "#cfd9ff", fontSize: 13, }, logCard: { flex: 1, backgroundColor: "#101833", borderRadius: 10, }, logContent: { padding: 12, gap: 6, }, logText: { color: "#9fb0e8", fontSize: 12, }, }); export default App;