From 886db2d904790d093283c820684b612c14a07b8d Mon Sep 17 00:00:00 2001 From: Nose Date: Wed, 22 Apr 2026 18:33:27 -0700 Subject: [PATCH] updated icons --- src/pages/Library.tsx | 69 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/pages/Library.tsx b/src/pages/Library.tsx index 276c6a2..b299da0 100644 --- a/src/pages/Library.tsx +++ b/src/pages/Library.tsx @@ -273,7 +273,9 @@ function getTrackFallbackArtwork(track?: Track) { } function getTrackArtworkSrc(track?: Track) { - if (track?.thumbnail && track.hasThumbnail !== false) return track.thumbnail + if (!track) return getTrackFallbackArtwork(track) + if (getTrackArtworkKind(track) === 'audio' && track.hasThumbnail !== true) return getTrackFallbackArtwork(track) + if (track.thumbnail && track.hasThumbnail !== false) return track.thumbnail return getTrackFallbackArtwork(track) } @@ -282,6 +284,14 @@ function getEntryThumbnail(track?: Track) { return track.hasThumbnail === false ? undefined : track.thumbnail } +function needsVisibleMediaInfoBackfill(track?: Track) { + if (!track?.serverId || track.fileId == null) return false + if (track.hasThumbnail === undefined) return true + if (!track.mimeType) return true + if (track.isVideo === undefined && getTrackArtworkKind(track) !== 'image') return true + return false +} + export default function Library({ mediaSection, onPlayNow, onDownloadTrack, isTrackDownloading, primaryTapAction, query, onQueryChange, displayModePreference }: Props) { const initialUiPreferences = useMemo(() => loadUiPreferences(), []) const initialSectionViews = useMemo(() => initialUiPreferences.librarySectionViews as Partial>, [initialUiPreferences]) @@ -303,6 +313,7 @@ export default function Library({ mediaSection, onPlayNow, onDownloadTrack, isTr const searchCacheRef = React.useRef>({}) const trackCacheRef = React.useRef>({}) const playMetadataAbortRef = useRef(null) + const visibleMediaInfoAbortRef = useRef(null) const persistTimeoutRef = useRef(null) const detailsAbortRef = useRef(null) const longPressTimerRef = useRef(null) @@ -839,6 +850,7 @@ export default function Library({ mediaSection, onPlayNow, onDownloadTrack, isTr useEffect(() => { return () => { try { playMetadataAbortRef.current?.abort() } catch {} + try { visibleMediaInfoAbortRef.current?.abort() } catch {} try { detailsAbortRef.current?.abort() } catch {} if (persistTimeoutRef.current && typeof window !== 'undefined') { window.clearTimeout(persistTimeoutRef.current) @@ -1063,6 +1075,61 @@ export default function Library({ mediaSection, onPlayNow, onDownloadTrack, isTr const visibleArtists = useMemo(() => sortedArtists.slice(0, visibleCount), [sortedArtists, visibleCount]) const showToolbarSortControls = effectiveDisplayMode === 'table' && isCompactTableLayout + const visibleRenderedTracks = useMemo(() => { + if (shouldShowGroupedArtists) return visibleArtistGroups.flatMap((group) => group.tracks) + return visibleResults + }, [shouldShowGroupedArtists, visibleArtistGroups, visibleResults]) + + useEffect(() => { + const candidates = visibleRenderedTracks.filter((track) => needsVisibleMediaInfoBackfill(track)) + if (candidates.length === 0) return + + try { visibleMediaInfoAbortRef.current?.abort() } catch {} + const controller = new AbortController() + visibleMediaInfoAbortRef.current = controller + + void (async () => { + const updatedTracks = new Map() + const concurrency = 4 + let index = 0 + + const workers = new Array(Math.min(concurrency, candidates.length)).fill(null).map(async () => { + while (index < candidates.length) { + const currentIndex = index + index += 1 + const track = candidates[currentIndex] + if (!track?.serverId || track.fileId == null) continue + + const server = servers.find((candidate) => candidate.id === track.serverId) + if (!server) continue + + try { + const mediaInfo = await new HydrusClient(server).getFileMediaInfo(track.fileId, controller.signal) + if (controller.signal.aborted) return + + const cacheKey = getTrackCacheKey(track.serverId, track.fileId) + if (!cacheKey) continue + + updatedTracks.set(cacheKey, { + ...track, + ...mediaInfo, + }) + } catch (error: any) { + if (error?.name === 'AbortError') return + } + } + }) + + await Promise.all(workers) + if (controller.signal.aborted || updatedTracks.size === 0) return + cacheTracks(Array.from(updatedTracks.values())) + })() + + return () => { + controller.abort() + } + }, [servers, visibleRenderedTracks]) + const canLoadMore = isTrackLikeView ? shouldShowGroupedArtists ? visibleCount < totalArtistGroupTracks