mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-11 15:56:37 +08:00
优化
This commit is contained in:
@@ -42,6 +42,7 @@ export function ExternalPlayerButton({
|
|||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
title={compact ? '使用外部播放器播放' : undefined}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -50,7 +51,7 @@ export function ExternalPlayerButton({
|
|||||||
}}
|
}}
|
||||||
className={
|
className={
|
||||||
compact
|
compact
|
||||||
? 'rounded-lg border border-primary-400/35 bg-white px-2 py-1 text-xs font-semibold text-brand-500 hover:bg-primary-400/10 disabled:opacity-50'
|
? 'shrink-0 inline-flex items-center rounded-lg border border-primary-400/35 bg-white px-2 py-1 text-xs font-semibold text-brand-500 hover:bg-primary-400/10 disabled:opacity-50 transition-colors whitespace-nowrap'
|
||||||
: 'btn-outline border-brand-500/30 px-5 text-[#c9954a] hover:border-brand-500 hover:bg-brand-50'
|
: 'btn-outline border-brand-500/30 px-5 text-[#c9954a] hover:border-brand-500 hover:bg-brand-50'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -62,48 +62,53 @@ export function LibrarySeriesEpisodes({
|
|||||||
<h3 className="mb-3 font-display text-lg font-semibold text-ink-600">
|
<h3 className="mb-3 font-display text-lg font-semibold text-ink-600">
|
||||||
{displaySeason === 0 ? '特别篇' : `第 ${displaySeason} 季`}
|
{displaySeason === 0 ? '特别篇' : `第 ${displaySeason} 季`}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
|
<div className="grid grid-cols-1 gap-2.5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
||||||
{visibleEpisodes.map((ep) => (
|
{visibleEpisodes.map((ep) => {
|
||||||
<div
|
const displayTitle = episodeDisplayTitle(ep, visibleEpisodes)
|
||||||
key={ep.id}
|
return (
|
||||||
className="group flex items-center gap-3 rounded-xl border border-sand-200 bg-white p-3 shadow-card transition-all hover:border-brand-300 hover:shadow-card-hover"
|
<div
|
||||||
>
|
key={ep.id}
|
||||||
<Link to={`/play/${ep.id}`} state={{ from: playbackFrom }} className="flex min-w-0 flex-1 items-center gap-3">
|
className="group flex items-center justify-between gap-3 rounded-xl border border-sand-200 bg-white p-2.5 shadow-card transition-all hover:border-brand-300 hover:shadow-card-hover"
|
||||||
<div
|
>
|
||||||
className="relative flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-xl bg-brand-50"
|
<Link to={`/play/${ep.id}`} state={{ from: playbackFrom }} className="flex min-w-0 flex-1 items-center gap-3">
|
||||||
title={ep.episode_num > 0 ? `第 ${ep.episode_num} 集` : episodeDisplayTitle(ep, visibleEpisodes)}
|
<div
|
||||||
>
|
className="relative flex h-11 w-16 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-brand-50/70 border border-sand-200/60"
|
||||||
{ep.backdrop_url || ep.poster_url ? (
|
title={displayTitle}
|
||||||
<img
|
>
|
||||||
src={imageURL(ep.backdrop_url || ep.poster_url || '', ep.updated_at)}
|
{ep.backdrop_url || ep.poster_url ? (
|
||||||
alt=""
|
<img
|
||||||
className="h-full w-full object-cover"
|
src={imageURL(ep.backdrop_url || ep.poster_url || '', ep.updated_at)}
|
||||||
referrerPolicy="no-referrer"
|
alt=""
|
||||||
/>
|
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
||||||
) : (
|
referrerPolicy="no-referrer"
|
||||||
<span className="text-brand-600 font-semibold text-sm">{ep.episode_num || '—'}</span>
|
/>
|
||||||
)}
|
) : (
|
||||||
{ep.episode_num > 0 && (
|
<span className="text-brand-600 font-bold text-sm">{ep.episode_num || '—'}</span>
|
||||||
<span className="absolute bottom-0 right-0 rounded-tl bg-black/70 px-1 py-px text-[9px] font-bold text-white">
|
)}
|
||||||
{ep.episode_num}
|
<div className="absolute inset-0 flex items-center justify-center bg-black/25 opacity-0 transition-opacity duration-200 group-hover:opacity-100">
|
||||||
</span>
|
<Play size={15} className="fill-white text-white drop-shadow-sm" />
|
||||||
)}
|
</div>
|
||||||
</div>
|
{ep.episode_num > 0 && (
|
||||||
<div className="min-w-0 flex-1">
|
<span className="absolute bottom-0 right-0 rounded-tl bg-black/75 px-1 py-0.5 text-[9px] font-bold leading-none text-white backdrop-blur-[2px]">
|
||||||
<p className="truncate text-sm font-medium text-ink-600">
|
{ep.episode_num}
|
||||||
{episodeDisplayTitle(ep, visibleEpisodes)}
|
</span>
|
||||||
</p>
|
)}
|
||||||
<p className="text-xs text-sand-500">
|
</div>
|
||||||
{ep.duration_sec > 0
|
<div className="min-w-0 flex-1">
|
||||||
? `${Math.floor(ep.duration_sec / 60)} 分钟`
|
<p className="truncate text-sm font-medium text-ink-600 transition-colors group-hover:text-brand-600" title={displayTitle}>
|
||||||
: formatSize(ep.size_bytes)}
|
{displayTitle}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<p className="text-xs text-sand-500 whitespace-nowrap">
|
||||||
<Play size={14} className="shrink-0 text-gray-500 opacity-0 transition-opacity group-hover:opacity-100 group-hover:text-brand-500" />
|
{ep.duration_sec > 0
|
||||||
</Link>
|
? `${Math.floor(ep.duration_sec / 60)} 分钟`
|
||||||
<ExternalPlayerButton mediaId={ep.id} label="外部" compact />
|
: formatSize(ep.size_bytes)}
|
||||||
</div>
|
</p>
|
||||||
))}
|
</div>
|
||||||
|
</Link>
|
||||||
|
<ExternalPlayerButton mediaId={ep.id} label="外部" compact />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -111,17 +116,29 @@ export function LibrarySeriesEpisodes({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function episodeDisplayTitle(ep: Media, siblings: Media[]): string {
|
function episodeDisplayTitle(ep: Media, siblings: Media[]): string {
|
||||||
const title = ep.episode_title?.trim()
|
let mainTitle = ''
|
||||||
if (title && !looksLikeSeriesTitle(ep, title, siblings)) {
|
const epTitle = ep.episode_title?.trim()
|
||||||
return title
|
if (epTitle && !looksLikeSeriesTitle(ep, epTitle, siblings)) {
|
||||||
|
mainTitle = epTitle
|
||||||
|
} else {
|
||||||
|
const mediaTitle = ep.title?.trim()
|
||||||
|
if (mediaTitle && !looksLikeSeriesTitle(ep, mediaTitle, siblings)) {
|
||||||
|
mainTitle = mediaTitle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediaTitle = ep.title?.trim()
|
if (ep.episode_num > 0) {
|
||||||
if (mediaTitle && !looksLikeSeriesTitle(ep, mediaTitle, siblings)) {
|
if (!mainTitle) {
|
||||||
return mediaTitle
|
return `第 ${ep.episode_num} 集`
|
||||||
|
}
|
||||||
|
const prefixRegex = new RegExp(`^(第\\s*0*${ep.episode_num}\\s*集|ep?\\.?\\s*0*${ep.episode_num}\\b)`, 'i')
|
||||||
|
if (prefixRegex.test(mainTitle)) {
|
||||||
|
return mainTitle
|
||||||
|
}
|
||||||
|
return `第 ${ep.episode_num} 集 · ${mainTitle}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return ep.episode_num > 0 ? `第 ${ep.episode_num} 集` : mediaTitle || title || '未命名'
|
return mainTitle || '未命名'
|
||||||
}
|
}
|
||||||
|
|
||||||
function looksLikeSeriesTitle(ep: Media, title: string, siblings: Media[]): boolean {
|
function looksLikeSeriesTitle(ep: Media, title: string, siblings: Media[]): boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user