mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-02 03:58:36 +01:00
feat: playback speed options
This commit is contained in:
@@ -35,6 +35,7 @@ import { useVideoNavigation } from "./hooks/useVideoNavigation";
|
||||
import { useVideoSlider } from "./hooks/useVideoSlider";
|
||||
import { useVideoTime } from "./hooks/useVideoTime";
|
||||
import { useControlsTimeout } from "./useControlsTimeout";
|
||||
import { PlaybackSpeedScope } from "./utils/playback-speed-settings";
|
||||
import { type AspectRatio } from "./VideoScalingModeSelector";
|
||||
import { type ScaleFactor } from "./VlcZoomControl";
|
||||
|
||||
@@ -66,6 +67,9 @@ interface Props {
|
||||
onZoomToggle?: () => void;
|
||||
api?: Api | null;
|
||||
downloadedFiles?: DownloadedItem[];
|
||||
// Playback speed props
|
||||
playbackSpeed?: number;
|
||||
setPlaybackSpeed?: (speed: number, scope: PlaybackSpeedScope) => void;
|
||||
}
|
||||
|
||||
export const Controls: FC<Props> = ({
|
||||
@@ -93,6 +97,8 @@ export const Controls: FC<Props> = ({
|
||||
offline = false,
|
||||
api = null,
|
||||
downloadedFiles = undefined,
|
||||
playbackSpeed = 1.0,
|
||||
setPlaybackSpeed,
|
||||
}) => {
|
||||
const { settings, updateSettings } = useSettings();
|
||||
const router = useRouter();
|
||||
@@ -484,6 +490,8 @@ export const Controls: FC<Props> = ({
|
||||
setVideoScaleFactor={setVideoScaleFactor}
|
||||
isZoomedToFill={isZoomedToFill}
|
||||
onZoomToggle={onZoomToggle}
|
||||
playbackSpeed={playbackSpeed}
|
||||
setPlaybackSpeed={setPlaybackSpeed}
|
||||
/>
|
||||
</Animated.View>
|
||||
<Animated.View
|
||||
|
||||
@@ -7,12 +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 { ICON_SIZES } from "./constants";
|
||||
import DropdownView from "./dropdown/DropdownView";
|
||||
import { PlaybackSpeedScope } from "./utils/playback-speed-settings";
|
||||
import {
|
||||
type AspectRatio,
|
||||
AspectRatioSelector,
|
||||
@@ -40,6 +42,9 @@ interface HeaderControlsProps {
|
||||
// KSPlayer-specific props
|
||||
isZoomedToFill?: boolean;
|
||||
onZoomToggle?: () => void;
|
||||
// Playback speed props
|
||||
playbackSpeed?: number;
|
||||
setPlaybackSpeed?: (speed: number, scope: PlaybackSpeedScope) => void;
|
||||
}
|
||||
|
||||
export const HeaderControls: FC<HeaderControlsProps> = ({
|
||||
@@ -60,6 +65,8 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
|
||||
setVideoScaleFactor,
|
||||
isZoomedToFill = false,
|
||||
onZoomToggle,
|
||||
playbackSpeed = 1.0,
|
||||
setPlaybackSpeed,
|
||||
}) => {
|
||||
const { settings } = useSettings();
|
||||
const router = useRouter();
|
||||
@@ -181,6 +188,14 @@ 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
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import type { Settings } from "@/utils/atoms/settings";
|
||||
|
||||
export enum PlaybackSpeedScope {
|
||||
Media = "media",
|
||||
Show = "show",
|
||||
All = "all",
|
||||
}
|
||||
|
||||
interface ClearConflictingSettingsResult {
|
||||
readonly updatedPerMedia: Settings["playbackSpeedPerMedia"];
|
||||
readonly updatedPerShow: Settings["playbackSpeedPerShow"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears conflicting playback speed settings based on the selected scope.
|
||||
*
|
||||
* When setting a playback speed at a certain scope, this function removes
|
||||
* any more specific settings that would override the new setting:
|
||||
* - "all" scope: clears both media-specific and show-specific settings
|
||||
* - "media" scope: clears show-specific settings
|
||||
* - "show" scope: clears media-specific settings
|
||||
*/
|
||||
export const clearConflictingSettings = (
|
||||
scope: PlaybackSpeedScope,
|
||||
item: BaseItemDto | undefined,
|
||||
perMedia: Settings["playbackSpeedPerMedia"],
|
||||
perShow: Settings["playbackSpeedPerShow"],
|
||||
): ClearConflictingSettingsResult => {
|
||||
const updatedPerMedia = { ...perMedia };
|
||||
const updatedPerShow = { ...perShow };
|
||||
|
||||
if (scope === "all") {
|
||||
// Clear both media-specific and show-specific settings
|
||||
if (item?.Id && updatedPerMedia[item.Id] !== undefined) {
|
||||
delete updatedPerMedia[item.Id];
|
||||
}
|
||||
if (item?.SeriesId && updatedPerShow[item.SeriesId] !== undefined) {
|
||||
delete updatedPerShow[item.SeriesId];
|
||||
}
|
||||
} else if (scope === "media") {
|
||||
// Clear show-specific setting only
|
||||
if (item?.SeriesId && updatedPerShow[item.SeriesId] !== undefined) {
|
||||
delete updatedPerShow[item.SeriesId];
|
||||
}
|
||||
} else if (scope === "show") {
|
||||
// Clear media-specific setting only
|
||||
if (item?.Id && updatedPerMedia[item.Id] !== undefined) {
|
||||
delete updatedPerMedia[item.Id];
|
||||
}
|
||||
}
|
||||
|
||||
return { updatedPerMedia, updatedPerShow };
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates playback speed settings based on the selected scope and speed.
|
||||
*
|
||||
* This function handles both clearing conflicting settings and updating
|
||||
* the appropriate setting based on the scope:
|
||||
* - "all": updates the default playback speed
|
||||
* - "media": sets a speed for the specific media item
|
||||
* - "show": sets a speed for the entire show
|
||||
*/
|
||||
export const updatePlaybackSpeedSettings = (
|
||||
speed: number,
|
||||
scope: PlaybackSpeedScope,
|
||||
item: BaseItemDto | undefined,
|
||||
settings: Settings,
|
||||
updateSettings: (updates: Partial<Settings>) => void,
|
||||
): void => {
|
||||
const { updatedPerMedia, updatedPerShow } = clearConflictingSettings(
|
||||
scope,
|
||||
item,
|
||||
settings.playbackSpeedPerMedia,
|
||||
settings.playbackSpeedPerShow,
|
||||
);
|
||||
|
||||
if (scope === "all") {
|
||||
updateSettings({
|
||||
defaultPlaybackSpeed: speed,
|
||||
playbackSpeedPerMedia: updatedPerMedia,
|
||||
playbackSpeedPerShow: updatedPerShow,
|
||||
});
|
||||
} else if (scope === "media" && item?.Id) {
|
||||
updatedPerMedia[item.Id] = speed;
|
||||
updateSettings({
|
||||
playbackSpeedPerMedia: updatedPerMedia,
|
||||
playbackSpeedPerShow: updatedPerShow,
|
||||
});
|
||||
} else if (scope === "show" && item?.SeriesId) {
|
||||
updatedPerShow[item.SeriesId] = speed;
|
||||
updateSettings({
|
||||
playbackSpeedPerShow: updatedPerShow,
|
||||
playbackSpeedPerMedia: updatedPerMedia,
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user