diff --git a/api/queries.ts b/api/queries.ts index 05482cfe..d285e17c 100644 --- a/api/queries.ts +++ b/api/queries.ts @@ -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 diff --git a/api/queries/albums.ts b/api/queries/albums.ts new file mode 100644 index 00000000..d495663d --- /dev/null +++ b/api/queries/albums.ts @@ -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 = (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 = (albumId: string) => useChildrenFromParent(QueryKeys.AlbumTracks, albumId); \ No newline at end of file diff --git a/api/queries/artists.ts b/api/queries/artists.ts index 0b351475..0539fdcf 100644 --- a/api/queries/artists.ts +++ b/api/queries/artists.ts @@ -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 + }); }) }) \ No newline at end of file diff --git a/api/queries/image.ts b/api/queries/image.ts new file mode 100644 index 00000000..d3e697c9 --- /dev/null +++ b/api/queries/image.ts @@ -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]]}); + }) +}) \ No newline at end of file diff --git a/api/queries/items.ts b/api/queries/items.ts new file mode 100644 index 00000000..21c194f9 --- /dev/null +++ b/api/queries/items.ts @@ -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! + }); + }) +}); \ No newline at end of file diff --git a/api/queries/playlist.ts b/api/queries/playlist.ts new file mode 100644 index 00000000..8d76c122 --- /dev/null +++ b/api/queries/playlist.ts @@ -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!) + } +}) \ No newline at end of file diff --git a/components/Player/component.tsx b/components/Player/component.tsx index c4d6a234..e1efa3ca 100644 --- a/components/Player/component.tsx +++ b/components/Player/component.tsx @@ -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 ( - Player + {activeTrack.title ?? "Nothing playing"} ); } \ No newline at end of file diff --git a/enums/query-keys.ts b/enums/query-keys.ts index fdb15f6c..11e2c473 100644 --- a/enums/query-keys.ts +++ b/enums/query-keys.ts @@ -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", } \ No newline at end of file diff --git a/helpers/mappings.ts b/helpers/mappings.ts new file mode 100644 index 00000000..4b5b89d6 --- /dev/null +++ b/helpers/mappings.ts @@ -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`, + } +} \ No newline at end of file diff --git a/player/queries/queue.ts b/player/queries/queue.ts index c0843673..95e7b04b 100644 --- a/player/queries/queue.ts +++ b/player/queries/queue.ts @@ -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); + } }) \ No newline at end of file diff --git a/types/JellifyAlbum.ts b/types/JellifyAlbum.ts new file mode 100644 index 00000000..e69de29b diff --git a/types/JellifyArtist.ts b/types/JellifyArtist.ts new file mode 100644 index 00000000..e69de29b diff --git a/types/JellifyPlaylist.ts b/types/JellifyPlaylist.ts new file mode 100644 index 00000000..e69de29b diff --git a/types/JellifyTrack.ts b/types/JellifyTrack.ts new file mode 100644 index 00000000..c83112c5 --- /dev/null +++ b/types/JellifyTrack.ts @@ -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; +} \ No newline at end of file