updated icons
This commit is contained in:
+68
-1
@@ -273,7 +273,9 @@ function getTrackFallbackArtwork(track?: Track) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTrackArtworkSrc(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)
|
return getTrackFallbackArtwork(track)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +284,14 @@ function getEntryThumbnail(track?: Track) {
|
|||||||
return track.hasThumbnail === false ? undefined : track.thumbnail
|
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) {
|
export default function Library({ mediaSection, onPlayNow, onDownloadTrack, isTrackDownloading, primaryTapAction, query, onQueryChange, displayModePreference }: Props) {
|
||||||
const initialUiPreferences = useMemo(() => loadUiPreferences(), [])
|
const initialUiPreferences = useMemo(() => loadUiPreferences(), [])
|
||||||
const initialSectionViews = useMemo(() => initialUiPreferences.librarySectionViews as Partial<Record<MediaSection, string>>, [initialUiPreferences])
|
const initialSectionViews = useMemo(() => initialUiPreferences.librarySectionViews as Partial<Record<MediaSection, string>>, [initialUiPreferences])
|
||||||
@@ -303,6 +313,7 @@ export default function Library({ mediaSection, onPlayNow, onDownloadTrack, isTr
|
|||||||
const searchCacheRef = React.useRef<Record<string, number[]>>({})
|
const searchCacheRef = React.useRef<Record<string, number[]>>({})
|
||||||
const trackCacheRef = React.useRef<Record<string, Track>>({})
|
const trackCacheRef = React.useRef<Record<string, Track>>({})
|
||||||
const playMetadataAbortRef = useRef<AbortController | null>(null)
|
const playMetadataAbortRef = useRef<AbortController | null>(null)
|
||||||
|
const visibleMediaInfoAbortRef = useRef<AbortController | null>(null)
|
||||||
const persistTimeoutRef = useRef<number | null>(null)
|
const persistTimeoutRef = useRef<number | null>(null)
|
||||||
const detailsAbortRef = useRef<AbortController | null>(null)
|
const detailsAbortRef = useRef<AbortController | null>(null)
|
||||||
const longPressTimerRef = useRef<number | null>(null)
|
const longPressTimerRef = useRef<number | null>(null)
|
||||||
@@ -839,6 +850,7 @@ export default function Library({ mediaSection, onPlayNow, onDownloadTrack, isTr
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
try { playMetadataAbortRef.current?.abort() } catch {}
|
try { playMetadataAbortRef.current?.abort() } catch {}
|
||||||
|
try { visibleMediaInfoAbortRef.current?.abort() } catch {}
|
||||||
try { detailsAbortRef.current?.abort() } catch {}
|
try { detailsAbortRef.current?.abort() } catch {}
|
||||||
if (persistTimeoutRef.current && typeof window !== 'undefined') {
|
if (persistTimeoutRef.current && typeof window !== 'undefined') {
|
||||||
window.clearTimeout(persistTimeoutRef.current)
|
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 visibleArtists = useMemo(() => sortedArtists.slice(0, visibleCount), [sortedArtists, visibleCount])
|
||||||
const showToolbarSortControls = effectiveDisplayMode === 'table' && isCompactTableLayout
|
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<string, Track>()
|
||||||
|
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
|
const canLoadMore = isTrackLikeView
|
||||||
? shouldShowGroupedArtists
|
? shouldShowGroupedArtists
|
||||||
? visibleCount < totalArtistGroupTracks
|
? visibleCount < totalArtistGroupTracks
|
||||||
|
|||||||
Reference in New Issue
Block a user