From e840cbd2c91b0b30f82e9ef56e912b007ebb4542 Mon Sep 17 00:00:00 2001 From: truewhile <779943132@qq.com> Date: Fri, 11 Sep 2026 15:35:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/local_metadata_artwork.go | 9 +++-- internal/service/organizer_sidecar.go | 6 +++- .../service/scraper_write_metadata_files.go | 11 ++++-- internal/service/strm_sync_test.go | 4 +++ web/src/components/LayoutUserMenu.tsx | 4 ++- .../components/ManageLibrariesDialogView.tsx | 34 ++++++++++++++++++ web/src/components/PlayerControls.tsx | 15 ++++---- web/src/components/manageLibrariesDialog.tsx | 36 ++----------------- web/src/hooks/useWebSocket.ts | 5 ++- web/src/pages/AdminLibraryTable.tsx | 12 +++---- web/src/pages/EmbyMountPage.tsx | 1 - web/src/pages/LibraryStorageStats.tsx | 4 +-- web/src/pages/PlayerPage.tsx | 11 ++++-- web/src/pages/ScraperQueuePage.tsx | 8 ++--- web/src/pages/StrmQueuePage.tsx | 8 ++--- .../pages/scraper-queue/ScrapeDetailModal.tsx | 16 +-------- web/src/pages/scraper-queue/scrapeLabels.ts | 14 ++++++++ web/src/pages/strm-queue/TaskDetailModal.tsx | 21 ++--------- web/src/pages/strm-queue/taskFileIcon.tsx | 19 ++++++++++ web/src/pages/useAdminLibraryPanel.ts | 2 +- 20 files changed, 135 insertions(+), 105 deletions(-) create mode 100644 web/src/components/ManageLibrariesDialogView.tsx create mode 100644 web/src/pages/scraper-queue/scrapeLabels.ts create mode 100644 web/src/pages/strm-queue/taskFileIcon.tsx diff --git a/internal/service/local_metadata_artwork.go b/internal/service/local_metadata_artwork.go index 84a79fc..51f692a 100644 --- a/internal/service/local_metadata_artwork.go +++ b/internal/service/local_metadata_artwork.go @@ -74,12 +74,15 @@ func localPosterCandidates(mediaPath string) []string { add(base + "-cover") add(base + ".cover") add(base) + } + for _, name := range []string{"poster", "folder", "cover", "movie", "show"} { + add(name) + } + for _, base := range mediaSidecarBaseVariants(mediaPath) { add(base + "-thumb") add(base + ".thumb") } - for _, name := range []string{"poster", "folder", "cover", "movie", "show", "thumb"} { - add(name) - } + add("thumb") return append(adultArtworkNameCandidates(mediaPath, "poster"), names...) } diff --git a/internal/service/organizer_sidecar.go b/internal/service/organizer_sidecar.go index 8b1f153..99854f3 100644 --- a/internal/service/organizer_sidecar.go +++ b/internal/service/organizer_sidecar.go @@ -84,6 +84,7 @@ func transferSidecarArtwork(srcMedia, dstMedia string, mode TransferMode) error if dstBase == "" { dstBase = strings.TrimSuffix(filepath.Base(dstMedia), filepath.Ext(dstMedia)) } + srcAdultCode := AdultCodeFromMediaPath(srcMedia) for _, src := range sources { name := filepath.Base(src) // Remap any legacy "Title.mkv-poster.jpg" onto the shared destination stem. @@ -97,7 +98,10 @@ func transferSidecarArtwork(srcMedia, dstMedia string, mode TransferMode) error } } dstName := name - if suffix != "" && dstBase != "" { + if srcAdultCode != "" && strings.HasPrefix(strings.ToLower(name), strings.ToLower(srcAdultCode)) { + // Adult sidecars are keyed by code (e.g. ADN-188-poster.jpg) and should + // follow the media without being renamed to the display title stem. + } else if suffix != "" && dstBase != "" { dstName = dstBase + suffix } dst := filepath.Join(dstDir, dstName) diff --git a/internal/service/scraper_write_metadata_files.go b/internal/service/scraper_write_metadata_files.go index ecf536a..0761cfa 100644 --- a/internal/service/scraper_write_metadata_files.go +++ b/internal/service/scraper_write_metadata_files.go @@ -46,14 +46,21 @@ func (s *ScraperService) writeMediaArtworkFilesAfterScrape(ctx context.Context, } isAdult := shouldCropAdultPoster(refreshed, lib) artworkUpdates := map[string]any{} + shouldPersistLocalArtworkURL := func(raw string) bool { + return isAdult || !isHTTPish(raw) + } if refreshed.PosterURL != "" { if dst := s.downloadArtworkToPathWithOptions(ctx, dir, base+"-poster", refreshed.PosterURL, isAdult); dst != "" { - artworkUpdates["poster_url"] = filepath.Join(filepath.Dir(refreshed.Path), filepath.Base(dst)) + if shouldPersistLocalArtworkURL(refreshed.PosterURL) { + artworkUpdates["poster_url"] = filepath.Join(filepath.Dir(refreshed.Path), filepath.Base(dst)) + } } } if refreshed.BackdropURL != "" { if dst := s.downloadArtworkToPathWithOptions(ctx, dir, base+"-backdrop", refreshed.BackdropURL, false); dst != "" { - artworkUpdates["backdrop_url"] = filepath.Join(filepath.Dir(refreshed.Path), filepath.Base(dst)) + if shouldPersistLocalArtworkURL(refreshed.BackdropURL) { + artworkUpdates["backdrop_url"] = filepath.Join(filepath.Dir(refreshed.Path), filepath.Base(dst)) + } } } else if isAdult && refreshed.PosterURL != "" { // 番号海报原图为完整封套横图,在无独立背景图时直接作为背景图写出 diff --git a/internal/service/strm_sync_test.go b/internal/service/strm_sync_test.go index 51b50b8..6bbd5a1 100644 --- a/internal/service/strm_sync_test.go +++ b/internal/service/strm_sync_test.go @@ -825,6 +825,7 @@ func TestWalkRemoteConcurrent(t *testing.T) { if err := st.walkRemote(); err != nil { t.Fatalf("walkRemote failed: %v", err) } + st.flushPreferredVideos() for _, dir := range []string{"root", "a", "a1", "b"} { if provider.listed[dir] != 1 { @@ -1113,6 +1114,7 @@ func TestWalk115FlatConcurrentProcessing(t *testing.T) { if err := st.walk115Flat(oc); err != nil { t.Fatalf("walk115Flat: %v", err) } + st.flushPreferredVideos() // 3 个视频的 strm 全部生成 if st.rec.NewStrm != 3 { @@ -1168,6 +1170,7 @@ func TestWalk115FlatConcurrentProcessing(t *testing.T) { if err := st2.walk115Flat(oc); err != nil { t.Fatalf("incremental walk115Flat: %v", err) } + st2.flushPreferredVideos() if infoCalls != 0 { t.Fatalf("incremental sync should reuse dir cache, got %d get_info calls", infoCalls) } @@ -1461,6 +1464,7 @@ func TestWalk115AdaptiveHierarchicalFlatScan(t *testing.T) { if err := st.walk115Flat(oc); err != nil { t.Fatalf("walk115Flat adaptive failed: %v", err) } + st.flushPreferredVideos() // 1个根目录视频 + 2个Heyzo视频 + 1个S1视频 = 共4个视频成功生成 .strm if st.rec.NewStrm != 4 { diff --git a/web/src/components/LayoutUserMenu.tsx b/web/src/components/LayoutUserMenu.tsx index 246a944..b674a1d 100644 --- a/web/src/components/LayoutUserMenu.tsx +++ b/web/src/components/LayoutUserMenu.tsx @@ -55,7 +55,9 @@ export function LayoutUserMenu({ const [menuPosition, setMenuPosition] = useState(null) const [isOtpOpen, setIsOtpOpen] = useState(false) - onCloseRef.current = onClose + useEffect(() => { + onCloseRef.current = onClose + }, [onClose]) const updateMenuPosition = useCallback(() => { const trigger = triggerRef.current diff --git a/web/src/components/ManageLibrariesDialogView.tsx b/web/src/components/ManageLibrariesDialogView.tsx new file mode 100644 index 0000000..0b51a28 --- /dev/null +++ b/web/src/components/ManageLibrariesDialogView.tsx @@ -0,0 +1,34 @@ +import { X } from 'lucide-react' + +import { AdminLibraryPanel } from '../pages/AdminLibraryPanel' + +export function ManageLibrariesDialogView({ onClose }: { onClose: () => void }) { + return ( +
+
event.stopPropagation()} + > +
+

