Adding a plethora of backend around retrieving album tracks, an artist's albums, images, and the player queue

This commit is contained in:
Violet Caulfield
2024-10-09 14:41:49 -05:00
parent aae1ae5b17
commit f7602c3f06
14 changed files with 164 additions and 6 deletions

View File

@@ -2,9 +2,7 @@ import { Jellyfin } from "@jellyfin/sdk"
import { useQuery } from "@tanstack/react-query";
import { getDeviceNameSync, getUniqueIdSync } from "react-native-device-info"
import { QueryKeys } from "../enums/query-keys";
import { useServerUrl } from "./queries/storage";
import { useCredentials } from "./queries/keychain";
import { SharedWebCredentials } from "react-native-keychain";
let clientName : string = require('root-require')('./package.json').name
let clientVersion : string = require('root-require')('./package.json').version

19
api/queries/albums.ts Normal file
View File

@@ -0,0 +1,19 @@
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api/items-api"
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { QueryKeys } from "../../enums/query-keys";
import { useApi } from "../queries";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { useChildrenFromParent } from "./items";
export const useArtistAlbums : (artistId: string) => UseQueryResult<BaseItemDto[], Error> = (artistId: string) => useQuery({
queryKey: [QueryKeys.ArtistAlbums, artistId],
queryFn: (({ queryKey }) => {
return getItemsApi(useApi.data!)
.getItems({ albumArtistIds: [queryKey[1]] })
.then((result) => {
return result.data.Items
});
})
});
export const useAlbumSongs : (albumId: string) => UseQueryResult<BaseItemDto[], Error> = (albumId: string) => useChildrenFromParent(QueryKeys.AlbumTracks, albumId);

View File

@@ -1,6 +1,5 @@
import { useQuery } from "@tanstack/react-query";
import { QueryKeys } from "../../enums/query-keys";
import { Api } from "@jellyfin/sdk";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api/items-api"
import { useApi } from "../queries";
@@ -9,6 +8,10 @@ export const useArtistById = (artistId: string) => useQuery({
queryKey: [QueryKeys.ArtistById, artistId],
queryFn: (({ queryKey }) => {
return getItemsApi(useApi.data!).getItems({ ids: [queryKey[1]]});
return getItemsApi(useApi.data!)
.getItems({ ids: [queryKey[1]]})
.then((result) => {
return result.data.Items
});
})
})

14
api/queries/image.ts Normal file
View File

@@ -0,0 +1,14 @@
import { useQuery } from "@tanstack/react-query";
import { QueryKeys } from "../../enums/query-keys";
import { Api } from "@jellyfin/sdk";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api/items-api"
import { useApi } from "../queries";
export const useImageByItemId = (artistId: string) => useQuery({
queryKey: [QueryKeys.ArtistById, artistId],
queryFn: (({ queryKey }) => {
return getItemsApi(useApi.data!).getItems({ ids: [queryKey[1]]});
})
})

18
api/queries/items.ts Normal file
View File

@@ -0,0 +1,18 @@
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api/items-api";
import { useQuery } from "@tanstack/react-query";
import { useApi } from "../queries";
import { QueryKeys } from "../../enums/query-keys";
export const useChildrenFromParent = (queryKey: QueryKeys, parentId: string) => useQuery({
queryKey: [queryKey, parentId],
queryFn: (({ queryKey }) => {
return getItemsApi(useApi.data!)
.getItems({ parentId: queryKey[1] })
.then((result) => {
// If our response is empty or null, return empty array
if (!!!result.data.Items)
return [];
return result.data.Items!
});
})
});

12
api/queries/playlist.ts Normal file
View File

@@ -0,0 +1,12 @@
import { useQuery } from "@tanstack/react-query";
import { QueryKeys } from "../../enums/query-keys";
import { getPlaylistsApi } from "@jellyfin/sdk/lib/utils/api/playlists-api"
import { useApi } from "../queries";
export const usePlaylists = useQuery({
queryKey: [QueryKeys.Playlists],
queryFn: () => {
return getPlaylistsApi(useApi.data!)
}
})

View File

@@ -1,9 +1,13 @@
import { Text, View } from "react-native";
import { useActiveTrack } from "react-native-track-player";
export default function Player(): React.JSX.Element {
let activeTrack = useActiveTrack()!;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30 }}>Player</Text>
<Text style={{ fontSize: 30 }}>{activeTrack.title ?? "Nothing playing"}</Text>
</View>
);
}

