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.
9.0 kB · 194 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194import { Heading, Icon, Span, Stack, Text, Timeline} from '@chakra-ui/react';import React, { Fragment, useMemo } from "react";import { BsExclamationTriangle, BsSkipForward, BsStoplights } from "react-icons/bs";import { MdMusicNote } from "react-icons/md";import type {JsonPlayObject, LifecycleStep} from "../../core/Atomic";import { patchObject } from "../../core/DataUtils";import { timelineIconProps, timelineTextFormatting } from "../utils/ComponentUtils";import { ChakraCodeBlockShort } from "./CodeBlock";import { ErrorAlert } from "./ErrorAlert";import { MSCollapsible, type MSCollapsibleExternalProps } from "./MSCollapsible";import { PlayData } from "./PlayData";import { Muted } from "./Typography";import { JsonDiffPatch } from "./diffs/JsonDiff";
export interface LifeycleStepsTimelineProps extends MSCollapsibleExternalProps { steps: LifecycleStep[] original: JsonPlayObject}
export const diffElements = (original: JsonPlayObject, steps: LifecycleStep[]): [React.JSX.Element[], JsonPlayObject?] => {
const currentPlay: JsonPlayObject = structuredClone(original); // JSON.parse(JSON.stringify(original)); let patchFailed = false;
const diffElements: React.JSX.Element[] | null = []; let index = 0;
for (const step of steps) { index++; const { patch, error } = step;
if (patch === undefined) { if (error !== undefined) { diffElements.push(null); } else { diffElements.push(<Text>Play was identical after Transform.</Text>); } continue; }
if (patchFailed) { diffElements.push(<ChakraCodeBlockShort key={`diffblockfallback-${index}`} title="Diff Patch" code={patch} />); continue; } const left: JsonPlayObject = structuredClone(currentPlay); // JSON.parse(JSON.stringify(currentPlay)); left.data.meta = { ...(left.data.meta ?? {}), brainz: { ...(left.data.meta?.brainz ?? {}) } } currentPlay.data.meta = { ...(currentPlay.data.meta ?? {}), brainz: { ...(currentPlay.data.meta?.brainz ?? {}) } }
try { currentPlay.data = patchObject(currentPlay.data, patch)// jdiff.patch(currentPlay, patch) as JsonPlayObject; diffElements.push( <JsonDiffPatch left={left.data} right={currentPlay.data}/> ) } catch (e) { diffElements.push(<Fragment><ErrorAlert error={e} /><ChakraCodeBlockShort title="Diff Patch" key={`diffblockfallback-${index}`} code={patch} /></Fragment>); patchFailed = true; }
}
return [diffElements, patchFailed !== undefined ? currentPlay : undefined]}
export const TransformSteps = (props: LifeycleStepsTimelineProps) => { const { steps, original, collapsibleOpen } = props;
const [diffs, finalPlay] = useMemo(() => diffElements(original, steps), [steps, original]);
return ( <Timeline.Root variant="subtle" size="lg" css={{ "--timeline-separator-display": 'block' }}> {steps.map((x, index) => { const { patch, inputs, source, error, flowKnownState, flowReason, flowResult, stageName, stageType, hook } = x;
let timelineIcon: React.JSX.Element, iconProps: Record<string, any>, summary: React.JSX.Element, alertStatus: "error" | "info" | "warning" | "success" | "neutral"; if(error === undefined && error !== null) { timelineIcon = <BsStoplights/>; iconProps = flowResult === 'continue' ? {color: "green.focusRing"} : {color: "red.focusRing"}; summary = <Fragment><Muted>was</Muted> completed{flowResult === 'stop' ? <Fragment><Muted> and </Muted> stopped <Muted> due to onSuccess condition</Muted></Fragment> : null}</Fragment>; if(patch === undefined) { summary = <Fragment>{summary}<Muted> with</Muted> no change <Muted>to Play</Muted></Fragment>; } } else { if(flowKnownState === 'skip') { timelineIcon = <BsSkipForward/>; summary = <Fragment><Muted>was</Muted> skipped.</Fragment>; alertStatus = 'info'; } else if(flowKnownState === 'prereq') { timelineIcon = <BsExclamationTriangle/>; iconProps = {color: "orange.focusRing"}; summary = <Fragment><Muted>was</Muted> not completed <Muted>due to</Muted> prerequisite failure.</Fragment>; alertStatus = "warning"; } else { if(flowResult === 'continue') { timelineIcon = <BsStoplights/>; iconProps = {color: "orange.focusRing"}; summary = <Fragment>encountered an error <Muted>but</Muted> will continue <Muted>due to onFailure condition.</Muted></Fragment>; } else { timelineIcon = <BsExclamationTriangle/>; iconProps = {color: "red.focusRing"}; summary = <Fragment>encountered an error.</Fragment> } } } return <Timeline.Item key={index}> <Timeline.Connector> <Timeline.Separator /> <Timeline.Indicator> <Icon {...timelineIconProps} {...iconProps}> {timelineIcon} </Icon> </Timeline.Indicator> </Timeline.Connector> <Timeline.Content> <Timeline.Title> <MSCollapsible indicator={<Span {...timelineTextFormatting}><Span color="fg.muted">Stage </Span>{stageType}-{stageName}<Span color="fg.muted"> in Hook </Span>{hook} <Span color="fg.muted">from</Span> {source} {summary}</Span>} defaultOpen={collapsibleOpen} disableUntil="md" unmountOnExit timeline> {error !== undefined && error !== null ? <ErrorAlert status={alertStatus} error={error}/> : null} <Stack gap="2"> {diffs[index] !== null ? ( <Fragment> <Heading size="sm">Diff</Heading> {diffs[index]}</Fragment> ) : null} {inputs !== undefined && inputs.length > 0 ? ( <Fragment> <Heading size="sm">Inputs</Heading> <Stack gap="1"> {x.inputs.map((y, inputsIndex) => { return <ChakraCodeBlockShort key={`inputs-${inputsIndex}`} code={y.input} title={y.type} /> })} </Stack></Fragment>) : null} </Stack> </MSCollapsible> </Timeline.Title> </Timeline.Content> </Timeline.Item> })} {finalPlay !== undefined ? ( <Timeline.Item key="finalPlay" hideBelow="sm"> <Timeline.Connector> <Timeline.Separator /> <Timeline.Indicator> <Icon {...timelineIconProps}> <MdMusicNote /> </Icon> </Timeline.Indicator> </Timeline.Connector> <Timeline.Content> <Timeline.Title {...timelineTextFormatting}> Final Play<Span color="fg.muted">after all Transforms</Span> </Timeline.Title> <PlayData play={original} final={finalPlay} dates={false} compareDefault="Final" /> </Timeline.Content> </Timeline.Item> ) : null} </Timeline.Root> )}