管理媒体库

+ +
+
+ +
+
+
+ ) +} diff --git a/web/src/components/PlayerControls.tsx b/web/src/components/PlayerControls.tsx index e8139d9..d1a6261 100644 --- a/web/src/components/PlayerControls.tsx +++ b/web/src/components/PlayerControls.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { Captions, CaptionsOff, @@ -84,11 +84,11 @@ export function PlayerControls({ streamOffset = 0, onSeekAbsolute, }: PlayerControlsProps) { - const video = () => videoRef.current - const container = () => + const video = useCallback(() => videoRef.current, [videoRef]) + const container = useCallback(() => videoRef.current?.closest('[data-player-stage]') ?? videoRef.current?.parentElement ?? - null + null, [videoRef]) const [playing, setPlaying] = useState(false) const [currentTime, setCurrentTime] = useState(0) @@ -255,15 +255,14 @@ export function PlayerControls({ el.removeEventListener('leavepictureinpicture', syncPip) if (hideTimerRef.current) clearTimeout(hideTimerRef.current) } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [videoRef, knownDuration, streamOffset, onUiVisibleChange]) + }, [container, video, knownDuration, streamOffset, onUiVisibleChange]) // Keep the scrubber max in sync when metadata duration arrives after mount. useEffect(() => { const el = video() const streamDur = el && Number.isFinite(el.duration) ? el.duration : 0 setDuration(Math.max(knownDuration || 0, streamOffset + streamDur)) - }, [knownDuration, streamOffset]) + }, [knownDuration, streamOffset, video]) // 当悬停或菜单状态改变时,更新控制栏计时器 useEffect(() => { @@ -277,7 +276,7 @@ export function PlayerControls({ hideTimerRef.current = setTimeout(() => onUiVisibleChange(false), 3000) } } - }, [controlsHovered, isScrubbing, subtitleMenuOpen, danmakuOpen, playlistOpen, onUiVisibleChange]) + }, [controlsHovered, isScrubbing, subtitleMenuOpen, danmakuOpen, playlistOpen, onUiVisibleChange, video]) const togglePlay = () => { const el = video() diff --git a/web/src/components/manageLibrariesDialog.tsx b/web/src/components/manageLibrariesDialog.tsx index b5d90bf..a8b77c2 100644 --- a/web/src/components/manageLibrariesDialog.tsx +++ b/web/src/components/manageLibrariesDialog.tsx @@ -1,7 +1,6 @@ import { createRoot } from 'react-dom/client' -import { X } from 'lucide-react' -import { AdminLibraryPanel } from '../pages/AdminLibraryPanel' +import { ManageLibrariesDialogView } from './ManageLibrariesDialogView' export function openManageLibrariesDialog(): Promise { return new Promise((resolve) => { @@ -15,37 +14,6 @@ export function openManageLibrariesDialog(): Promise { resolve() } - root.render() + root.render() }) -} - -function ManageLibrariesDialog({ onClose }: { onClose: () => void }) { - return ( -
-
event.stopPropagation()} - > -
-

