Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
Something went wrong. Try again.
4.9 kB · 127 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127import { createQueryKeys, mergeQueryKeys } from "@lukemorales/query-key-factory";import { useQueryClient, hashKey, QueryObserver } from '@tanstack/react-query'import { useEffect, useState, useMemo } from 'react';import ky from 'ky';import qs from 'qs';import { baseUrl } from "../utils";import type {ComponentsApiJson, PaginatedResponse, PlayApiCommonDetailed, PlayStateUI, QueryPlaysOptsJson} from "../../core/Api";import { INGRESS_QUEUE, isPlayState, type SourcePlayerJson } from "../../core/Atomic";
export type QueryPlaysOptsJsonRefreshable = Omit<QueryPlaysOptsJson, 'state'> & {nonce?: string, state?: PlayStateUI[]};
const components = createQueryKeys('components', { list: () => ({ queryKey: ['components'], queryFn: (ctx) => { return ky.get(`components`, { baseUrl: baseUrl, }).json<ComponentsApiJson[]>() } }), single: (componentId: number) => ({ queryKey: ['components', componentId], queryFn: (ctx) => ky.get(`components/${componentId}`, { baseUrl }).json<ComponentsApiJson>() }), authUrl: (componentId: number) => ({ queryKey: ['components', componentId, 'auth'], queryFn: (ctx) => ky.get(`components/${componentId}/auth`, { baseUrl }).text() })})
const activities = createQueryKeys('activities', { list: (componentId: number, filters: QueryPlaysOptsJsonRefreshable) => ({ queryKey: ['components', componentId, 'plays', filters], queryFn: (ctx) => { const { nonce, state, ...rest } = filters; const derived: QueryPlaysOptsJson = rest; if(state !== undefined) { derived.state = state.filter(x => isPlayState(x));
if(state.includes('failed TBR') && state.includes('queued')) { derived.queues = [{queueName: INGRESS_QUEUE, queueStatus: 'queued'},{queueName: INGRESS_QUEUE, queueStatus: 'failed'}]; derived.state = Array.from(new Set([...derived.state, 'failed', 'queued'])); } else if(state.includes('failed TBR')) { derived.queues = [{queueName: INGRESS_QUEUE, queueStatus: 'failed'}]; derived.state = Array.from(new Set([...derived.state, 'failed'])); } } return ky.get(`components/${componentId}/plays`, { baseUrl: baseUrl, searchParams: qs.stringify({...derived, offset: ctx.pageParam}) }).json<PaginatedResponse<PlayApiCommonDetailed>>() } }), single: (componentId: number, activityUid: string) => ({ queryKey: ['components', componentId, 'play', activityUid], queryFn: (ctx) => ky.get(`components/${componentId}/plays/${activityUid}`, { baseUrl }).json<PlayApiCommonDetailed>() })})
const players = createQueryKeys('players', { list: (componentId: number) => ({ queryKey: ['components', componentId, 'players'], queryFn: (ctx) => { return ky.get(`components/${componentId}/players`, { baseUrl: baseUrl, }).json<Record<string, SourcePlayerJson>>() } }), single: (componentId: number, platformId: string) => ({ queryKey: ['components', componentId, 'play', platformId], queryFn: (ctx) => ky.get(`components/${componentId}/players/${platformId}`, { baseUrl }).json<SourcePlayerJson>() })});
const logs = createQueryKeys('logs', { list: (level: string, limit: number) => ({ queryKey: ['logs', {level, limit}], queryFn: (ctx) => { return ky.get(`logs`, { baseUrl: baseUrl }).json<{data: {line: string, time: number, levelLabel: string, level: number}[]}>(); } })})
export const tanQueries = mergeQueryKeys(components, activities, players, logs);
export const useQueryState = (queryKey: Readonly<unknown[]>) => { const queryClient = useQueryClient() const [state, setState] = useState(() => queryClient.getQueryState(queryKey))
useEffect(() => { const targetHash = hashKey(queryKey) return queryClient.getQueryCache().subscribe((event) => { if (event.query.queryHash === targetHash) { setState(event.query.state) } }) }, [queryClient, queryKey])
return state // { status, data, error, fetchStatus, ... }}
export const useQueryWatcher = <T>(queryKey: Readonly<unknown[]>) => { const queryClient = useQueryClient()
const observer = useMemo( () => new QueryObserver<T>(queryClient, { queryKey, enabled: false, // never triggers its own fetch }), [queryClient, queryKey] )
const [result, setResult] = useState(() => observer.getCurrentResult())
useEffect(() => { return observer.subscribe(setResult) }, [observer])
return result // { status, data, error, isPending, isSuccess, ... }}