mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
fix: bump biome and fix error (#864)
Some checks failed
🤖 Android APK Build / 🏗️ Build Android APK (push) Has been cancelled
🤖 iOS IPA Build / 🏗️ Build iOS IPA (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🕒 Handle Stale Issues / 🗑️ Cleanup Stale Issues (push) Has been cancelled
Some checks failed
🤖 Android APK Build / 🏗️ Build Android APK (push) Has been cancelled
🤖 iOS IPA Build / 🏗️ Build iOS IPA (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🕒 Handle Stale Issues / 🗑️ Cleanup Stale Issues (push) Has been cancelled
This commit is contained in:
@@ -3,9 +3,11 @@ import { useEffect, useRef } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
import { Slider } from "react-native-awesome-slider";
|
||||
import { useSharedValue } from "react-native-reanimated";
|
||||
|
||||
const VolumeManager = Platform.isTV
|
||||
? null
|
||||
: require("react-native-volume-manager");
|
||||
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { VolumeResult } from "react-native-volume-manager";
|
||||
|
||||
@@ -14,9 +16,7 @@ interface AudioSliderProps {
|
||||
}
|
||||
|
||||
const AudioSlider: React.FC<AudioSliderProps> = ({ setVisibility }) => {
|
||||
if (Platform.isTV) {
|
||||
return;
|
||||
}
|
||||
const isTv = Platform.isTV;
|
||||
|
||||
const volume = useSharedValue<number>(50); // Explicitly type as number
|
||||
const min = useSharedValue<number>(0); // Explicitly type as number
|
||||
@@ -25,6 +25,7 @@ const AudioSlider: React.FC<AudioSliderProps> = ({ setVisibility }) => {
|
||||
const timeoutRef = useRef<number | null>(null); // Use a ref to store the timeout ID
|
||||
|
||||
useEffect(() => {
|
||||
if (isTv) return;
|
||||
const fetchInitialVolume = async () => {
|
||||
try {
|
||||
const { volume: initialVolume } = await VolumeManager.getVolume();
|
||||
@@ -42,7 +43,7 @@ const AudioSlider: React.FC<AudioSliderProps> = ({ setVisibility }) => {
|
||||
// Re-enable the native volume UI when the component unmounts
|
||||
VolumeManager.showNativeVolumeUI({ enabled: true });
|
||||
};
|
||||
}, []);
|
||||
}, [isTv, volume]);
|
||||
|
||||
const handleValueChange = async (value: number) => {
|
||||
volume.value = value;
|
||||
@@ -53,6 +54,7 @@ const AudioSlider: React.FC<AudioSliderProps> = ({ setVisibility }) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isTv) return;
|
||||
const volumeListener = VolumeManager.addVolumeListener(
|
||||
(result: VolumeResult) => {
|
||||
volume.value = result.volume * 100;
|
||||
@@ -76,7 +78,9 @@ const AudioSlider: React.FC<AudioSliderProps> = ({ setVisibility }) => {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, [volume]);
|
||||
}, [isTv, volume, setVisibility]);
|
||||
|
||||
if (isTv) return;
|
||||
|
||||
return (
|
||||
<View style={styles.sliderContainer}>
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
import { Slider } from "react-native-awesome-slider";
|
||||
import { useSharedValue } from "react-native-reanimated";
|
||||
|
||||
// import * as Brightness from "expo-brightness";
|
||||
const Brightness = !Platform.isTV ? require("expo-brightness") : null;
|
||||
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
|
||||
|
||||
const BrightnessSlider = () => {
|
||||
if (Platform.isTV) return;
|
||||
const isTv = Platform.isTV;
|
||||
|
||||
const brightness = useSharedValue(50);
|
||||
const min = useSharedValue(0);
|
||||
const max = useSharedValue(100);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTv) return;
|
||||
const fetchInitialBrightness = async () => {
|
||||
const initialBrightness = await Brightness.getBrightnessAsync();
|
||||
brightness.value = initialBrightness * 100;
|
||||
};
|
||||
fetchInitialBrightness();
|
||||
}, []);
|
||||
}, [brightness, isTv]);
|
||||
|
||||
const handleValueChange = async (value: number) => {
|
||||
brightness.value = value;
|
||||
await Brightness.setBrightnessAsync(value / 100);
|
||||
};
|
||||
|
||||
if (isTv) return;
|
||||
|
||||
return (
|
||||
<View style={styles.sliderContainer}>
|
||||
<Slider
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { useRouter } from "expo-router";
|
||||
import { t } from "i18next";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { Button } from "@/components/Button";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
|
||||
export interface ContinueWatchingOverlayProps {
|
||||
goToNextItem: (options: {
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
import { Loader } from "@/components/Loader";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import ContinueWatchingOverlay from "@/components/video-player/controls/ContinueWatchingOverlay";
|
||||
import { useAdjacentItems } from "@/hooks/useAdjacentEpisodes";
|
||||
import { useCreditSkipper } from "@/hooks/useCreditSkipper";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { useIntroSkipper } from "@/hooks/useIntroSkipper";
|
||||
import { useTrickplay } from "@/hooks/useTrickplay";
|
||||
import type { TrackInfo, VlcPlayerViewRef } from "@/modules/VlcPlayer.types";
|
||||
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { VideoPlayer, useSettings } from "@/utils/atoms/settings";
|
||||
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
|
||||
import { getItemById } from "@/utils/jellyfin/user-library/getItemById";
|
||||
import { writeToLog } from "@/utils/log";
|
||||
import {
|
||||
formatTimeString,
|
||||
msToTicks,
|
||||
secondsToMs,
|
||||
ticksToMs,
|
||||
ticksToSeconds,
|
||||
} from "@/utils/time";
|
||||
import { Ionicons, MaterialIcons } from "@expo/vector-icons";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
@@ -29,7 +7,7 @@ import { Image } from "expo-image";
|
||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import { debounce } from "lodash";
|
||||
import React, {
|
||||
import {
|
||||
type Dispatch,
|
||||
type FC,
|
||||
type MutableRefObject,
|
||||
@@ -42,27 +20,48 @@ import React, {
|
||||
import {
|
||||
Platform,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { Slider } from "react-native-awesome-slider";
|
||||
import {
|
||||
type SharedValue,
|
||||
runOnJS,
|
||||
type SharedValue,
|
||||
useAnimatedReaction,
|
||||
useSharedValue,
|
||||
} from "react-native-reanimated";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Loader } from "@/components/Loader";
|
||||
import ContinueWatchingOverlay from "@/components/video-player/controls/ContinueWatchingOverlay";
|
||||
import { useAdjacentItems } from "@/hooks/useAdjacentEpisodes";
|
||||
import { useCreditSkipper } from "@/hooks/useCreditSkipper";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { useIntroSkipper } from "@/hooks/useIntroSkipper";
|
||||
import { useTrickplay } from "@/hooks/useTrickplay";
|
||||
import type { TrackInfo, VlcPlayerViewRef } from "@/modules/VlcPlayer.types";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings, VideoPlayer } from "@/utils/atoms/settings";
|
||||
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
|
||||
import { getItemById } from "@/utils/jellyfin/user-library/getItemById";
|
||||
import { writeToLog } from "@/utils/log";
|
||||
import {
|
||||
formatTimeString,
|
||||
msToTicks,
|
||||
secondsToMs,
|
||||
ticksToMs,
|
||||
ticksToSeconds,
|
||||
} from "@/utils/time";
|
||||
import AudioSlider from "./AudioSlider";
|
||||
import BrightnessSlider from "./BrightnessSlider";
|
||||
import { EpisodeList } from "./EpisodeList";
|
||||
import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton";
|
||||
import SkipButton from "./SkipButton";
|
||||
import { VideoTouchOverlay } from "./VideoTouchOverlay";
|
||||
import { ControlProvider } from "./contexts/ControlContext";
|
||||
import { VideoProvider } from "./contexts/VideoContext";
|
||||
import DropdownView from "./dropdown/DropdownView";
|
||||
import { EpisodeList } from "./EpisodeList";
|
||||
import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton";
|
||||
import SkipButton from "./SkipButton";
|
||||
import { useControlsTimeout } from "./useControlsTimeout";
|
||||
import { VideoTouchOverlay } from "./VideoTouchOverlay";
|
||||
|
||||
interface Props {
|
||||
item: BaseItemDto;
|
||||
@@ -241,7 +240,10 @@ export const Controls: FC<Props> = ({
|
||||
({
|
||||
isAutoPlay,
|
||||
resetWatchCount,
|
||||
}: { isAutoPlay?: boolean; resetWatchCount?: boolean }) => {
|
||||
}: {
|
||||
isAutoPlay?: boolean;
|
||||
resetWatchCount?: boolean;
|
||||
}) => {
|
||||
if (!nextItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
import ContinueWatchingPoster from "@/components/ContinueWatchingPoster";
|
||||
import { DownloadSingleItem } from "@/components/DownloadItem";
|
||||
import { Loader } from "@/components/Loader";
|
||||
import {
|
||||
HorizontalScroll,
|
||||
type HorizontalScrollRef,
|
||||
} from "@/components/common/HorrizontalScroll";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import {
|
||||
SeasonDropdown,
|
||||
type SeasonIndexState,
|
||||
} from "@/components/series/SeasonDropdown";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
import { runtimeTicksToSeconds } from "@/utils/time";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
@@ -21,6 +6,21 @@ import { atom, useAtom } from "jotai";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import ContinueWatchingPoster from "@/components/ContinueWatchingPoster";
|
||||
import {
|
||||
HorizontalScroll,
|
||||
type HorizontalScrollRef,
|
||||
} from "@/components/common/HorrizontalScroll";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { DownloadSingleItem } from "@/components/DownloadItem";
|
||||
import { Loader } from "@/components/Loader";
|
||||
import {
|
||||
SeasonDropdown,
|
||||
type SeasonIndexState,
|
||||
} from "@/components/series/SeasonDropdown";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
import { runtimeTicksToSeconds } from "@/utils/time";
|
||||
|
||||
type Props = {
|
||||
item: BaseItemDto;
|
||||
@@ -33,7 +33,7 @@ export const seasonIndexAtom = atom<SeasonIndexState>({});
|
||||
export const EpisodeList: React.FC<Props> = ({ item, close, goToItem }) => {
|
||||
const [api] = useAtom(apiAtom);
|
||||
const [user] = useAtom(userAtom);
|
||||
const insets = useSafeAreaInsets(); // Get safe area insets
|
||||
const _insets = useSafeAreaInsets(); // Get safe area insets
|
||||
const [seasonIndexState, setSeasonIndexState] = useAtom(seasonIndexAtom);
|
||||
const scrollViewRef = useRef<HorizontalScrollRef>(null); // Reference to the HorizontalScroll
|
||||
const scrollToIndex = (index: number) => {
|
||||
@@ -163,92 +163,87 @@ export const EpisodeList: React.FC<Props> = ({ item, close, goToItem }) => {
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
className={"flex flex-row items-center space-x-2 z-10 p-4"}
|
||||
>
|
||||
{seriesItem && (
|
||||
<SeasonDropdown
|
||||
item={seriesItem}
|
||||
seasons={seasons}
|
||||
state={seasonIndexState}
|
||||
onSelect={(season) => {
|
||||
setSeasonIndexState((prev) => ({
|
||||
...prev,
|
||||
[item.SeriesId ?? ""]: season.IndexNumber,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
onPress={async () => {
|
||||
close();
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
className={"flex flex-row items-center space-x-2 z-10 p-4"}
|
||||
>
|
||||
{seriesItem && (
|
||||
<SeasonDropdown
|
||||
item={seriesItem}
|
||||
seasons={seasons}
|
||||
state={seasonIndexState}
|
||||
onSelect={(season) => {
|
||||
setSeasonIndexState((prev) => ({
|
||||
...prev,
|
||||
[item.SeriesId ?? ""]: season.IndexNumber,
|
||||
}));
|
||||
}}
|
||||
className='aspect-square flex flex-col bg-neutral-800/90 rounded-xl items-center justify-center p-2'
|
||||
>
|
||||
<Ionicons name='close' size={24} color='white' />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
/>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
onPress={async () => {
|
||||
close();
|
||||
}}
|
||||
className='aspect-square flex flex-col bg-neutral-800/90 rounded-xl items-center justify-center p-2'
|
||||
>
|
||||
<Ionicons name='close' size={24} color='white' />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<HorizontalScroll
|
||||
ref={scrollViewRef}
|
||||
data={episodes}
|
||||
extraData={item}
|
||||
renderItem={(_item, idx) => (
|
||||
<View
|
||||
key={_item.Id}
|
||||
style={{}}
|
||||
className={`flex flex-col w-44 ${
|
||||
item.Id !== _item.Id ? "opacity-75" : ""
|
||||
}`}
|
||||
<HorizontalScroll
|
||||
ref={scrollViewRef}
|
||||
data={episodes}
|
||||
extraData={item}
|
||||
renderItem={(_item, _idx) => (
|
||||
<View
|
||||
key={_item.Id}
|
||||
style={{}}
|
||||
className={`flex flex-col w-44 ${
|
||||
item.Id !== _item.Id ? "opacity-75" : ""
|
||||
}`}
|
||||
>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
goToItem(_item.Id);
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
goToItem(_item.Id);
|
||||
<ContinueWatchingPoster
|
||||
item={_item}
|
||||
useEpisodePoster
|
||||
showPlayButton={_item.Id !== item.Id}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View className='shrink'>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
style={{
|
||||
lineHeight: 18, // Adjust this value based on your text size
|
||||
height: 36, // lineHeight * 2 for consistent two-line space
|
||||
}}
|
||||
>
|
||||
<ContinueWatchingPoster
|
||||
item={_item}
|
||||
useEpisodePoster
|
||||
showPlayButton={_item.Id !== item.Id}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View className='shrink'>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
style={{
|
||||
lineHeight: 18, // Adjust this value based on your text size
|
||||
height: 36, // lineHeight * 2 for consistent two-line space
|
||||
}}
|
||||
>
|
||||
{_item.Name}
|
||||
</Text>
|
||||
<Text numberOfLines={1} className='text-xs text-neutral-475'>
|
||||
{`S${_item.ParentIndexNumber?.toString()}:E${_item.IndexNumber?.toString()}`}
|
||||
</Text>
|
||||
<Text className='text-xs text-neutral-500'>
|
||||
{runtimeTicksToSeconds(_item.RunTimeTicks)}
|
||||
</Text>
|
||||
</View>
|
||||
<View className='self-start mt-2'>
|
||||
<DownloadSingleItem item={_item} />
|
||||
</View>
|
||||
<Text
|
||||
numberOfLines={5}
|
||||
className='text-xs text-neutral-500 shrink'
|
||||
>
|
||||
{_item.Overview}
|
||||
{_item.Name}
|
||||
</Text>
|
||||
<Text numberOfLines={1} className='text-xs text-neutral-475'>
|
||||
{`S${_item.ParentIndexNumber?.toString()}:E${_item.IndexNumber?.toString()}`}
|
||||
</Text>
|
||||
<Text className='text-xs text-neutral-500'>
|
||||
{runtimeTicksToSeconds(_item.RunTimeTicks)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
keyExtractor={(e: BaseItemDto) => e.Id ?? ""}
|
||||
estimatedItemSize={200}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
/>
|
||||
</>
|
||||
<View className='self-start mt-2'>
|
||||
<DownloadSingleItem item={_item} />
|
||||
</View>
|
||||
<Text numberOfLines={5} className='text-xs text-neutral-500 shrink'>
|
||||
{_item.Overview}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
keyExtractor={(e: BaseItemDto) => e.Id ?? ""}
|
||||
estimatedItemSize={200}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import type React from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -9,12 +7,14 @@ import {
|
||||
View,
|
||||
} from "react-native";
|
||||
import Animated, {
|
||||
Easing,
|
||||
runOnJS,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
Easing,
|
||||
runOnJS,
|
||||
} from "react-native-reanimated";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
|
||||
interface NextEpisodeCountDownButtonProps extends TouchableOpacityProps {
|
||||
onFinish?: () => void;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useTrickplay } from "@/hooks/useTrickplay";
|
||||
import { formatTimeString, msToTicks, ticksToSeconds } from "@/utils/time";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { Image } from "expo-image";
|
||||
import type React from "react";
|
||||
import { useRef, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import { Slider } from "react-native-awesome-slider";
|
||||
import { type SharedValue, useSharedValue } from "react-native-reanimated";
|
||||
import { type SharedValue } from "react-native-reanimated";
|
||||
import { useTrickplay } from "@/hooks/useTrickplay";
|
||||
import { formatTimeString, msToTicks, ticksToSeconds } from "@/utils/time";
|
||||
|
||||
interface SliderScrubberProps {
|
||||
cacheProgress: SharedValue<number>;
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
MediaSourceInfo,
|
||||
} from "@jellyfin/sdk/lib/generated-client";
|
||||
import type React from "react";
|
||||
import { type ReactNode, createContext, useContext, useState } from "react";
|
||||
import { createContext, type ReactNode, useContext } from "react";
|
||||
|
||||
interface ControlContextProps {
|
||||
item: BaseItemDto;
|
||||
|
||||
Reference in New Issue
Block a user