完善动漫特别内容分类与版本判定

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
truewhile
2026-09-09 22:03:52 +08:00
parent 92e693ccac
commit 18e6cb40fd
20 changed files with 426 additions and 60 deletions
+30 -4
View File
@@ -4,7 +4,21 @@ import { Play, Search } from 'lucide-react'
import { imageURL } from '../api/client'
import { ExternalPlayerButton } from '../components/ExternalPlayerButton'
import type { Media } from '../types'
import { isTheatricalFeature, seriesTitleFromPath, THEATRICAL_SEASON } from '../utils/groupSeries'
import {
BONUS_SEASON,
EXTRA_SEASON,
isTheatricalFeature,
NCED_SEASON,
NCOP_SEASON,
OAD_SEASON,
OMAKE_SEASON,
ONA_SEASON,
OVA_SEASON,
OVD_SEASON,
PICTURE_DRAMA_SEASON,
seriesTitleFromPath,
THEATRICAL_SEASON,
} from '../utils/groupSeries'
import { formatSize } from './libraryPageModel'
type SeasonGroup = {
@@ -135,9 +149,21 @@ export function LibrarySeriesEpisodes({
}
function seasonLabel(season: number): string {
if (season === 0) return '特别篇'
if (season === THEATRICAL_SEASON) return '剧场版'
return `第 ${season} 季`
switch (season) {
case 0: return '特别篇'
case THEATRICAL_SEASON: return '剧场版'
case OVA_SEASON: return 'OVA'
case OAD_SEASON: return 'OAD'
case OVD_SEASON: return 'OVD'
case ONA_SEASON: return 'ONA'
case EXTRA_SEASON: return 'Extra'
case BONUS_SEASON: return 'Bonus'
case OMAKE_SEASON: return 'Omake'
case PICTURE_DRAMA_SEASON: return 'Picture Drama'
case NCOP_SEASON: return 'NCOP'
case NCED_SEASON: return 'NCED'
default: return `第 ${season} 季`
}
}
function episodeDisplayTitle(ep: Media, siblings: Media[]): string {
+1 -1
View File
@@ -15,7 +15,7 @@ export function seriesSourceRoot(episodes: Media[]): string {
if (!firstPath) return ''
const dir = dirname(firstPath)
const base = basename(dir)
if (/^(?:s\d{1,2}|season[\s._-]*\d{1,2}|第\s*\d{1,2}\s*季|specials?|sp|ova|oad|extra|extras|特别篇|特別篇|番外|特典)$/i.test(base)) {
if (/^(?:s\d{1,2}|season[\s._-]*\d{1,2}|第\s*\d{1,2}\s*季|specials?|sp|ovas?|oads?|ovds?|onas?|extras?|bonus(?:es)?|omake|picture[\s._-]*drama|ncop|nced|特别篇|特別篇|番外|特典|画像特典)$/i.test(base)) {
return dirname(dir)
}
return dir
+35 -7
View File
@@ -1,7 +1,23 @@
import { useEffect, useMemo } from 'react'
import type { Media } from '../types'
import { getSeriesKey, isTheatricalFeature, THEATRICAL_SEASON, type SeriesCard } from '../utils/groupSeries'
import {
BONUS_SEASON,
EXTRA_SEASON,
getSeriesKey,
isTheatricalFeature,
NCED_SEASON,
NCOP_SEASON,
OAD_SEASON,
OMAKE_SEASON,
ONA_SEASON,
OVA_SEASON,
OVD_SEASON,
PICTURE_DRAMA_SEASON,
specialSectionForMedia,
THEATRICAL_SEASON,
type SeriesCard,
} from '../utils/groupSeries'
type SeasonEpisodes = {
season: number
@@ -47,9 +63,9 @@ export function useLibrarySeriesSelection({
: sourceItems.filter((m) => getSeriesKey(m) === selectedSeries.key)
const seasons = new Map<number, Media[]>()
for (const ep of eps) {
const s = isTheatricalFeature(ep)
? THEATRICAL_SEASON
: ep.episode_num > 0 ? (ep.season_num ?? 0) : (ep.season_num || 1)
const specialSection = specialSectionForMedia(ep)
const s = specialSection ??
(ep.episode_num > 0 ? (ep.season_num ?? 0) : (ep.season_num || 1))
if (!seasons.has(s)) seasons.set(s, [])
seasons.get(s)!.push(ep)
}
@@ -132,7 +148,19 @@ export function useLibrarySeriesSelection({
}
function seasonSortOrder(season: number): number {
if (season === 0) return -2
if (season === THEATRICAL_SEASON) return -1
return season
switch (season) {
case 0: return 0
case OVA_SEASON: return 1
case OAD_SEASON: return 2
case OVD_SEASON: return 3
case ONA_SEASON: return 4
case EXTRA_SEASON: return 5
case BONUS_SEASON: return 6
case OMAKE_SEASON: return 7
case PICTURE_DRAMA_SEASON: return 8
case NCOP_SEASON: return 9
case NCED_SEASON: return 10
case THEATRICAL_SEASON: return 11
default: return 100 + season
}
}