add playlist support on home screen

refactor album stuff so that it works for playlists since they're like the same
This commit is contained in:
Violet Caulfield
2025-01-10 05:11:19 -06:00
parent 6730eb4048
commit 841f9ab4cd
10 changed files with 141 additions and 39 deletions

View File

@@ -4,19 +4,3 @@ import { QueryKeys } from "../../enums/query-keys";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
import { ItemSortBy } from "@jellyfin/sdk/lib/generated-client/models";
export const useAlbumTracks = (albumId: string, api: Api) => useQuery({
queryKey: [QueryKeys.AlbumTracks, albumId, api],
queryFn: ({ queryKey }) => {
return getItemsApi(queryKey[2] as Api).getItems({
parentId: albumId,
sortBy: [
ItemSortBy.ParentIndexNumber,
ItemSortBy.IndexNumber,
ItemSortBy.SortName
]
})
.then((response) => {
return response.data.Items ? response.data.Items! : [];
})
}
})

View File

@@ -20,7 +20,35 @@ export function fetchUserPlaylists(api: Api, userId: string, playlistLibraryId:
})
.then((response) => {
if (response.data.Items)
resolve(response.data.Items)
resolve(response.data.Items.filter(playlist => playlist.Path?.includes("/config/data/playlists")))
else
resolve([]);
})
.catch((error) => {
console.error(error);
reject(error)
})
})
}
export function fetchPublicPlaylists(api: Api, playlistLibraryId: string): Promise<BaseItemDto[]> {
console.debug("Fetching public playlists");
return new Promise(async (resolve, reject) => {
getItemsApi(api)
.getItems({
parentId: playlistLibraryId,
sortBy: [
ItemSortBy.IsFolder,
ItemSortBy.SortName
],
sortOrder: [
SortOrder.Ascending
]
})
.then((response) => {
if (response.data.Items)
resolve(response.data.Items.filter(playlist => !playlist.Path?.includes("/config/data/playlists")))
else
resolve([]);
})

View File

@@ -12,4 +12,5 @@ export const useUserPlaylists = (api: Api, userId: string, playlistLibraryId: st
return fetchUserPlaylists(api, userId, playlistLibraryId);
}
})
})

26
api/queries/tracks.ts Normal file
View File

@@ -0,0 +1,26 @@
import { QueryKeys } from "@/enums/query-keys";
import { Api } from "@jellyfin/sdk";
import { ItemSortBy } from "@jellyfin/sdk/lib/generated-client/models/item-sort-by";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api/items-api";
import { useQuery } from "@tanstack/react-query";
export const useItemTracks = (itemId: string, api: Api) => useQuery({
queryKey: [QueryKeys.ItemTracks, itemId, api],
queryFn: ({ queryKey }) => {
const itemId : string = queryKey[1] as string;
const api : Api = queryKey[2] as Api;
return getItemsApi(api).getItems({
parentId: itemId,
sortBy: [
ItemSortBy.ParentIndexNumber,
ItemSortBy.IndexNumber,
ItemSortBy.SortName
]
})
.then((response) => {
return response.data.Items ? response.data.Items! : [];
})
}
})

View File

