mirror of
https://github.com/anultravioletaurora/Jellify.git
synced 2026-01-06 05:40:26 -06:00
Adding a plethora of backend around retrieving album tracks, an artist's albums, images, and the player queue
This commit is contained in:
@@ -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
19
api/queries/albums.ts
Normal 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);
|
||||
@@ -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
14
api/queries/image.ts
Normal 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
18
api/queries/items.ts
Normal 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
12
api/queries/playlist.ts
Normal 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!)
|
||||
}
|
||||
})
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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
10
helpers/mappings.ts
Normal 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`,
|
||||
}
|
||||
}
|
||||
@@ -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
0
types/JellifyAlbum.ts
Normal file
0
types/JellifyArtist.ts
Normal file
0
types/JellifyArtist.ts
Normal file
0
types/JellifyPlaylist.ts
Normal file
0
types/JellifyPlaylist.ts
Normal file
26
types/JellifyTrack.ts
Normal file
26
types/JellifyTrack.ts
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user