diff --git a/app/(auth)/(tabs)/(home)/_layout.tsx b/app/(auth)/(tabs)/(home)/_layout.tsx
index 5d5054b0..430d4ba1 100644
--- a/app/(auth)/(tabs)/(home)/_layout.tsx
+++ b/app/(auth)/(tabs)/(home)/_layout.tsx
@@ -1,4 +1,4 @@
-import { Chromecast } from "@/components/Chromecast";
+const Chromecast = !Platform.isTV ? require("@/components/Chromecast") : null;
import { Text } from "@/components/common/Text";
import { nestedTabPageScreenOptions } from "@/components/stacks/NestedTabPageStack";
import { Feather } from "@expo/vector-icons";
diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/albums/[albumId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/albums/[albumId].tsx
deleted file mode 100644
index de7d10e7..00000000
--- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/albums/[albumId].tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import { Chromecast } from "@/components/Chromecast";
-import { ItemImage } from "@/components/common/ItemImage";
-import { Text } from "@/components/common/Text";
-import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
-import { SongsList } from "@/components/music/SongsList";
-import { ParallaxScrollView } from "@/components/ParallaxPage";
-import ArtistPoster from "@/components/posters/ArtistPoster";
-import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
-import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
-import { useQuery } from "@tanstack/react-query";
-import { router, useLocalSearchParams, useNavigation } from "expo-router";
-import { useAtom } from "jotai";
-import { useEffect, useState } from "react";
-import { Platform, ScrollView, TouchableOpacity, View } from "react-native";
-import { useSafeAreaInsets } from "react-native-safe-area-context";
-
-export default function page() {
- const searchParams = useLocalSearchParams();
- const { collectionId, artistId, albumId } = searchParams as {
- collectionId: string;
- artistId: string;
- albumId: string;
- };
-
- const [api] = useAtom(apiAtom);
- const [user] = useAtom(userAtom);
-
- const navigation = useNavigation();
-
- if (!Platform.isTV) {
- useEffect(() => {
- navigation.setOptions({
- headerRight: () => (
-
-
-
- ),
- });
- });
- }
-
- const { data: album } = useQuery({
- queryKey: ["album", albumId, artistId],
- queryFn: async () => {
- if (!api) return null;
- const response = await getItemsApi(api).getItems({
- userId: user?.Id,
- ids: [albumId],
- });
- const data = response.data.Items?.[0];
- return data;
- },
- enabled: !!api && !!user?.Id && !!albumId,
- staleTime: 0,
- });
-
- const {
- data: songs,
- isLoading,
- isError,
- } = useQuery<{
- Items: BaseItemDto[];
- TotalRecordCount: number;
- }>({
- queryKey: ["songs", artistId, albumId],
- queryFn: async () => {
- if (!api)
- return {
- Items: [],
- TotalRecordCount: 0,
- };
-
- const response = await getItemsApi(api).getItems({
- userId: user?.Id,
- parentId: albumId,
- fields: [
- "ItemCounts",
- "PrimaryImageAspectRatio",
- "CanDelete",
- "MediaSourceCount",
- ],
- sortBy: ["ParentIndexNumber", "IndexNumber", "SortName"],
- });
-
- const data = response.data.Items;
-
- return {
- Items: data || [],
- TotalRecordCount: response.data.TotalRecordCount || 0,
- };
- },
- enabled: !!api && !!user?.Id,
- });
-
- const insets = useSafeAreaInsets();
-
- if (!album) return null;
-
- return (
-
- }
- >
-
- {album?.Name}
-
- {songs?.TotalRecordCount} songs
-
-
-
-
-
-
- );
-}
diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/[artistId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/[artistId].tsx
deleted file mode 100644
index 8d82d205..00000000
--- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/[artistId].tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import ArtistPoster from "@/components/posters/ArtistPoster";
-import { Text } from "@/components/common/Text";
-import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
-import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
-import { useQuery } from "@tanstack/react-query";
-import { router, useLocalSearchParams, useNavigation } from "expo-router";
-import { useAtom } from "jotai";
-import { useEffect, useState } from "react";
-import { FlatList, ScrollView, TouchableOpacity, View } from "react-native";
-import { useSafeAreaInsets } from "react-native-safe-area-context";
-import { ItemImage } from "@/components/common/ItemImage";
-import { ParallaxScrollView } from "@/components/ParallaxPage";
-import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
-
-export default function page() {
- const searchParams = useLocalSearchParams();
- const { artistId } = searchParams as {
- artistId: string;
- };
-
- const [api] = useAtom(apiAtom);
- const [user] = useAtom(userAtom);
-
- const navigation = useNavigation();
-
- const [startIndex, setStartIndex] = useState(0);
-
- const { data: artist } = useQuery({
- queryKey: ["album", artistId],
- queryFn: async () => {
- if (!api) return null;
- const response = await getItemsApi(api).getItems({
- userId: user?.Id,
- ids: [artistId],
- });
- const data = response.data.Items?.[0];
- return data;
- },
- enabled: !!api && !!user?.Id && !!artistId,
- staleTime: 0,
- });
-
- const {
- data: albums,
- isLoading,
- isError,
- } = useQuery<{
- Items: BaseItemDto[];
- TotalRecordCount: number;
- }>({
- queryKey: ["albums", artistId, startIndex],
- queryFn: async () => {
- if (!api)
- return {
- Items: [],
- TotalRecordCount: 0,
- };
-
- const response = await getItemsApi(api).getItems({
- userId: user?.Id,
- parentId: artistId,
- sortOrder: ["Descending", "Descending", "Ascending"],
- includeItemTypes: ["MusicAlbum"],
- recursive: true,
- fields: [
- "ParentId",
- "PrimaryImageAspectRatio",
- "ParentId",
- "PrimaryImageAspectRatio",
- ],
- collapseBoxSetItems: false,
- albumArtistIds: [artistId],
- startIndex,
- limit: 100,
- sortBy: ["PremiereDate", "ProductionYear", "SortName"],
- });
-
- const data = response.data.Items;
-
- return {
- Items: data || [],
- TotalRecordCount: response.data.TotalRecordCount || 0,
- };
- },
- enabled: !!api && !!user?.Id,
- });
-
- const insets = useSafeAreaInsets();
-
- if (!artist || !albums) return null;
-
- return (
-
- }
- >
-
- {artist?.Name}
-
- {albums.TotalRecordCount} albums
-
-
-
- {albums.Items.map((item, idx) => (
-
-
-
- {item.Name}
- {item.ProductionYear}
-
-
- ))}
-
-
- );
-}
diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/index.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/index.tsx
deleted file mode 100644
index 4827287e..00000000
--- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/index.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import { Text } from "@/components/common/Text";
-import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
-import ArtistPoster from "@/components/posters/ArtistPoster";
-import MoviePoster from "@/components/posters/MoviePoster";
-import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
-import { getArtistsApi, getItemsApi } from "@jellyfin/sdk/lib/utils/api";
-import { useQuery } from "@tanstack/react-query";
-import { router, useLocalSearchParams } from "expo-router";
-import { useAtom } from "jotai";
-import { useMemo, useState } from "react";
-import { FlatList, TouchableOpacity, View } from "react-native";
-
-export default function page() {
- const searchParams = useLocalSearchParams();
- const { collectionId } = searchParams as { collectionId: string };
-
- const [api] = useAtom(apiAtom);
- const [user] = useAtom(userAtom);
-
- const { data: collection } = useQuery({
- queryKey: ["collection", collectionId],
- queryFn: async () => {
- if (!api) return null;
- const response = await getItemsApi(api).getItems({
- userId: user?.Id,
- ids: [collectionId],
- });
- const data = response.data.Items?.[0];
- return data;
- },
- enabled: !!api && !!user?.Id && !!collectionId,
- staleTime: 0,
- });
-
- const [startIndex, setStartIndex] = useState(0);
-
- const { data, isLoading, isError } = useQuery<{
- Items: BaseItemDto[];
- TotalRecordCount: number;
- }>({
- queryKey: ["collection-items", collection?.Id, startIndex],
- queryFn: async () => {
- if (!api || !collectionId)
- return {
- Items: [],
- TotalRecordCount: 0,
- };
-
- const response = await getArtistsApi(api).getArtists({
- sortBy: ["SortName"],
- sortOrder: ["Ascending"],
- fields: ["PrimaryImageAspectRatio", "SortName"],
- imageTypeLimit: 1,
- enableImageTypes: ["Primary", "Backdrop", "Banner", "Thumb"],
- parentId: collectionId,
- userId: user?.Id,
- });
-
- const data = response.data.Items;
-
- return {
- Items: data || [],
- TotalRecordCount: response.data.TotalRecordCount || 0,
- };
- },
- enabled: !!collection?.Id && !!api && !!user?.Id,
- });
-
- const totalItems = useMemo(() => {
- return data?.TotalRecordCount;
- }, [data]);
-
- if (!data) return null;
-
- return (
-
- Artists
-
- }
- nestedScrollEnabled
- data={data.Items}
- numColumns={3}
- columnWrapperStyle={{
- justifyContent: "space-between",
- }}
- renderItem={({ item, index }) => (
-
-
- {collection?.CollectionType === "movies" && (
-
- )}
- {collection?.CollectionType === "music" && (
-
- )}
- {item.Name}
- {item.ProductionYear}
-
-
- )}
- keyExtractor={(item) => item.Id || ""}
- />
- );
-}
diff --git a/app/(auth)/(tabs)/(search)/index.tsx b/app/(auth)/(tabs)/(search)/index.tsx
index 1ae0059c..373a1fbf 100644
--- a/app/(auth)/(tabs)/(search)/index.tsx
+++ b/app/(auth)/(tabs)/(search)/index.tsx
@@ -441,48 +441,6 @@ export default function search() {
)}
/>
- m.Id!)}
- header="Artists"
- renderItem={(item: BaseItemDto) => (
-
-
-
-
- )}
- />
- m.Id!)}
- header="Albums"
- renderItem={(item: BaseItemDto) => (
-
-
-
-
- )}
- />
- m.Id!)}
- header="Songs"
- renderItem={(item: BaseItemDto) => (
-
-
-
-
- )}
- />
>
)}
{searchType === "Discover" && (
diff --git a/components/Chromecast.tv.tsx b/components/Chromecast.tv.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/components/ItemContent.tsx b/components/ItemContent.tsx
index f1f3bcde..c59ccdfb 100644
--- a/components/ItemContent.tsx
+++ b/components/ItemContent.tsx
@@ -3,7 +3,7 @@ import { Bitrate, BitrateSelector } from "@/components/BitrateSelector";
import { DownloadSingleItem } from "@/components/DownloadItem";
import { OverviewText } from "@/components/OverviewText";
import { ParallaxScrollView } from "@/components/ParallaxPage";
-import { PlayButton } from "@/components/PlayButton";
+const PlayButton = !Platform.isTV ? require("@/components/PlayButton") : null;
import { PlayedStatus } from "@/components/PlayedStatus";
import { SimilarItems } from "@/components/SimilarItems";
import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
@@ -29,7 +29,7 @@ import { useAtom } from "jotai";
import React, { useEffect, useMemo, useState } from "react";
import { Platform, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
-import { Chromecast } from "./Chromecast";
+const Chromecast = !Platform.isTV ? require("./Chromecast") : null;
import { ItemHeader } from "./ItemHeader";
import { ItemTechnicalDetails } from "./ItemTechnicalDetails";
import { MediaSourceSelector } from "./MediaSourceSelector";
@@ -248,12 +248,13 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
/>
)}
-
-
+ {!Platform.isTV && (
+
+ )}
{item.Type === "Episode" && (
diff --git a/components/PlayButton.tsx b/components/PlayButton.tsx
index 27a9021e..9a403be0 100644
--- a/components/PlayButton.tsx
+++ b/components/PlayButton.tsx
@@ -1,3 +1,4 @@
+import { Platform } from "react-native";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
import { useSettings } from "@/utils/atoms/settings";
@@ -31,7 +32,9 @@ import Animated, {
} from "react-native-reanimated";
import { Button } from "./Button";
import { SelectedOptions } from "./ItemContent";
-import { chromecastProfile } from "@/utils/profiles/chromecast";
+const chromecastProfile = !Platform.isTV
+ ? require("@/utils/profiles/chromecast")
+ : null;
import * as Haptics from "@/packages/expo-haptics";
interface Props extends React.ComponentProps {
diff --git a/components/common/TouchableItemRouter.tsx b/components/common/TouchableItemRouter.tsx
index b1b886bd..cb6dff12 100644
--- a/components/common/TouchableItemRouter.tsx
+++ b/components/common/TouchableItemRouter.tsx
@@ -26,18 +26,6 @@ export const itemRouter = (
return `/(auth)/(tabs)/${from}/series/${item.Id}`;
}
- if (item.Type === "MusicAlbum") {
- return `/(auth)/(tabs)/${from}/albums/${item.Id}`;
- }
-
- if (item.Type === "Audio") {
- return `/(auth)/(tabs)/${from}/albums/${item.AlbumId}`;
- }
-
- if (item.Type === "MusicArtist") {
- return `/(auth)/(tabs)/${from}/artists/${item.Id}`;
- }
-
if (item.Type === "Person" || item.Type === "Actor") {
return `/(auth)/(tabs)/${from}/actors/${item.Id}`;
}
diff --git a/components/stacks/NestedTabPageStack.tsx b/components/stacks/NestedTabPageStack.tsx
index 024b1272..2cfeed1d 100644
--- a/components/stacks/NestedTabPageStack.tsx
+++ b/components/stacks/NestedTabPageStack.tsx
@@ -17,14 +17,7 @@ export const commonScreenOptions: ICommonScreenOptions = {
headerLeft: () => ,
};
-const routes = [
- "actors/[actorId]",
- "albums/[albumId]",
- "artists/index",
- "artists/[artistId]",
- "items/page",
- "series/[id]",
-];
+const routes = ["actors/[actorId]", "items/page", "series/[id]"];
export const nestedTabPageScreenOptions: Record =
Object.fromEntries(routes.map((route) => [route, commonScreenOptions]));
diff --git a/utils/jellyseerr b/utils/jellyseerr
index e69d160e..4401b164 160000
--- a/utils/jellyseerr
+++ b/utils/jellyseerr
@@ -1 +1 @@
-Subproject commit e69d160e25f0962cd77b01c861ce248050e1ad38
+Subproject commit 4401b16414af604a7372dacac326c38b18ad8555