fix: retrieve codec metadata and lyrics asynchronously

This commit is contained in:
Lei Nelissen
2024-10-25 00:25:01 +02:00
parent 4dd0d6e0e5
commit 77db5a51d2
9 changed files with 125 additions and 88 deletions

View File

@@ -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),
);

View File

@@ -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;
});
}
});

View File

@@ -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[];
}