mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 11:36:36 +08:00
4dc9cbe2e7
- 后端新增远程库→本地结构映射(emby_remote_web.go):Library/Media/SeriesCard 与本地完全同构,前端无感知 - /api/libraries(含 with_preview)、/api/libraries/:id、/api/libraries/:id/media、 /api/libraries/:id/series、/api/libraries/:id/series/episodes、/api/media/:id、 /api/media/:id/episodes 全部支持远程伪装 ID 透传映射 - /api/stream/:id 对远程条目 302 直连远程 Emby 原地址(播放不经过本机) - 库内容按 CollectionType 过滤(电影库取 Movie、剧集库取 Series), RecursiveItemCount 作为系列集数;修复伪装 ID 双重编码导致的图片/详情 500 - SPA fallback 放行 /library/embyremote~ 前缀(不再被当作 Emby API 路由 404) - 前端:远程库/条目标记 is_remote_emby,隐藏扫描/刮削/编辑/NFO/回收站/ HLS 转码等不适用操作;后台管理面板过滤远程库
94 lines
3.1 KiB
TypeScript
94 lines
3.1 KiB
TypeScript
import type { Library } from '../types'
|
|
import { EpisodeArtworkToggle } from '../components/EpisodeArtworkToggle'
|
|
import { MediaSortDropdown } from '../components/MediaSortDropdown'
|
|
import type { SortField, SortOrder } from '../utils/mediaSort'
|
|
import { libraryDisplayPath } from './libraryDisplayModel'
|
|
|
|
type LibraryPageHeaderProps = {
|
|
library: Library | null
|
|
itemCount: number
|
|
loadingAllText: string
|
|
scanProgress: string
|
|
isAdmin: boolean
|
|
scrapeEpisodeArtwork: boolean
|
|
scanning: boolean
|
|
scraping: boolean
|
|
repairing: boolean
|
|
sortField: SortField
|
|
sortOrder: SortOrder
|
|
onSortChange: (field: SortField, order: SortOrder) => void
|
|
onReshuffle: () => void
|
|
onScrapeEpisodeArtworkChange: (checked: boolean) => void
|
|
onScan: () => void
|
|
onScrape: () => void
|
|
onRepairRescrape: () => void
|
|
}
|
|
|
|
export function LibraryPageHeader({
|
|
library,
|
|
itemCount,
|
|
loadingAllText,
|
|
scanProgress,
|
|
isAdmin,
|
|
scrapeEpisodeArtwork,
|
|
scanning,
|
|
scraping,
|
|
repairing,
|
|
sortField,
|
|
sortOrder,
|
|
onSortChange,
|
|
onReshuffle,
|
|
onScrapeEpisodeArtworkChange,
|
|
onScan,
|
|
onScrape,
|
|
onRepairRescrape,
|
|
}: LibraryPageHeaderProps) {
|
|
const displayPath = library ? libraryDisplayPath(library.path) : ''
|
|
|
|
return (
|
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
<div>
|
|
<h1 className="font-display text-3xl font-bold text-ink-600">
|
|
{library?.name ?? '媒体库'}
|
|
<span className="text-sand-500"> ({itemCount})</span>
|
|
</h1>
|
|
{library && <p className="text-sm text-ink-50" title={library.path}>{library.type} · {displayPath}</p>}
|
|
{loadingAllText && <p className="mt-1 text-xs text-sand-500">{loadingAllText}</p>}
|
|
{scanProgress && <p className="mt-1 text-xs text-brand-500">{scanProgress}</p>}
|
|
</div>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<MediaSortDropdown
|
|
value={sortField}
|
|
order={sortOrder}
|
|
onChange={onSortChange}
|
|
onReshuffle={onReshuffle}
|
|
/>
|
|
{isAdmin && !library?.is_remote_emby && (
|
|
<>
|
|
<EpisodeArtworkToggle
|
|
checked={scrapeEpisodeArtwork}
|
|
onChange={onScrapeEpisodeArtworkChange}
|
|
title="关闭后仍会获取主海报和每集文字元数据,只跳过每集图片"
|
|
className="h-9"
|
|
/>
|
|
<button onClick={onScan} disabled={scanning} className="btn-outline">
|
|
{scanning ? '扫描中…' : '扫描媒体库'}
|
|
</button>
|
|
<button onClick={onScrape} disabled={scraping} className="btn-outline" title="对整个媒体库执行刮削元数据">
|
|
{scraping ? '刮削中…' : '整库刮削元数据'}
|
|
</button>
|
|
<button
|
|
onClick={onRepairRescrape}
|
|
disabled={repairing}
|
|
className="btn-outline"
|
|
title="回填本库占位符外部 ID 并重刮,修正空 ID / 拆集问题"
|
|
>
|
|
{repairing ? '修复中…' : '修复+重刮整库'}
|
|
</button>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|