From be867a3b103a442750e3dde8aab3e46348d53f46 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Fri, 11 Oct 2024 22:10:47 +0200 Subject: [PATCH] working --- .vscode/settings.json | 3 + app/(auth)/(tabs)/(home)/index.tsx | 495 ++++-------------- modules/vlc-player/ios/VlcPlayerModule.swift | 98 +++- modules/vlc-player/ios/VlcPlayerView.swift | 452 ++++++++++++++-- modules/vlc-player/src/VlcPlayer.types.ts | 88 +++- modules/vlc-player/src/VlcPlayerModule.web.ts | 13 - modules/vlc-player/src/VlcPlayerView.tsx | 125 ++++- modules/vlc-player/src/VlcPlayerView.web.tsx | 11 - 8 files changed, 773 insertions(+), 512 deletions(-) delete mode 100644 modules/vlc-player/src/VlcPlayerModule.web.ts delete mode 100644 modules/vlc-player/src/VlcPlayerView.web.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 2fe7c24f..22480b68 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,8 @@ "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true + }, + "[swift]": { + "editor.defaultFormatter": "sswg.swift-lang" } } diff --git a/app/(auth)/(tabs)/(home)/index.tsx b/app/(auth)/(tabs)/(home)/index.tsx index 31bff9b1..a4ddce6a 100644 --- a/app/(auth)/(tabs)/(home)/index.tsx +++ b/app/(auth)/(tabs)/(home)/index.tsx @@ -1,394 +1,52 @@ -import { Button } from "@/components/Button"; import { Text } from "@/components/common/Text"; -import { LargeMovieCarousel } from "@/components/home/LargeMovieCarousel"; -import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList"; -import { Loader } from "@/components/Loader"; -import { MediaListSection } from "@/components/medialists/MediaListSection"; -import { Colors } from "@/constants/Colors"; import { TAB_HEIGHT } from "@/constants/Values"; -import { hello, VlcPlayerView } from "@/modules/vlc-player"; -import { useDownload } from "@/providers/DownloadProvider"; -import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; -import { useSettings } from "@/utils/atoms/settings"; -import { Feather, Ionicons } from "@expo/vector-icons"; -import { Api } from "@jellyfin/sdk"; +import { VlcPlayerView } from "@/modules/vlc-player"; import { - BaseItemDto, - BaseItemKind, -} from "@jellyfin/sdk/lib/generated-client/models"; -import { - getItemsApi, - getSuggestionsApi, - getTvShowsApi, - getUserLibraryApi, - getUserViewsApi, -} from "@jellyfin/sdk/lib/utils/api"; -import NetInfo from "@react-native-community/netinfo"; -import { QueryFunction, useQuery, useQueryClient } from "@tanstack/react-query"; -import { useNavigation, useRouter } from "expo-router"; -import { useAtom, useAtomValue } from "jotai"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { - ActivityIndicator, - Platform, - RefreshControl, - ScrollView, - TouchableOpacity, - View, -} from "react-native"; + PlaybackStatePayload, + ProgressUpdatePayload, + VlcPlayerViewRef, +} from "@/modules/vlc-player/src/VlcPlayer.types"; +import React, { useEffect, useRef, useState } from "react"; +import { Button, ScrollView, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -type ScrollingCollectionListSection = { - type: "ScrollingCollectionList"; - title?: string; - queryKey: (string | undefined | null)[]; - queryFn: QueryFunction; - orientation?: "horizontal" | "vertical"; -}; - -type MediaListSection = { - type: "MediaListSection"; - queryKey: (string | undefined)[]; - queryFn: QueryFunction; -}; - -type Section = ScrollingCollectionListSection | MediaListSection; - export default function index() { - const queryClient = useQueryClient(); - const router = useRouter(); - - const api = useAtomValue(apiAtom); - const user = useAtomValue(userAtom); - - const [loading, setLoading] = useState(false); - const [settings, _] = useSettings(); - - const [isConnected, setIsConnected] = useState(null); - const [loadingRetry, setLoadingRetry] = useState(false); - - const { downloadedFiles } = useDownload(); - const navigation = useNavigation(); - - useEffect(() => { - const hasDownloads = downloadedFiles && downloadedFiles.length > 0; - navigation.setOptions({ - headerLeft: () => ( - { - router.push("/(auth)/downloads"); - }} - className="p-2" - > - - - ), - }); - }, [downloadedFiles, navigation, router]); - - const checkConnection = useCallback(async () => { - setLoadingRetry(true); - const state = await NetInfo.fetch(); - setIsConnected(state.isConnected); - setLoadingRetry(false); - }, []); - - useEffect(() => { - const unsubscribe = NetInfo.addEventListener((state) => { - if (state.isConnected == false || state.isInternetReachable === false) - setIsConnected(false); - else setIsConnected(true); - }); - - NetInfo.fetch().then((state) => { - setIsConnected(state.isConnected); - }); - - return () => { - unsubscribe(); - }; - }, []); - - const { - data: userViews, - isError: e1, - isLoading: l1, - } = useQuery({ - queryKey: ["home", "userViews", user?.Id], - queryFn: async () => { - if (!api || !user?.Id) { - return null; - } - - const response = await getUserViewsApi(api).getUserViews({ - userId: user.Id, - }); - - return response.data.Items || null; - }, - enabled: !!api && !!user?.Id, - staleTime: 60 * 1000, - }); - - const { - data: mediaListCollections, - isError: e2, - isLoading: l2, - } = useQuery({ - queryKey: ["home", "sf_promoted", user?.Id, settings?.usePopularPlugin], - queryFn: async () => { - if (!api || !user?.Id) return []; - - const response = await getItemsApi(api).getItems({ - userId: user.Id, - tags: ["sf_promoted"], - recursive: true, - fields: ["Tags"], - includeItemTypes: ["BoxSet"], - }); - - return response.data.Items || []; - }, - enabled: !!api && !!user?.Id && settings?.usePopularPlugin === true, - staleTime: 60 * 1000, - }); - - const collections = useMemo(() => { - const allow = ["movies", "tvshows"]; - return ( - userViews?.filter( - (c) => c.CollectionType && allow.includes(c.CollectionType) - ) || [] - ); - }, [userViews]); - - const refetch = useCallback(async () => { - setLoading(true); - await queryClient.invalidateQueries({ - queryKey: ["home"], - refetchType: "all", - type: "all", - exact: false, - }); - await queryClient.invalidateQueries({ - queryKey: ["home"], - refetchType: "all", - type: "all", - exact: false, - }); - await queryClient.invalidateQueries({ - queryKey: ["item"], - refetchType: "all", - type: "all", - exact: false, - }); - setLoading(false); - }, [queryClient]); - - const createCollectionConfig = useCallback( - ( - title: string, - queryKey: string[], - includeItemTypes: BaseItemKind[], - parentId: string | undefined - ): ScrollingCollectionListSection => ({ - title, - queryKey, - queryFn: async () => { - if (!api) return []; - return ( - ( - await getUserLibraryApi(api).getLatestMedia({ - userId: user?.Id, - limit: 50, - fields: ["PrimaryImageAspectRatio", "Path"], - imageTypeLimit: 1, - enableImageTypes: ["Primary", "Backdrop", "Thumb"], - includeItemTypes, - parentId, - }) - ).data || [] - ); - }, - type: "ScrollingCollectionList", - }), - [api, user?.Id] - ); - - const sections = useMemo(() => { - if (!api || !user?.Id) return []; - - const latestMediaViews = collections.map((c) => { - const includeItemTypes: BaseItemKind[] = - c.CollectionType === "tvshows" ? ["Series"] : ["Movie"]; - const title = "Recently Added in " + c.Name; - const queryKey = [ - "home", - "recentlyAddedIn" + c.CollectionType, - user?.Id!, - c.Id!, - ]; - return createCollectionConfig( - title || "", - queryKey, - includeItemTypes, - c.Id - ); - }); - - const ss: Section[] = [ - { - title: "Continue Watching", - queryKey: ["home", "resumeItems", user.Id], - queryFn: async () => - ( - await getItemsApi(api).getResumeItems({ - userId: user.Id, - enableImageTypes: ["Primary", "Backdrop", "Thumb"], - includeItemTypes: ["Movie", "Series", "Episode"], - }) - ).data.Items || [], - type: "ScrollingCollectionList", - orientation: "horizontal", - }, - { - title: "Next Up", - queryKey: ["home", "nextUp-all", user?.Id], - queryFn: async () => - ( - await getTvShowsApi(api).getNextUp({ - userId: user?.Id, - fields: ["MediaSourceCount"], - limit: 20, - enableImageTypes: ["Primary", "Backdrop", "Thumb"], - enableResumable: false, - }) - ).data.Items || [], - type: "ScrollingCollectionList", - orientation: "horizontal", - }, - ...latestMediaViews, - ...(mediaListCollections?.map( - (ml) => - ({ - title: ml.Name, - queryKey: ["home", "mediaList", ml.Id!], - queryFn: async () => ml, - type: "MediaListSection", - orientation: "vertical", - } as Section) - ) || []), - { - title: "Suggested Movies", - queryKey: ["home", "suggestedMovies", user?.Id], - queryFn: async () => - ( - await getSuggestionsApi(api).getSuggestions({ - userId: user?.Id, - limit: 10, - mediaType: ["Video"], - type: ["Movie"], - }) - ).data.Items || [], - type: "ScrollingCollectionList", - orientation: "vertical", - }, - { - title: "Suggested Episodes", - queryKey: ["home", "suggestedEpisodes", user?.Id], - queryFn: async () => { - try { - const suggestions = await getSuggestions(api, user.Id); - const nextUpPromises = suggestions.map((series) => - getNextUp(api, user.Id, series.Id) - ); - const nextUpResults = await Promise.all(nextUpPromises); - - return nextUpResults.filter((item) => item !== null) || []; - } catch (error) { - console.error("Error fetching data:", error); - return []; - } - }, - type: "ScrollingCollectionList", - orientation: "horizontal", - }, - ]; - return ss; - }, [api, user?.Id, collections, mediaListCollections]); - - if (isConnected === false) { - return ( - - No Internet - - No worries, you can still watch{"\n"}downloaded content. - - - - - - - ); - } - const insets = useSafeAreaInsets(); + const videoRef = useRef(null); + const [playbackState, setPlaybackState] = useState< + PlaybackStatePayload["nativeEvent"] | null + >(null); + const [progress, setProgress] = useState< + ProgressUpdatePayload["nativeEvent"] | null + >(null); - if (e1 || e2) - return ( - - Oops! - - Something went wrong.{"\n"}Please log out and in again. - - - ); + useEffect(() => { + videoRef.current?.play(); + }, []); - if (l1 || l2) - return ( - - - - ); + const onProgress = (event: ProgressUpdatePayload) => { + const { currentTime, duration } = event.nativeEvent; + console.log(`Current Time: ${currentTime}, Duration: ${duration}`); + setProgress(event.nativeEvent); + }; + + const onPlaybackStateChanged = (event: PlaybackStatePayload) => { + const { isBuffering, currentTime, duration, target, type } = + event.nativeEvent; + console.log("onVideoStateChange", { + isBuffering, + currentTime, + duration, + target, + type, + }); + setPlaybackState(event.nativeEvent); + }; return ( - } key={"home"} contentContainerStyle={{ paddingLeft: insets.left, @@ -400,36 +58,63 @@ export default function index() { }} > - {hello()} - + + +