mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 10:33:26 +00:00
feat: loading screen prep
This commit is contained in:
@@ -1,8 +1,14 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { ActivityIndicator, View } from "react-native";
|
import { ActivityIndicator, View } from "react-native";
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
|
|
||||||
import type { HlsBasedStream, RunnerEvent } from "@movie-web/provider-utils";
|
import type {
|
||||||
|
DiscoverEmbedsEvent,
|
||||||
|
HlsBasedStream,
|
||||||
|
InitEvent,
|
||||||
|
RunnerEvent,
|
||||||
|
UpdateEvent,
|
||||||
|
} from "@movie-web/provider-utils";
|
||||||
import {
|
import {
|
||||||
extractTracksFromHLS,
|
extractTracksFromHLS,
|
||||||
getVideoStream,
|
getVideoStream,
|
||||||
@@ -20,6 +26,12 @@ interface ScraperProcessProps {
|
|||||||
data: ItemData;
|
data: ItemData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ScrapeStatus {
|
||||||
|
LOADING = "loading",
|
||||||
|
SUCCESS = "success",
|
||||||
|
ERROR = "error",
|
||||||
|
}
|
||||||
|
|
||||||
export const ScraperProcess = ({ data }: ScraperProcessProps) => {
|
export const ScraperProcess = ({ data }: ScraperProcessProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -33,11 +45,47 @@ export const ScraperProcess = ({ data }: ScraperProcessProps) => {
|
|||||||
const setMeta = usePlayerStore((state) => state.setMeta);
|
const setMeta = usePlayerStore((state) => state.setMeta);
|
||||||
const [checkedSource, setCheckedSource] = useState("");
|
const [checkedSource, setCheckedSource] = useState("");
|
||||||
|
|
||||||
const handleEvent = (event: RunnerEvent) => {
|
function isInitEvent(event: RunnerEvent): event is InitEvent {
|
||||||
|
return (event as InitEvent).sourceIds !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUpdateEvent(event: RunnerEvent): event is UpdateEvent {
|
||||||
|
return (event as UpdateEvent).percentage !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDiscoverEmbedsEvent(
|
||||||
|
event: RunnerEvent,
|
||||||
|
): event is DiscoverEmbedsEvent {
|
||||||
|
return (event as DiscoverEmbedsEvent).sourceId !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEvent = useCallback((event: RunnerEvent) => {
|
||||||
if (typeof event === "string") {
|
if (typeof event === "string") {
|
||||||
setCheckedSource(event);
|
setCheckedSource(event);
|
||||||
|
setScrapeStatus({ status: ScrapeStatus.LOADING, progress: 10 });
|
||||||
|
} else if (isUpdateEvent(event)) {
|
||||||
|
switch (event.status) {
|
||||||
|
case ScrapeStatus.SUCCESS:
|
||||||
|
setScrapeStatus({ status: ScrapeStatus.SUCCESS, progress: 100 });
|
||||||
|
break;
|
||||||
|
case ScrapeStatus.ERROR as string:
|
||||||
|
setScrapeStatus({ status: ScrapeStatus.ERROR, progress: 0 });
|
||||||
|
break;
|
||||||
|
case ScrapeStatus.LOADING as string:
|
||||||
|
}
|
||||||
|
setCheckedSource(event.id);
|
||||||
|
} else if (isInitEvent(event) || isDiscoverEmbedsEvent(event)) {
|
||||||
|
setScrapeStatus((prevStatus) => ({
|
||||||
|
status: ScrapeStatus.LOADING,
|
||||||
|
progress: Math.min(prevStatus.progress + 20, 95),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
|
const [_scrapeStatus, setScrapeStatus] = useState({
|
||||||
|
status: ScrapeStatus.LOADING,
|
||||||
|
progress: 0,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
@@ -84,8 +132,8 @@ export const ScraperProcess = ({ data }: ScraperProcessProps) => {
|
|||||||
media: scrapeMedia,
|
media: scrapeMedia,
|
||||||
events: {
|
events: {
|
||||||
// init: handleEvent,
|
// init: handleEvent,
|
||||||
// update: handleEvent,
|
update: handleEvent,
|
||||||
// discoverEmbeds: handleEvent,
|
discoverEmbeds: handleEvent,
|
||||||
start: handleEvent,
|
start: handleEvent,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -94,7 +142,7 @@ export const ScraperProcess = ({ data }: ScraperProcessProps) => {
|
|||||||
|
|
||||||
if (streamResult.stream.type === "hls") {
|
if (streamResult.stream.type === "hls") {
|
||||||
const tracks = await extractTracksFromHLS(
|
const tracks = await extractTracksFromHLS(
|
||||||
streamResult.stream.playlist, // multiple tracks example: "https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8",
|
streamResult.stream.playlist,
|
||||||
{
|
{
|
||||||
...streamResult.stream.preferredHeaders,
|
...streamResult.stream.preferredHeaders,
|
||||||
...streamResult.stream.headers,
|
...streamResult.stream.headers,
|
||||||
@@ -154,6 +202,7 @@ export const ScraperProcess = ({ data }: ScraperProcessProps) => {
|
|||||||
meta?.season?.number,
|
meta?.season?.number,
|
||||||
meta?.episode?.number,
|
meta?.episode?.number,
|
||||||
setAudioTracks,
|
setAudioTracks,
|
||||||
|
handleEvent,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -164,6 +213,7 @@ export const ScraperProcess = ({ data }: ScraperProcessProps) => {
|
|||||||
Checking {checkedSource}
|
Checking {checkedSource}
|
||||||
</Text>
|
</Text>
|
||||||
<ActivityIndicator size="large" color="#0000ff" />
|
<ActivityIndicator size="large" color="#0000ff" />
|
||||||
|
{/* <StatusCircle type={scrapeStatus.status} percentage={scrapeStatus.progress} /> */}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
80
apps/expo/src/components/player/StatusCircle.tsx
Normal file
80
apps/expo/src/components/player/StatusCircle.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { StyleSheet, View } from "react-native";
|
||||||
|
import Animated, {
|
||||||
|
Easing,
|
||||||
|
useAnimatedProps,
|
||||||
|
useSharedValue,
|
||||||
|
withTiming,
|
||||||
|
} from "react-native-reanimated";
|
||||||
|
import { Circle, Svg } from "react-native-svg";
|
||||||
|
import { AntDesign } from "@expo/vector-icons";
|
||||||
|
|
||||||
|
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
|
||||||
|
|
||||||
|
export const StatusCircle = ({
|
||||||
|
type,
|
||||||
|
percentage = 0,
|
||||||
|
}: {
|
||||||
|
type: string;
|
||||||
|
percentage: number;
|
||||||
|
}) => {
|
||||||
|
const radius = 25;
|
||||||
|
const strokeWidth = 5;
|
||||||
|
const circleCircumference = 2 * Math.PI * radius;
|
||||||
|
|
||||||
|
const strokeDashoffset = useSharedValue(circleCircumference);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
strokeDashoffset.value = withTiming(
|
||||||
|
circleCircumference - (circleCircumference * percentage) / 100,
|
||||||
|
{
|
||||||
|
duration: 500,
|
||||||
|
easing: Easing.linear,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}, [circleCircumference, percentage, strokeDashoffset]);
|
||||||
|
|
||||||
|
const animatedProps = useAnimatedProps(() => ({
|
||||||
|
strokeDashoffset: strokeDashoffset.value,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const renderIcon = () => {
|
||||||
|
switch (type) {
|
||||||
|
case "success":
|
||||||
|
return <AntDesign name="checkcircle" size={50} color="green" />;
|
||||||
|
case "error":
|
||||||
|
return <AntDesign name="closecircle" size={50} color="red" />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Svg height="60" width="60" viewBox="0 0 60 60">
|
||||||
|
{type === "loading" && (
|
||||||
|
<AnimatedCircle
|
||||||
|
cx="30"
|
||||||
|
cy="30"
|
||||||
|
r={radius}
|
||||||
|
stroke="blue"
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
fill="none"
|
||||||
|
strokeDasharray={circleCircumference}
|
||||||
|
animatedProps={animatedProps}
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Svg>
|
||||||
|
{renderIcon()}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
position: "relative",
|
||||||
|
},
|
||||||
|
});
|
Reference in New Issue
Block a user