管理媒体库

- -
-
- -
-
-
- ) } \ No newline at end of file diff --git a/web/src/hooks/useWebSocket.ts b/web/src/hooks/useWebSocket.ts index ba8edca..df1de00 100644 --- a/web/src/hooks/useWebSocket.ts +++ b/web/src/hooks/useWebSocket.ts @@ -15,7 +15,10 @@ export function useWebSocket(onEvent: (topic: string, payload: unknown) => void) const ref = useRef(null) const token = useAuthStore((s) => s.token) const onEventRef = useRef(onEvent) - onEventRef.current = onEvent + + useEffect(() => { + onEventRef.current = onEvent + }, [onEvent]) useEffect(() => { if (!token) return diff --git a/web/src/pages/AdminLibraryTable.tsx b/web/src/pages/AdminLibraryTable.tsx index 83eb35c..0cab45b 100644 --- a/web/src/pages/AdminLibraryTable.tsx +++ b/web/src/pages/AdminLibraryTable.tsx @@ -28,7 +28,7 @@ export function AdminLibraryTable({ libs, ...actions }: LibraryTableProps) { const [browsingRoot, setBrowsingRoot] = useState<{ libraryID: string; root: LibraryRoot; initialPath?: string } | null>(null) const [addingRootLib, setAddingRootLib] = useState(null) const [draggingId, setDraggingId] = useState(null) - const dragOverId = useRef(null) + const [dragOverId, setDragOverId] = useState(null) const handleSelectRootPath = (selectedPath: string) => { if (browsingRoot) { @@ -59,7 +59,7 @@ export function AdminLibraryTable({ libs, ...actions }: LibraryTableProps) { const handleDrop = (e: DragEvent, overId: string) => { e.preventDefault() - dragOverId.current = null + setDragOverId(null) setDraggingId(null) if (draggingId && overId !== draggingId) { handleReorder(draggingId, overId) @@ -90,19 +90,19 @@ export function AdminLibraryTable({ libs, ...actions }: LibraryTableProps) { onBrowseRoot={(root) => setBrowsingRoot({ libraryID: library.id, root, initialPath: root.path })} onOpenAddRoot={() => setAddingRootLib(library)} dragging={draggingId === library.id} - dragOver={dragOverId.current === library.id} + dragOver={dragOverId === library.id} onDragStart={(e) => { e.dataTransfer.effectAllowed = 'move' - dragOverId.current = null + setDragOverId(null) setDraggingId(library.id) }} onDragOver={(e) => { e.preventDefault() e.dataTransfer.dropEffect = 'move' - if (dragOverId.current !== library.id) dragOverId.current = library.id + if (dragOverId !== library.id) setDragOverId(library.id) }} onDragEnd={() => { - dragOverId.current = null + setDragOverId(null) setDraggingId(null) }} onDrop={(e) => handleDrop(e, library.id)} diff --git a/web/src/pages/EmbyMountPage.tsx b/web/src/pages/EmbyMountPage.tsx index dc5daaf..f507097 100644 --- a/web/src/pages/EmbyMountPage.tsx +++ b/web/src/pages/EmbyMountPage.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react-refresh/only-export-components */ import { useCallback, useEffect, useState } from 'react' import toast from 'react-hot-toast' import { diff --git a/web/src/pages/LibraryStorageStats.tsx b/web/src/pages/LibraryStorageStats.tsx index aa8ed71..18e1e29 100644 --- a/web/src/pages/LibraryStorageStats.tsx +++ b/web/src/pages/LibraryStorageStats.tsx @@ -3,7 +3,7 @@ import { Database, HardDrive, PieChart, RefreshCw } from 'lucide-react' import { storageAPI, type StorageBreakdown } from '../api/storage' -export function fmtBytes(n: number): string { +function fmtBytes(n: number): string { if (!n) return '0 B' const u = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] let v = n @@ -15,7 +15,7 @@ export function fmtBytes(n: number): string { return `${v.toFixed(2)} ${u[i]}` } -export function fmtHours(seconds: number): string { +function fmtHours(seconds: number): string { if (!seconds) return '—' const h = Math.floor(seconds / 3600) return `${h.toLocaleString()} h` diff --git a/web/src/pages/PlayerPage.tsx b/web/src/pages/PlayerPage.tsx index 63ec9ae..c6ceff1 100644 --- a/web/src/pages/PlayerPage.tsx +++ b/web/src/pages/PlayerPage.tsx @@ -311,7 +311,10 @@ export function PlayerPage() { // Chromium 会把被中断的首次加载报告成播放错误并误触发 HLS 回退。 const activeBurnedSubtitleStream = mode === 'hls' ? burnedSubtitleStream : undefined const mediaRef = useRef(media) - mediaRef.current = media + useEffect(() => { + mediaRef.current = media + }, [media]) + useEffect(() => { if (!mediaId || !ref.current) return const currentMedia = mediaRef.current @@ -498,9 +501,11 @@ export function PlayerPage() { // 使用 ref 实时同步进度计算所需的状态,避免每次 hlsStartSec 改变都触发 cleanup 并误上报旧进度 const hlsStartSecRef = useRef(hlsStartSec) - hlsStartSecRef.current = hlsStartSec const modeRef = useRef(mode) - modeRef.current = mode + useEffect(() => { + hlsStartSecRef.current = hlsStartSec + modeRef.current = mode + }, [hlsStartSec, mode]) // Persist resume position every 10 seconds while playing, and immediately upon pause/unmount. useEffect(() => { diff --git a/web/src/pages/ScraperQueuePage.tsx b/web/src/pages/ScraperQueuePage.tsx index 5704b5e..2810654 100644 --- a/web/src/pages/ScraperQueuePage.tsx +++ b/web/src/pages/ScraperQueuePage.tsx @@ -23,7 +23,8 @@ import { imageURL } from '../api/client' import { scraperAPI } from '../api/scraper' import type { ScrapeQueueSnapshot, ScrapeTask, ScrapeTaskStatus } from '../types/scraper' import { apiErrorMessage, formatTime, taskStatusMeta } from './StrmManagePage' -import { PROVIDER_LABELS, ScrapeDetailModal } from './scraper-queue/ScrapeDetailModal' +import { ScrapeDetailModal } from './scraper-queue/ScrapeDetailModal' +import { PROVIDER_LABELS } from './scraper-queue/scrapeLabels' import { copyToClipboard, useTaskSelection } from './queue-shared' const FILTERS: { key: 'all' | ScrapeTaskStatus; label: string; icon: typeof Clock; color: string }[] = [ @@ -196,9 +197,8 @@ export function ScraperQueuePage({ embedded = false }: { embedded?: boolean }) { } // Filter and search - const tasks = snapshot?.tasks ?? [] const filteredTasks = useMemo(() => { - let list = tasks + let list = snapshot?.tasks ?? [] if (filter !== 'all') { list = list.filter((t) => t.status === filter) } @@ -214,7 +214,7 @@ export function ScraperQueuePage({ embedded = false }: { embedded?: boolean }) { ) } return list - }, [tasks, filter, search]) + }, [snapshot?.tasks, filter, search]) const counts = snapshot?.counts const pendingCount = counts?.pending ?? 0 diff --git a/web/src/pages/StrmQueuePage.tsx b/web/src/pages/StrmQueuePage.tsx index 63ea87e..bd5e3a4 100644 --- a/web/src/pages/StrmQueuePage.tsx +++ b/web/src/pages/StrmQueuePage.tsx @@ -21,7 +21,8 @@ import { strmAPI } from '../api/strm' import type { StrmQueueSnapshot, StrmTask, StrmTaskStatus } from '../types/strm' import { STRM_PROVIDER_LABELS } from '../types/strm' import { apiErrorMessage, formatBytes, formatTime, taskStatusMeta } from './StrmManagePage' -import { TaskDetailModal, getFileIcon } from './strm-queue/TaskDetailModal' +import { TaskDetailModal } from './strm-queue/TaskDetailModal' +import { getFileIcon } from './strm-queue/taskFileIcon' import { copyToClipboard, useTaskSelection } from './queue-shared' const FILTERS: { key: 'all' | StrmTaskStatus; label: string; icon: typeof Clock; color: string }[] = [ @@ -190,9 +191,8 @@ export function StrmQueuePanel({ } // Filter and search tasks in memory - const tasks = snapshot?.tasks ?? [] const filteredTasks = useMemo(() => { - let list = tasks + let list = snapshot?.tasks ?? [] if (filter !== 'all') { list = list.filter((t) => t.status === filter) } @@ -207,7 +207,7 @@ export function StrmQueuePanel({ ) } return list - }, [tasks, filter, search]) + }, [snapshot?.tasks, filter, search]) const counts = snapshot?.counts const pendingCount = counts?.pending ?? 0 diff --git a/web/src/pages/scraper-queue/ScrapeDetailModal.tsx b/web/src/pages/scraper-queue/ScrapeDetailModal.tsx index 8e7ef42..5342f90 100644 --- a/web/src/pages/scraper-queue/ScrapeDetailModal.tsx +++ b/web/src/pages/scraper-queue/ScrapeDetailModal.tsx @@ -4,21 +4,7 @@ import { AlertCircle, Ban, Copy, ExternalLink, RefreshCw, Sparkles, Trash2, X } import { imageURL } from '../../api/client' import type { ScrapeTask } from '../../types/scraper' import { formatTime, taskStatusMeta } from '../StrmManagePage' - -export const PROVIDER_LABELS: Record = { - tmdb: 'TheMovieDB', - douban: '豆瓣 Douban', - bangumi: 'Bangumi 番组计划', - thetvdb: 'TheTVDB', - metatube: 'MetaTube', -} - -export const TYPE_LABELS: Record = { - movie: '电影', - tv: '剧集', - anime: '动漫', - adult: 'Adult', -} +import { PROVIDER_LABELS, TYPE_LABELS } from './scrapeLabels' export function ScrapeDetailModal({ task, diff --git a/web/src/pages/scraper-queue/scrapeLabels.ts b/web/src/pages/scraper-queue/scrapeLabels.ts new file mode 100644 index 0000000..a059b75 --- /dev/null +++ b/web/src/pages/scraper-queue/scrapeLabels.ts @@ -0,0 +1,14 @@ +export const PROVIDER_LABELS: Record = { + tmdb: 'TheMovieDB', + douban: '豆瓣 Douban', + bangumi: 'Bangumi 番组计划', + thetvdb: 'TheTVDB', + metatube: 'MetaTube', +} + +export const TYPE_LABELS: Record = { + movie: '电影', + tv: '剧集', + anime: '动漫', + adult: 'Adult', +} diff --git a/web/src/pages/strm-queue/TaskDetailModal.tsx b/web/src/pages/strm-queue/TaskDetailModal.tsx index 6d67c65..eacc175 100644 --- a/web/src/pages/strm-queue/TaskDetailModal.tsx +++ b/web/src/pages/strm-queue/TaskDetailModal.tsx @@ -1,26 +1,9 @@ -import type { ReactNode } from 'react' -import { AlertCircle, Ban, Copy, FileText, Film, Image as ImageIcon, File, MessageSquare, RefreshCw, Trash2, X } from 'lucide-react' +import { AlertCircle, Ban, Copy, RefreshCw, Trash2, X } from 'lucide-react' import type { StrmTask } from '../../types/strm' import { STRM_PROVIDER_LABELS } from '../../types/strm' import { formatBytes, formatTime, taskStatusMeta } from '../StrmManagePage' - -export function getFileIcon(filename: string): ReactNode { - const ext = filename.split('.').pop()?.toLowerCase() ?? '' - if (['nfo', 'txt', 'xml', 'json'].includes(ext)) { - return - } - if (['jpg', 'jpeg', 'png', 'webp', 'bmp', 'gif', 'svg'].includes(ext)) { - return - } - if (['srt', 'ass', 'ssa', 'sub', 'vtt'].includes(ext)) { - return - } - if (['mkv', 'mp4', 'avi', 'mov', 'wmv', 'ts', 'flv', 'iso', 'm4v', 'strm'].includes(ext)) { - return - } - return -} +import { getFileIcon } from './taskFileIcon' export function TaskDetailModal({ task, diff --git a/web/src/pages/strm-queue/taskFileIcon.tsx b/web/src/pages/strm-queue/taskFileIcon.tsx new file mode 100644 index 0000000..0e5dc8c --- /dev/null +++ b/web/src/pages/strm-queue/taskFileIcon.tsx @@ -0,0 +1,19 @@ +import type { ReactNode } from 'react' +import { File, FileText, Film, Image as ImageIcon, MessageSquare } from 'lucide-react' + +export function getFileIcon(filename: string): ReactNode { + const ext = filename.split('.').pop()?.toLowerCase() ?? '' + if (['nfo', 'txt', 'xml', 'json'].includes(ext)) { + return + } + if (['jpg', 'jpeg', 'png', 'webp', 'bmp', 'gif', 'svg'].includes(ext)) { + return + } + if (['srt', 'ass', 'ssa', 'sub', 'vtt'].includes(ext)) { + return + } + if (['mkv', 'mp4', 'avi', 'mov', 'wmv', 'ts', 'flv', 'iso', 'm4v', 'strm'].includes(ext)) { + return + } + return +} diff --git a/web/src/pages/useAdminLibraryPanel.ts b/web/src/pages/useAdminLibraryPanel.ts index d5d7802..6c36c8a 100644 --- a/web/src/pages/useAdminLibraryPanel.ts +++ b/web/src/pages/useAdminLibraryPanel.ts @@ -180,7 +180,7 @@ function useLibraryActions(refresh: () => Promise) { } const toggleCarouselLibrary = async (library: Library) => { - const next = !Boolean(library.carousel_enabled) + const next = !library.carousel_enabled await libraryAPI.update(library.id, { carousel_enabled: next }) toast.success(next ? `「${library.name}」已加入首页轮播` : `「${library.name}」已移出首页轮播`) await refresh()