fix: retrieve codec metadata and lyrics asynchronously
This commit is contained in:
@@ -1,15 +1,61 @@
|
||||
import { createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit';
|
||||
import { Album, AlbumTrack, Playlist } from './types';
|
||||
import { AsyncThunkAPI } from '..';
|
||||
import { AsyncThunkPayloadCreator, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit';
|
||||
import { Album, AlbumTrack, CodecMetadata, Lyrics, Playlist } from './types';
|
||||
import type { AsyncThunkAPI } from '..';
|
||||
import { retrieveAllAlbums, retrieveRecentAlbums, retrieveAlbumTracks, retrieveAlbum, retrieveSimilarAlbums } from '@/utility/JellyfinApi/album';
|
||||
import { retrieveAllPlaylists, retrievePlaylistTracks } from '@/utility/JellyfinApi/playlist';
|
||||
import { searchItem } from '@/utility/JellyfinApi/search';
|
||||
import { retrieveTrackLyrics } from '@/utility/JellyfinApi/lyrics';
|
||||
import { retrieveTrackCodecMetadata } from '@/utility/JellyfinApi/track';
|
||||
|
||||
export const albumAdapter = createEntityAdapter<Album, string>({
|
||||
selectId: album => album.Id,
|
||||
sortComparer: (a, b) => a.Name.localeCompare(b.Name),
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetch lyrics for a given track
|
||||
*/
|
||||
export const fetchLyricsByTrack = createAsyncThunk<Lyrics, string, AsyncThunkAPI>(
|
||||
'/track/lyrics',
|
||||
retrieveTrackLyrics,
|
||||
);
|
||||
|
||||
/**
|
||||
* Fetch codec metadata for a given track
|
||||
*/
|
||||
export const fetchCodecMetadataByTrack = createAsyncThunk<CodecMetadata, string, AsyncThunkAPI>(
|
||||
'/track/codecMetadata',
|
||||
retrieveTrackCodecMetadata,
|
||||
);
|
||||
|
||||
/** A generic type for any action that retrieves tracks */
|
||||
type AlbumTrackPayloadCreator = AsyncThunkPayloadCreator<AlbumTrack[], string, AsyncThunkAPI>;
|
||||
|
||||
/**
|
||||
* This is a wrapper that postprocesses any tracks, so that we can also support
|
||||
* lyrics, codec metadata and potential other applications.
|
||||
*/
|
||||
export const postProcessTracks = function(creator: AlbumTrackPayloadCreator): AlbumTrackPayloadCreator {
|
||||
// Return a new payload creator
|
||||
return async (args, thunkAPI) => {
|
||||
// Retrieve the tracks using the original creator
|
||||
const tracks = await creator(args, thunkAPI);
|
||||
|
||||
// GUARD: Check if we've retrieved any tracks
|
||||
if (Array.isArray(tracks)) {
|
||||
// If so, attempt to retrieve lyrics for the tracks that have them
|
||||
tracks.filter((t) => t.HasLyrics)
|
||||
.forEach((t) => thunkAPI.dispatch(fetchLyricsByTrack(t.Id)));
|
||||
|
||||
// Also, retrieve codec metadata
|
||||
tracks.forEach((t) => thunkAPI.dispatch(fetchCodecMetadataByTrack(t.Id)));
|
||||
}
|
||||
|
||||
return tracks;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Fetch all albums available on the jellyfin server
|
||||
*/
|
||||
@@ -36,7 +82,7 @@ export const trackAdapter = createEntityAdapter<AlbumTrack, string>({
|
||||
*/
|
||||
export const fetchTracksByAlbum = createAsyncThunk<AlbumTrack[], string, AsyncThunkAPI>(
|
||||
'/tracks/byAlbum',
|
||||
retrieveAlbumTracks,
|
||||
postProcessTracks(retrieveAlbumTracks),
|
||||
);
|
||||
|
||||
export const fetchAlbum = createAsyncThunk<Album, string, AsyncThunkAPI>(
|
||||
@@ -100,5 +146,5 @@ export const fetchAllPlaylists = createAsyncThunk<Playlist[], undefined, AsyncTh
|
||||
*/
|
||||
export const fetchTracksByPlaylist = createAsyncThunk<AlbumTrack[], string, AsyncThunkAPI>(
|
||||
'/tracks/byPlaylist',
|
||||
retrievePlaylistTracks,
|
||||
);
|
||||
postProcessTracks(retrievePlaylistTracks),
|
||||
);
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
fetchAllPlaylists,
|
||||
fetchTracksByPlaylist,
|
||||
fetchAlbum,
|
||||
fetchSimilarAlbums
|
||||
fetchSimilarAlbums,
|
||||
fetchCodecMetadataByTrack,
|
||||
fetchLyricsByTrack
|
||||
} from './actions';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { Album, AlbumTrack, Playlist } from './types';
|
||||
@@ -162,6 +164,16 @@ const music = createSlice({
|
||||
|
||||
// Reset any caches we have when a new server is set
|
||||
builder.addCase(setJellyfinCredentials, () => initialState);
|
||||
|
||||
/**
|
||||
* Fetch track metadata
|
||||
*/
|
||||
builder.addCase(fetchCodecMetadataByTrack.fulfilled, (state, { payload, meta }) => {
|
||||
state.tracks.entities[meta.arg].Codec = payload;
|
||||
});
|
||||
builder.addCase(fetchLyricsByTrack.fulfilled, (state, { payload, meta }) => {
|
||||
state.tracks.entities[meta.arg].Lyrics = payload;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import {Lyrics} from '@/utility/JellyfinApi/lyrics.ts';
|
||||
|
||||
export interface UserData {
|
||||
PlaybackPositionTicks: number;
|
||||
PlayCount: number;
|
||||
@@ -72,6 +70,34 @@ export interface Album {
|
||||
PrimaryImageItemId?: string;
|
||||
}
|
||||
|
||||
export interface CodecMetadata {
|
||||
contentType?: string;
|
||||
isDirectPlay: boolean;
|
||||
}
|
||||
|
||||
export interface LyricMetadata {
|
||||
Artist: string
|
||||
Album: string
|
||||
Title: string
|
||||
Author: string
|
||||
Length: number
|
||||
By: string
|
||||
Offset: number
|
||||
Creator: string
|
||||
Version: string
|
||||
IsSynced: boolean
|
||||
}
|
||||
|
||||
export interface LyricData {
|
||||
Text: string
|
||||
Start: number
|
||||
}
|
||||
|
||||
export interface Lyrics {
|
||||
Metadata: LyricMetadata;
|
||||
Lyrics: LyricData[]
|
||||
}
|
||||
|
||||
export interface AlbumTrack {
|
||||
Name: string;
|
||||
ServerId: string;
|
||||
@@ -95,7 +121,8 @@ export interface AlbumTrack {
|
||||
LocationType: string;
|
||||
MediaType: string;
|
||||
HasLyrics: boolean;
|
||||
Lyrics: Lyrics | null;
|
||||
Lyrics?: Lyrics;
|
||||
Codec?: CodecMetadata;
|
||||
MediaStreams: MediaStream[];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user