feat: MPV player for both Android and iOS with added HW decoding PiP (with subtitles) (#1332)

Co-authored-by: Alex Kim <alexkim@Alexs-MacBook-Pro.local>
Co-authored-by: Alex <111128610+Alexk2309@users.noreply.github.com>
Co-authored-by: Simon-Eklundh <simon.eklundh@proton.me>
This commit is contained in:
Fredrik Burmester
2026-01-10 19:35:27 +01:00
committed by GitHub
parent df2f44e086
commit f1575ca48b
98 changed files with 3257 additions and 7448 deletions

View File

@@ -7,19 +7,14 @@ import { useRouter } from "expo-router";
import { type FC, useCallback, useState } from "react";
import { Platform, TouchableOpacity, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { PlaybackSpeedSelector } from "@/components/PlaybackSpeedSelector";
import { useHaptic } from "@/hooks/useHaptic";
import { useOrientation } from "@/hooks/useOrientation";
import { OrientationLock } from "@/packages/expo-screen-orientation";
import { useSettings, VideoPlayerIOS } from "@/utils/atoms/settings";
import { useSettings } from "@/utils/atoms/settings";
import { ICON_SIZES } from "./constants";
import DropdownView from "./dropdown/DropdownView";
import { PlaybackSpeedScope } from "./utils/playback-speed-settings";
import {
type AspectRatio,
AspectRatioSelector,
} from "./VideoScalingModeSelector";
import { type ScaleFactor, VlcZoomControl } from "./VlcZoomControl";
import { type AspectRatio } from "./VideoScalingModeSelector";
import { ZoomToggle } from "./ZoomToggle";
interface HeaderControlsProps {
@@ -33,13 +28,7 @@ interface HeaderControlsProps {
goToNextItem: (options: { isAutoPlay?: boolean }) => void;
previousItem?: BaseItemDto | null;
nextItem?: BaseItemDto | null;
useVlcPlayer?: boolean;
// VLC-specific props
aspectRatio?: AspectRatio;
setVideoAspectRatio?: (aspectRatio: string | null) => Promise<void>;
scaleFactor?: ScaleFactor;
setVideoScaleFactor?: (scaleFactor: number) => Promise<void>;
// KSPlayer-specific props
isZoomedToFill?: boolean;
onZoomToggle?: () => void;
// Playback speed props
@@ -58,11 +47,7 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
goToNextItem,
previousItem,
nextItem,
useVlcPlayer = false,
aspectRatio = "default",
setVideoAspectRatio,
scaleFactor = 0,
setVideoScaleFactor,
aspectRatio: _aspectRatio = "default",
isZoomedToFill = false,
onZoomToggle,
playbackSpeed = 1.0,
@@ -109,9 +94,10 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
style={[
{
position: "absolute",
top: settings?.safeAreaInControlsEnabled ? insets.top : 0,
left: settings?.safeAreaInControlsEnabled ? insets.left : 0,
right: settings?.safeAreaInControlsEnabled ? insets.right : 0,
top: (settings?.safeAreaInControlsEnabled ?? true) ? insets.top : 0,
left: (settings?.safeAreaInControlsEnabled ?? true) ? insets.left : 0,
right:
(settings?.safeAreaInControlsEnabled ?? true) ? insets.right : 0,
},
]}
pointerEvents={showControls ? "auto" : "none"}
@@ -120,7 +106,10 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
<View className='mr-auto p-2' pointerEvents='box-none'>
{!Platform.isTV && (!offline || !mediaSource?.TranscodingUrl) && (
<View pointerEvents='auto'>
<DropdownView />
<DropdownView
playbackSpeed={playbackSpeed}
setPlaybackSpeed={setPlaybackSpeed}
/>
</View>
)}
</View>
@@ -142,20 +131,18 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
/>
</TouchableOpacity>
)}
{!Platform.isTV &&
startPictureInPicture &&
settings?.videoPlayerIOS !== VideoPlayerIOS.VLC && (
<TouchableOpacity
onPress={startPictureInPicture}
className='aspect-square flex flex-col rounded-xl items-center justify-center p-2'
>
<MaterialIcons
name='picture-in-picture'
size={ICON_SIZES.HEADER}
color='white'
/>
</TouchableOpacity>
)}
{!Platform.isTV && startPictureInPicture && (
<TouchableOpacity
onPress={startPictureInPicture}
className='aspect-square flex flex-col rounded-xl items-center justify-center p-2'
>
<MaterialIcons
name='picture-in-picture'
size={ICON_SIZES.HEADER}
color='white'
/>
</TouchableOpacity>
)}
{item?.Type === "Episode" && (
<TouchableOpacity
onPress={switchOnEpisodeMode}
@@ -188,47 +175,12 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
/>
</TouchableOpacity>
)}
{/* Playback Speed Control */}
{!Platform.isTV && setPlaybackSpeed && (
<PlaybackSpeedSelector
selected={playbackSpeed}
onChange={setPlaybackSpeed}
item={item}
/>
)}
{/* VLC-specific controls: Aspect Ratio and Scale/Zoom */}
{useVlcPlayer && (
<AspectRatioSelector
currentRatio={aspectRatio}
onRatioChange={async (newRatio) => {
if (setVideoAspectRatio) {
const aspectRatioString =
newRatio === "default" ? null : newRatio;
await setVideoAspectRatio(aspectRatioString);
}
}}
disabled={!setVideoAspectRatio}
/>
)}
{useVlcPlayer && (
<VlcZoomControl
currentScale={scaleFactor}
onScaleChange={async (newScale) => {
if (setVideoScaleFactor) {
await setVideoScaleFactor(newScale);
}
}}
disabled={!setVideoScaleFactor}
/>
)}
{/* KSPlayer-specific control: Zoom to Fill */}
{!useVlcPlayer && (
<ZoomToggle
isZoomedToFill={isZoomedToFill}
onToggle={onZoomToggle ?? (() => {})}
disabled={!onZoomToggle}
/>
)}
{/* MPV Zoom Toggle */}
<ZoomToggle
isZoomedToFill={isZoomedToFill}
onToggle={onZoomToggle ?? (() => {})}
disabled={!onZoomToggle}
/>
<TouchableOpacity
onPress={onClose}
className='aspect-square flex flex-col rounded-xl items-center justify-center p-2'