View File

@@ -1,12 +1,18 @@
export enum QueryKeys {
AddToQueue = "ADD_TO_QUEUE",
AlbumTracks = "ALBUM_TRACKS",
Api = "API",
ArtistAlbums = "ARTIST_ALBUMS",
ArtistById = "ARTIST_BY_ID",
Credentials = "CREDENTIALS",
Pause = "PAUSE",
Play = "PLAY",
Playlists = "PLAYLISTS",
Progress = "PROGRESS",
ReportPlaybackPosition = "REPORT_PLAYBACK_POSITION",
ReportPlaybackStarted = "REPORT_PLAYBACK_STARTED",
ReportPlaybackStopped = "REPORT_PLAYBACK_STOPPED",
ServerUrl = "SERVER_URL",
RemoveFromQueue = "REMOVE_FROM_QUEUE",
RemoveMultipleFromQueue = "REMOVE_MULTIPLE_FROM_QUEUE",
}

10
helpers/mappings.ts Normal file
View File

@@ -0,0 +1,10 @@
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"
import { Track } from "react-native-track-player"
import { JellifyTrack } from "../types/JellifyTrack"
import { useApi } from "../api/queries"
export function mapDtoToJellifyTrack(item: BaseItemDto) : JellifyTrack {
return {
url: `${useApi.data!.basePath}/Audio/${item.Id!}/universal`,
}
}

View File

@@ -1,9 +1,57 @@
import { BaseItemDto, SongInfo } from "@jellyfin/sdk/lib/generated-client/models";
import { useQuery } from "@tanstack/react-query";
import { removeUpcomingTracks } from "react-native-track-player/lib/src/trackPlayer";
import { add, remove, removeUpcomingTracks } from "react-native-track-player/lib/src/trackPlayer";
import { QueryKeys } from "../../enums/query-keys";
import { JellifyTrack } from "../../types/JellifyTrack";
import { mapDtoToJellifyTrack } from "../../helpers/mappings";
export const useClearQueue = useQuery({
queryKey: [],
queryFn: () => {
return removeUpcomingTracks();
}
})
/**
* Adds a song to the beginning of the queue
* @param song The song to play next
* @returns
*/
export const playNext = (song: BaseItemDto) => addToQueue(song, 1);
/**
*
* @param song The song to add to the queue
* @param index The index position to slot the song in, where "0" is the currently playing track. Defaults to the end of the queue
* @returns
*/
export const addToQueue = (song: BaseItemDto, index: number | undefined) => useQuery({
queryKey: [QueryKeys.AddToQueue, song.Id, index],
queryFn: () => {
return add(mapDtoToJellifyTrack(song), index)
}
})
/**
* Removes a singular song at the provided index from the queue
* @param index The index of the song to remove
* @returns
*/
export const removeFromQueue = (index: number) => useQuery({
queryKey: [QueryKeys.RemoveFromQueue, index],
queryFn: ({ queryKey }) => {
remove(index)
}
})
/**
* Removes multiple songs from the currently playing queue
* @param indexes The song indexes to remove from the queue
* @returns
*/
export const removeMultipleFromQueue = (indexes: number[]) => useQuery({
queryKey: [QueryKeys.RemoveMultipleFromQueue, indexes],
queryFn: ({ queryKey }) => {
remove(indexes);
}
})

0
types/JellifyAlbum.ts Normal file
View File

0
types/JellifyArtist.ts Normal file
View File

0
types/JellifyPlaylist.ts Normal file
View File

26
types/JellifyTrack.ts Normal file
View File

@@ -0,0 +1,26 @@
import { SongInfo } from "@jellyfin/sdk/lib/generated-client/models";
import { PitchAlgorithm, RatingType, Track, TrackType } from "react-native-track-player"
export interface JellifyTrack extends Track {
url: string;
type?: TrackType | undefined;
userAgent?: string | undefined;
contentType?: string | undefined;
pitchAlgorithm?: PitchAlgorithm | undefined;
headers?: { [key: string]: any; } | undefined;
title?: string | undefined;
album?: string | undefined;
artist?: string | undefined;
duration?: number | undefined;
artwork?: string | undefined;
description?: string | undefined;
genre?: string | undefined;
date?: string | undefined;
rating?: RatingType | undefined;
isLiveStream?: boolean | undefined;
Year?: number | null | undefined;
IndexNumber?: number | null | undefined;
ParentIndexNumber?: number | null | undefined;
}