@@ -8,10 +8,10 @@ import { BaseItemDto, ImageType } from "@jellyfin/sdk/lib/generated-client/model
import { queryConfig } from "../../api/queries/query.config";
import { H4, H5, Text } from "../Global/helpers/text";
import { FlatList } from "react-native";
import { useAlbumTracks } from "../../api/queries/album";
import { usePlayerContext } from "../../player/provider";
import RunTimeTicks from "../Global/helpers/runtimeticks";
import Track from "../Global/components/track";
import { useItemTracks } from "@/api/queries/tracks";
interface AlbumProps {
album: BaseItemDto,
@@ -22,9 +22,9 @@ export default function Album(props: AlbumProps): React.JSX.Element {
const { apiClient, sessionId } = useApiClientContext();
const { resetQueue, addToQueue, play, nowPlaying } = usePlayerContext();
const { nowPlaying } = usePlayerContext();
const { data: tracks, isLoading } = useAlbumTracks(props.album.Id!, apiClient!);
const { data: tracks, isLoading } = useItemTracks(props.album.Id!, apiClient!);
return (
<ScrollView>

View File

@@ -29,7 +29,9 @@ export default function Artist(props: ArtistProps): React.JSX.Element {
return (
<SafeAreaView edges={["top", "right", "left"]}>
<ScrollView alignContent="center">
<ScrollView
contentInsetAdjustmentBehavior="automatic"
alignContent="center">
<CachedImage
source={getImageApi(apiClient!)
.getItemImageUrlById(

View File

@@ -27,23 +27,21 @@ export default function Track({
showArtwork?: boolean | undefined
}) : React.JSX.Element {
const { apiClient, sessionId } = useApiClientContext();
const { nowPlaying, playNewQueue } = usePlayerContext();
const { nowPlaying, resetQueue, addToQueue, play } = usePlayerContext();
const theme = useTheme();
let isPlaying = nowPlaying?.ItemId === track.Id
const isPlaying = nowPlaying?.ItemId === track.Id
return (
<View>
<Separator />
<XStack
flex={1}
onPress={async () => {
await resetQueue(false)
await addToQueue(tracklist.map((track) => mapDtoToTrack(apiClient!, sessionId, track)));
play(index);
onPress={() => {
playNewQueue.mutate({
track,
index,
tracklist
});
}}
paddingVertical={"$2"}
paddingHorizontal={"$1"}

View File

@@ -2,11 +2,12 @@ import { useUserPlaylists } from "@/api/queries/playlist";
import { Card } from "@/components/Global/helpers/card";
import { H2 } from "@/components/Global/helpers/text";
import { useApiClientContext } from "@/components/jellyfin-api-provider";
import { ProvidedHomeProps } from "@/components/types";
import React from "react";
import { FlatList } from "react-native";
import { ScrollView, View } from "tamagui";
import { View } from "tamagui";
export default function Playlists() : React.JSX.Element {
export default function Playlists({ navigation }: ProvidedHomeProps) : React.JSX.Element {
const { apiClient, user, library } = useApiClientContext();
@@ -23,7 +24,9 @@ export default function Playlists() : React.JSX.Element {
itemId={playlist.Id!}
caption={playlist.Name ?? "Untitled Playlist"}
onPress={() => {
navigation.navigate('Playlist', {
playlist
})
}} />
)
}} />

View File

@@ -1,7 +1,17 @@
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { BaseItemDto, ImageType } from "@jellyfin/sdk/lib/generated-client/models";
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { StackParamList } from "../types";
import { ScrollView } from "tamagui";
import { ScrollView, XStack, YStack } from "tamagui";
import { useApiClientContext } from "../jellyfin-api-provider";
import { usePlayerContext } from "@/player/provider";
import { useItemTracks } from "@/api/queries/tracks";
import RunTimeTicks from "../Global/helpers/runtimeticks";
import { H4, H5, Text } from "../Global/helpers/text";
import Track from "../Global/components/track";
import { FlatList } from "react-native";
import { queryConfig } from "@/api/queries/query.config";
import { getImageApi } from "@jellyfin/sdk/lib/utils/api/image-api";
import CachedImage from "@georstat/react-native-image-cache/lib/typescript/CachedImage";
interface PlaylistProps {
playlist: BaseItemDto;
@@ -9,9 +19,58 @@ interface PlaylistProps {
}
export default function Playlist(props: PlaylistProps): React.JSX.Element {
const { apiClient, sessionId } = useApiClientContext();
const { playNewQueue, nowPlaying } = usePlayerContext();
const { data: tracks, isLoading } = useItemTracks(props.playlist.Id!, apiClient!);
return (
<ScrollView>
</ScrollView>
<YStack alignItems="center">
<CachedImage
source={getImageApi(apiClient!)
.getItemImageUrlById(
props.playlist.Id!,
ImageType.Primary,
{ ...queryConfig.images})}
imageStyle={{
position: "relative",
width: 300,
height: 300,
borderRadius: 2
}}
/>
<H4>{ props.playlist.Name ?? "Untitled Playlist" }</H4>
<H5>{ props.playlist.ProductionYear?.toString() ?? "" }</H5>
</YStack>
<FlatList
data={tracks}
extraData={nowPlaying}
numColumns={1}
renderItem={({ item: track, index }) => {
return (
<Track
track={track}
tracklist={tracks!}
index={index}
/>
)
}}/>
<XStack justifyContent="flex-end">
<Text
color={"$gray10"}
style={{ display: "block"}}
>
Total Runtime:
</Text>
<RunTimeTicks>{ props.playlist.RunTimeTicks }</RunTimeTicks>
</XStack>
</ScrollView>
)
}

View File

@@ -28,4 +28,5 @@ export enum QueryKeys {
PlaybackStateChange = "PlaybackStateChange",
Player = "Player",
UserPlaylists = "UserPlaylists",
ItemTracks = "ItemTracks",
}