diff --git a/web/src/pages/LibraryPage.tsx b/web/src/pages/LibraryPage.tsx
index 8c71649..4d631c3 100644
--- a/web/src/pages/LibraryPage.tsx
+++ b/web/src/pages/LibraryPage.tsx
@@ -1,16 +1,14 @@
import { useState } from 'react'
import { useLocation, useParams, useSearchParams } from 'react-router-dom'
-import { motion, AnimatePresence } from 'framer-motion'
+import { motion } from 'framer-motion'
import type { Media } from '../types'
import { useAuthStore } from '../stores/auth'
-import { seriesTitle, type SeriesCard } from '../utils/groupSeries'
-import { ManualScrapeDialog } from '../components/ManualScrapeDialog'
-import { MetadataEditDialog } from '../components/MetadataEditDialog'
-import { LibrarySeriesEpisodes } from './LibrarySeriesEpisodes'
-import { LibrarySeriesDetailHeader } from './LibrarySeriesDetailHeader'
+import type { SeriesCard } from '../utils/groupSeries'
+import { LibraryPageDialogs } from './LibraryPageDialogs'
import { LibraryPageHeader } from './LibraryPageHeader'
import { LibraryMediaSections } from './LibraryMediaSections'
+import { LibrarySeriesDetailSection } from './LibrarySeriesDetailSection'
import { useLibraryData } from './useLibraryData'
import { useLibraryScanStatus } from './useLibraryScanStatus'
import { useLibrarySeriesSelection } from './useLibrarySeriesSelection'
@@ -141,84 +139,40 @@ export function LibraryPage() {
onSeriesClick={handleSeriesClick}
/>
- {/* 剧集详情:季/集选择器 */}
-
- {selectedSeries && (
-
- setManualSeriesScrapeOpen(true)}
- onMetadataEdit={() => setSeriesMetadataEditOpen(true)}
- onProbe={handleSeriesProbe}
- onNFO={handleSeriesNFO}
- onOrganize={handleSeriesOrganize}
- onSoftDelete={handleSeriesSoftDelete}
- />
-
- {/* 季 / 集列表 */}
-
-
-
-
- )}
-
-
- setManualSeriesScrapeOpen(false)}
- onApplied={reloadCurrentLibrary}
+ setManualSeriesScrapeOpen(true)}
+ onMetadataEdit={() => setSeriesMetadataEditOpen(true)}
+ onProbe={handleSeriesProbe}
+ onNFO={handleSeriesNFO}
+ onOrganize={handleSeriesOrganize}
+ onSoftDelete={handleSeriesSoftDelete}
+ onSeasonChange={setSelectedSeason}
/>
- setSeriesMetadataEditOpen(false)}
- onSaved={reloadCurrentLibrary}
- />
- setManualMovie(null)}
+
+ setManualSeriesScrapeOpen(false)}
+ onCloseSeriesMetadataEdit={() => setSeriesMetadataEditOpen(false)}
+ onCloseManualMovie={() => setManualMovie(null)}
onApplied={reloadCurrentLibrary}
/>
)
}
-
-function scrapeMediaType(libraryType: string | undefined, media: Media): string {
- if ((media.season_num ?? 0) > 0 || (media.episode_num ?? 0) > 0) {
- return 'tv'
- }
- return libraryType || 'movie'
-}
diff --git a/web/src/pages/LibraryPageDialogs.tsx b/web/src/pages/LibraryPageDialogs.tsx
new file mode 100644
index 0000000..7fef288
--- /dev/null
+++ b/web/src/pages/LibraryPageDialogs.tsx
@@ -0,0 +1,76 @@
+import { ManualScrapeDialog } from '../components/ManualScrapeDialog'
+import { MetadataEditDialog } from '../components/MetadataEditDialog'
+import type { Media } from '../types'
+import { seriesTitle, type SeriesCard } from '../utils/groupSeries'
+
+type LibraryPageDialogsProps = {
+ manualSeriesScrapeOpen: boolean
+ seriesMetadataEditOpen: boolean
+ manualMovie: Media | null
+ selectedSeries: SeriesCard | null
+ selectedSeriesMediaIDs: string[]
+ libraryType?: string
+ scrapeEpisodeArtwork: boolean
+ onCloseManualSeriesScrape: () => void
+ onCloseSeriesMetadataEdit: () => void
+ onCloseManualMovie: () => void
+ onApplied: () => void
+}
+
+export function LibraryPageDialogs({
+ manualSeriesScrapeOpen,
+ seriesMetadataEditOpen,
+ manualMovie,
+ selectedSeries,
+ selectedSeriesMediaIDs,
+ libraryType,
+ scrapeEpisodeArtwork,
+ onCloseManualSeriesScrape,
+ onCloseSeriesMetadataEdit,
+ onCloseManualMovie,
+ onApplied,
+}: LibraryPageDialogsProps) {
+ const selectedSeriesTitle = selectedSeries ? seriesTitle(selectedSeries.rep) : ''
+
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+function scrapeMediaType(libraryType: string | undefined, media: Media): string {
+ if ((media.season_num ?? 0) > 0 || (media.episode_num ?? 0) > 0) {
+ return 'tv'
+ }
+ return libraryType || 'movie'
+}
diff --git a/web/src/pages/LibrarySeriesDetailSection.tsx b/web/src/pages/LibrarySeriesDetailSection.tsx
new file mode 100644
index 0000000..3dd1966
--- /dev/null
+++ b/web/src/pages/LibrarySeriesDetailSection.tsx
@@ -0,0 +1,123 @@
+import { AnimatePresence, motion } from 'framer-motion'
+
+import type { Media } from '../types'
+import type { SeriesCard } from '../utils/groupSeries'
+import { LibrarySeriesDetailHeader } from './LibrarySeriesDetailHeader'
+import { LibrarySeriesEpisodes } from './LibrarySeriesEpisodes'
+
+type SeasonEpisodes = {
+ season: number
+ episodes: Media[]
+}
+
+type LibrarySeriesDetailSectionProps = {
+ selectedSeries: SeriesCard | null
+ selectedEpisodes: SeasonEpisodes[]
+ selectedSeason: number | null
+ visibleEpisodes: Media[]
+ allEpisodes: Media[]
+ loadingEpisodes: boolean
+ playbackFrom: string
+ isAdmin: boolean
+ seriesToolBusy: string
+ onBack: () => void
+ onSmartScrape: () => void
+ onManualScrape: () => void
+ onMetadataEdit: () => void
+ onProbe: () => void
+ onNFO: () => void
+ onOrganize: () => void
+ onSoftDelete: () => void
+ onSeasonChange: (season: number) => void
+}
+
+export function LibrarySeriesDetailSection({
+ selectedSeries,
+ selectedEpisodes,
+ selectedSeason,
+ visibleEpisodes,
+ allEpisodes,
+ loadingEpisodes,
+ playbackFrom,
+ isAdmin,
+ seriesToolBusy,
+ onBack,
+ onSmartScrape,
+ onManualScrape,
+ onMetadataEdit,
+ onProbe,
+ onNFO,
+ onOrganize,
+ onSoftDelete,
+ onSeasonChange,
+}: LibrarySeriesDetailSectionProps) {
+ return (
+
+ {selectedSeries && (
+
+
+
+
+
+ )}
+
+ )
+}
+
+type LibrarySeriesEpisodesPanelProps = {
+ loading: boolean
+ selectedEpisodes: SeasonEpisodes[]
+ selectedSeason: number | null
+ visibleEpisodes: Media[]
+ playbackFrom: string
+ onSeasonChange: (season: number) => void
+}
+
+function LibrarySeriesEpisodesPanel({
+ loading,
+ selectedEpisodes,
+ selectedSeason,
+ visibleEpisodes,
+ playbackFrom,
+ onSeasonChange,
+}: LibrarySeriesEpisodesPanelProps) {
+ return (
+
+
+
+ )
+}