mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-26 07:40:31 +01:00
Merge branch 'develop' into feature/newarch
This commit is contained in:
@@ -14,7 +14,7 @@ export type HapticFeedbackType =
|
||||
| "error";
|
||||
|
||||
export const useHaptic = (feedbackType: HapticFeedbackType = "selection") => {
|
||||
const [settings] = useSettings(null);
|
||||
const { settings } = useSettings();
|
||||
const isTv = Platform.isTV;
|
||||
const isDisabled =
|
||||
isTv ||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
import { t } from "i18next";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { toast } from "sonner-native";
|
||||
import { defaultValues, Settings } from "@/utils/atoms/settings";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import type { RTRating } from "@/utils/jellyseerr/server/api/rating/rottentomatoes";
|
||||
import {
|
||||
IssueStatus,
|
||||
@@ -40,6 +40,7 @@ import type { UserResultsResponse } from "@/utils/jellyseerr/server/interfaces/a
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type {
|
||||
CombinedCredit,
|
||||
PersonCreditCast,
|
||||
PersonDetails,
|
||||
} from "@/utils/jellyseerr/server/models/Person";
|
||||
import type {
|
||||
@@ -386,7 +387,7 @@ export class JellyseerrApi {
|
||||
`Jellyseerr response error\nerror: ${error.toString()}\nurl: ${error?.config?.url}`,
|
||||
error.response?.data,
|
||||
);
|
||||
if (error.status === 403) {
|
||||
if (error.response?.status === 403) {
|
||||
clearJellyseerrStorageData();
|
||||
}
|
||||
return Promise.reject(error);
|
||||
@@ -416,10 +417,8 @@ export class JellyseerrApi {
|
||||
|
||||
const jellyseerrUserAtom = atom(storage.get<JellyseerrUser>(JELLYSEERR_USER));
|
||||
|
||||
export const useJellyseerr = (
|
||||
settings: Settings = defaultValues,
|
||||
updateSettings: (update: Partial<Settings>) => void = () => {},
|
||||
) => {
|
||||
export const useJellyseerr = () => {
|
||||
const { settings, updateSettings } = useSettings();
|
||||
const [jellyseerrUser, setJellyseerrUser] = useAtom(jellyseerrUserAtom);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
@@ -468,54 +467,52 @@ export const useJellyseerr = (
|
||||
[jellyseerrApi],
|
||||
);
|
||||
|
||||
const isJellyseerrResult = (
|
||||
const isJellyseerrMovieOrTvResult = (
|
||||
items: any | null | undefined,
|
||||
): items is Results => {
|
||||
): items is MovieResult | TvResult => {
|
||||
return (
|
||||
items &&
|
||||
Object.hasOwn(items, "mediaType") &&
|
||||
Object.values(MediaType).includes(items.mediaType as MediaType)
|
||||
(items.mediaType === MediaType.MOVIE || items.mediaType === MediaType.TV)
|
||||
);
|
||||
};
|
||||
|
||||
const getTitle = (
|
||||
item?: TvResult | TvDetails | MovieResult | MovieDetails,
|
||||
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
||||
) => {
|
||||
return isJellyseerrResult(item)
|
||||
return isJellyseerrMovieOrTvResult(item)
|
||||
? item.mediaType === MediaType.MOVIE
|
||||
? item?.title
|
||||
: item?.name
|
||||
: item?.mediaInfo.mediaType === MediaType.MOVIE
|
||||
: item?.mediaInfo?.mediaType === MediaType.MOVIE
|
||||
? (item as MovieDetails)?.title
|
||||
: (item as TvDetails)?.name;
|
||||
};
|
||||
|
||||
const getYear = (
|
||||
item?: TvResult | TvDetails | MovieResult | MovieDetails,
|
||||
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
||||
) => {
|
||||
return new Date(
|
||||
(isJellyseerrResult(item)
|
||||
(isJellyseerrMovieOrTvResult(item)
|
||||
? item.mediaType === MediaType.MOVIE
|
||||
? item?.releaseDate
|
||||
: item?.firstAirDate
|
||||
: item?.mediaInfo.mediaType === MediaType.MOVIE
|
||||
: item?.mediaInfo?.mediaType === MediaType.MOVIE
|
||||
? (item as MovieDetails)?.releaseDate
|
||||
: (item as TvDetails)?.firstAirDate) || "",
|
||||
)?.getFullYear?.();
|
||||
};
|
||||
|
||||
const getMediaType = (
|
||||
item?: TvResult | TvDetails | MovieResult | MovieDetails,
|
||||
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
||||
): MediaType => {
|
||||
return isJellyseerrResult(item)
|
||||
? item.mediaType === "movie"
|
||||
? MediaType.MOVIE
|
||||
: MediaType.TV
|
||||
return isJellyseerrMovieOrTvResult(item)
|
||||
? (item.mediaType as MediaType)
|
||||
: item?.mediaInfo?.mediaType;
|
||||
};
|
||||
|
||||
const jellyseerrRegion = useMemo(
|
||||
() => jellyseerrUser?.settings?.region || "US",
|
||||
() => jellyseerrUser?.settings?.discoverRegion || "US",
|
||||
[jellyseerrUser],
|
||||
);
|
||||
|
||||
@@ -528,7 +525,7 @@ export const useJellyseerr = (
|
||||
jellyseerrUser,
|
||||
setJellyseerrUser,
|
||||
clearAllJellyseerData,
|
||||
isJellyseerrResult,
|
||||
isJellyseerrMovieOrTvResult,
|
||||
getTitle,
|
||||
getYear,
|
||||
getMediaType,
|
||||
|
||||
@@ -1,30 +1,58 @@
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { useAtom } from "jotai";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
|
||||
async function checkApiReachable(basePath?: string): Promise<boolean> {
|
||||
if (!basePath) return false;
|
||||
try {
|
||||
const response = await fetch(basePath, { method: "HEAD" });
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function useNetworkStatus() {
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [serverConnected, setServerConnected] = useState<boolean | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [api] = useAtom(apiAtom);
|
||||
|
||||
const validateConnection = useCallback(async () => {
|
||||
if (!api?.basePath) return false;
|
||||
const reachable = await checkApiReachable(api.basePath);
|
||||
setServerConnected(reachable);
|
||||
return reachable;
|
||||
}, [api?.basePath]);
|
||||
|
||||
// Manual check (optional)
|
||||
const retryCheck = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const state = await NetInfo.fetch();
|
||||
setIsConnected(!!state.isConnected && !!state.isInternetReachable);
|
||||
await validateConnection();
|
||||
setLoading(false);
|
||||
}, []);
|
||||
}, [validateConnection]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = NetInfo.addEventListener((state) => {
|
||||
setIsConnected(!!state.isConnected && !!state.isInternetReachable);
|
||||
const unsubscribe = NetInfo.addEventListener(async (state) => {
|
||||
setIsConnected(!!state.isConnected);
|
||||
if (state.isConnected) {
|
||||
await validateConnection();
|
||||
} else {
|
||||
setServerConnected(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial state
|
||||
// Initial check: wait for NetInfo first
|
||||
NetInfo.fetch().then((state) => {
|
||||
setIsConnected(!!state.isConnected && !!state.isInternetReachable);
|
||||
if (state.isConnected) {
|
||||
validateConnection();
|
||||
} else {
|
||||
setServerConnected(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => unsubscribe();
|
||||
}, []);
|
||||
}, [validateConnection]);
|
||||
|
||||
return { isConnected, loading, retryCheck };
|
||||
return { isConnected, serverConnected, loading, retryCheck };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
PlaybackProgressInfo,
|
||||
} from "@jellyfin/sdk/lib/generated-client";
|
||||
import { getPlaystateApi, getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useAtomValue } from "jotai";
|
||||
@@ -141,13 +144,10 @@ export const usePlaybackManager = ({
|
||||
* @param positionTicks The current playback position in ticks.
|
||||
*/
|
||||
const reportPlaybackProgress = async (
|
||||
itemId: string,
|
||||
positionTicks: number,
|
||||
metadata?: {
|
||||
AudioStreamIndex: number;
|
||||
SubtitleStreamIndex: number;
|
||||
},
|
||||
playbackProgressInfo: PlaybackProgressInfo,
|
||||
) => {
|
||||
const positionTicks = playbackProgressInfo.PositionTicks || 0;
|
||||
const itemId = playbackProgressInfo.ItemId!;
|
||||
const localItem = getDownloadedItemById(itemId);
|
||||
|
||||
// Handle local state update for downloaded items
|
||||
@@ -192,14 +192,7 @@ export const usePlaybackManager = ({
|
||||
if (isOnline && api) {
|
||||
try {
|
||||
await getPlaystateApi(api).reportPlaybackProgress({
|
||||
playbackProgressInfo: {
|
||||
ItemId: itemId,
|
||||
PositionTicks: Math.floor(positionTicks),
|
||||
...(metadata && { AudioStreamIndex: metadata.AudioStreamIndex }),
|
||||
...(metadata && {
|
||||
SubtitleStreamIndex: metadata.SubtitleStreamIndex,
|
||||
}),
|
||||
},
|
||||
playbackProgressInfo,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to report playback progress", error);
|
||||
|
||||
Reference in New Issue
Block a user