mirror of
https://github.com/Jellify-Music/App.git
synced 2026-01-04 18:10:44 -06:00
ADd playlists to home screen
lots of backend player adjustments to get lastfm scrobbling
This commit is contained in:
32
api/queries/functions/playlists.ts
Normal file
32
api/queries/functions/playlists.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Api } from "@jellyfin/sdk";
|
||||
import { BaseItemDto, ItemSortBy, SortOrder } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
|
||||
export function fetchUserPlaylists(api: Api, userId: string, playlistLibraryId: string): Promise<BaseItemDto[]> {
|
||||
console.debug("Fetching user playlists");
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
getItemsApi(api)
|
||||
.getItems({
|
||||
userId: userId,
|
||||
parentId: playlistLibraryId,
|
||||
sortBy: [
|
||||
ItemSortBy.IsFolder,
|
||||
ItemSortBy.SortName
|
||||
],
|
||||
sortOrder: [
|
||||
SortOrder.Ascending
|
||||
]
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.data.Items)
|
||||
resolve(response.data.Items)
|
||||
else
|
||||
resolve([]);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -29,9 +29,9 @@ export function fetchRecentlyPlayed(api: Api, libraryId: string): Promise<BaseIt
|
||||
|
||||
if (response.data.Items)
|
||||
resolve(response.data.Items);
|
||||
else {
|
||||
else
|
||||
resolve([]);
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
reject(error);
|
||||
|
||||
15
api/queries/playlist.ts
Normal file
15
api/queries/playlist.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { QueryKeys } from "@/enums/query-keys";
|
||||
import { Api } from "@jellyfin/sdk";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { fetchUserPlaylists } from "./functions/playlists";
|
||||
|
||||
export const useUserPlaylists = (api: Api, userId: string, playlistLibraryId: string) => useQuery({
|
||||
queryKey: [QueryKeys.UserPlaylists, api, userId, playlistLibraryId],
|
||||
queryFn: ({ queryKey }) => {
|
||||
const api: Api = queryKey[1] as Api;
|
||||
const userId: string = queryKey[2] as string;
|
||||
const playlistLibraryId: string = queryKey[3] as string;
|
||||
|
||||
return fetchUserPlaylists(api, userId, playlistLibraryId);
|
||||
}
|
||||
})
|
||||
@@ -85,8 +85,13 @@ export default function Album(props: AlbumProps): React.JSX.Element {
|
||||
|
||||
}}/>
|
||||
|
||||
<XStack alignContent="flex-end">
|
||||
<Text style={{display: "block"}}>Total Runtime:</Text>
|
||||
<XStack justifyContent="flex-end">
|
||||
<Text
|
||||
color={"$gray10"}
|
||||
style={{ display: "block"}}
|
||||
>
|
||||
Total Runtime:
|
||||
</Text>
|
||||
<RunTimeTicks>{ props.album.RunTimeTicks }</RunTimeTicks>
|
||||
</XStack>
|
||||
</ScrollView>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { HomeArtistScreen } from "./screens/artist";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import Avatar from "../Global/avatar";
|
||||
import { HomeAlbumScreen } from "./screens/album";
|
||||
import Playlists from "./helpers/playlists";
|
||||
|
||||
export const HomeStack = createNativeStackNavigator<StackParamList>();
|
||||
|
||||
@@ -80,11 +81,18 @@ function ProvidedHome({ route, navigation }: ProvidedHomeProps): React.JSX.Eleme
|
||||
<YStack />
|
||||
<Avatar maxHeight={30} itemId={user!.id} />
|
||||
</XStack>
|
||||
|
||||
<Separator marginVertical={15} />
|
||||
|
||||
<RecentArtists route={route} navigation={navigation} />
|
||||
|
||||
<Separator marginVertical={15} />
|
||||
|
||||
<RecentlyPlayed />
|
||||
|
||||
<Separator marginVertical={15} />
|
||||
|
||||
<Playlists />
|
||||
</YStack>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
|
||||
32
components/Home/helpers/playlists.tsx
Normal file
32
components/Home/helpers/playlists.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { useUserPlaylists } from "@/api/queries/playlist";
|
||||
import { Card } from "@/components/Global/card";
|
||||
import { H2 } from "@/components/Global/text";
|
||||
import { useApiClientContext } from "@/components/jellyfin-api-provider";
|
||||
import React from "react";
|
||||
import { FlatList } from "react-native";
|
||||
import { ScrollView, View } from "tamagui";
|
||||
|
||||
export default function Playlists() : React.JSX.Element {
|
||||
|
||||
const { apiClient, user, library } = useApiClientContext();
|
||||
|
||||
const { data: playlists } = useUserPlaylists(apiClient!, user!.id, library!.playlistLibraryId);
|
||||
|
||||
return (
|
||||
<View>
|
||||
<H2>Your Playlists</H2>
|
||||
<FlatList horizontal
|
||||
data={playlists}
|
||||
renderItem={({ item: playlist }) => {
|
||||
return (
|
||||
<Card
|
||||
itemId={playlist.Id!}
|
||||
caption={playlist.Name ?? "Untitled Playlist"}
|
||||
onPress={() => {
|
||||
|
||||
}} />
|
||||
)
|
||||
}} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { H5, ScrollView, View } from "tamagui";
|
||||
import { ScrollView, View } from "tamagui";
|
||||
import { useHomeContext } from "../provider";
|
||||
import { H2, Text } from "../../Global/text";
|
||||
import { H2 } from "../../Global/text";
|
||||
import { Card } from "../../Global/card";
|
||||
import { mapDtoToTrack } from "../../../helpers/mappings";
|
||||
import { useApiClientContext } from "../../jellyfin-api-provider";
|
||||
|
||||
@@ -27,4 +27,5 @@ export enum QueryKeys {
|
||||
ArtistImage = "ArtistImage",
|
||||
PlaybackStateChange = "PlaybackStateChange",
|
||||
Player = "Player",
|
||||
UserPlaylists = "UserPlaylists",
|
||||
}
|
||||
@@ -1,8 +1,36 @@
|
||||
import { Progress } from "react-native-track-player";
|
||||
import { Progress, State } from "react-native-track-player";
|
||||
import { JellifyTrack } from "../types/JellifyTrack";
|
||||
import { PlaystateApi } from "@jellyfin/sdk/lib/generated-client/api/playstate-api";
|
||||
import { convertSecondsToRunTimeTicks } from "@/helpers/runtimeticks";
|
||||
|
||||
export async function handlePlaybackState(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack, state: State) {
|
||||
switch (state) {
|
||||
case (State.Playing) : {
|
||||
return playstateApi.reportPlaybackStart({
|
||||
playbackStartInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.ItemId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
case (State.Ended) :
|
||||
case (State.Paused) :
|
||||
case (State.Stopped) : {
|
||||
return playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.ItemId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
default : {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function handlePlaybackStopped(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack) {
|
||||
console.debug("Stopping playback for session");
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import _ from "lodash";
|
||||
import { buildNewQueue } from "./helpers/queue";
|
||||
import { useApiClientContext } from "../components/jellyfin-api-provider";
|
||||
import { getPlaystateApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { handlePlaybackProgressUpdated, handlePlaybackStarted, handlePlaybackStopped } from "./handlers";
|
||||
import { handlePlaybackProgressUpdated, handlePlaybackStarted, handlePlaybackState, handlePlaybackStopped } from "./handlers";
|
||||
import { useSetupPlayer } from "@/components/Player/hooks";
|
||||
import { UPDATE_INTERVAL } from "./config";
|
||||
import { sleep } from "@/helpers/sleep";
|
||||
@@ -85,14 +85,19 @@ const PlayerContextInitializer = () => {
|
||||
|
||||
useTrackPlayerEvents([
|
||||
Event.PlaybackProgressUpdated,
|
||||
Event.PlaybackState,
|
||||
Event.PlaybackActiveTrackChanged,
|
||||
], async (event) => {
|
||||
switch (event.type) {
|
||||
|
||||
case (Event.PlaybackState) : {
|
||||
handlePlaybackState(sessionId, playStateApi, nowPlaying!, event.state);
|
||||
break;
|
||||
|
||||
}
|
||||
case (Event.PlaybackProgressUpdated) : {
|
||||
if (event.position === event.duration - 10)
|
||||
handlePlaybackStopped(sessionId, playStateApi, nowPlaying!)
|
||||
else
|
||||
handlePlaybackProgressUpdated(sessionId, playStateApi, nowPlaying!, event)
|
||||
handlePlaybackProgressUpdated(sessionId, playStateApi, nowPlaying!, event);
|
||||
break;
|
||||
}
|
||||
|
||||
case (Event.PlaybackActiveTrackChanged) : {
|
||||
|
||||
Reference in New Issue
Block a user