mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-25 00:11:26 -07:00
feat(stats): improve YouTube media metadata and picker key handling
This commit is contained in:
@@ -8,6 +8,10 @@ interface MediaCardProps {
|
||||
}
|
||||
|
||||
export function MediaCard({ item, onClick }: MediaCardProps) {
|
||||
const primaryTitle = item.videoTitle?.trim() || item.canonicalTitle;
|
||||
const secondaryTitle =
|
||||
item.videoTitle?.trim() && item.videoTitle !== item.canonicalTitle ? item.canonicalTitle : null;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
@@ -20,9 +24,9 @@ export function MediaCard({ item, onClick }: MediaCardProps) {
|
||||
className="w-full aspect-[3/4] rounded-t-lg"
|
||||
/>
|
||||
<div className="p-3">
|
||||
<div className="text-sm font-medium text-ctp-text truncate">{item.canonicalTitle}</div>
|
||||
{item.videoTitle && item.videoTitle !== item.canonicalTitle ? (
|
||||
<div className="text-xs text-ctp-subtext1 truncate mt-1">{item.videoTitle}</div>
|
||||
<div className="text-sm font-medium text-ctp-text truncate">{primaryTitle}</div>
|
||||
{secondaryTitle ? (
|
||||
<div className="text-xs text-ctp-subtext1 truncate mt-1">{secondaryTitle}</div>
|
||||
) : null}
|
||||
<div className="text-xs text-ctp-overlay2 mt-1">
|
||||
{formatDuration(item.totalActiveMs)} · {formatNumber(item.totalCards)} cards
|
||||
|
||||
41
stats/src/components/library/MediaDetailView.test.tsx
Normal file
41
stats/src/components/library/MediaDetailView.test.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { getRelatedCollectionLabel } from './MediaDetailView';
|
||||
|
||||
test('getRelatedCollectionLabel returns View Channel for youtube-backed media', () => {
|
||||
assert.equal(
|
||||
getRelatedCollectionLabel({
|
||||
animeId: 1,
|
||||
canonicalTitle: 'Video',
|
||||
totalSessions: 1,
|
||||
totalActiveMs: 1,
|
||||
totalCards: 0,
|
||||
totalTokensSeen: 0,
|
||||
totalLinesSeen: 0,
|
||||
totalLookupCount: 0,
|
||||
totalLookupHits: 0,
|
||||
totalYomitanLookupCount: 0,
|
||||
channelName: 'Creator',
|
||||
}),
|
||||
'View Channel',
|
||||
);
|
||||
});
|
||||
|
||||
test('getRelatedCollectionLabel returns View Anime for non-youtube media', () => {
|
||||
assert.equal(
|
||||
getRelatedCollectionLabel({
|
||||
animeId: 1,
|
||||
canonicalTitle: 'Episode 5',
|
||||
totalSessions: 1,
|
||||
totalActiveMs: 1,
|
||||
totalCards: 0,
|
||||
totalTokensSeen: 0,
|
||||
totalLinesSeen: 0,
|
||||
totalLookupCount: 0,
|
||||
totalLookupHits: 0,
|
||||
totalYomitanLookupCount: 0,
|
||||
channelName: null,
|
||||
}),
|
||||
'View Anime',
|
||||
);
|
||||
});
|
||||
@@ -5,7 +5,14 @@ import { confirmSessionDelete } from '../../lib/delete-confirm';
|
||||
import { getSessionDisplayWordCount } from '../../lib/session-word-count';
|
||||
import { MediaHeader } from './MediaHeader';
|
||||
import { MediaSessionList } from './MediaSessionList';
|
||||
import type { SessionSummary } from '../../types/stats';
|
||||
import type { MediaDetailData, SessionSummary } from '../../types/stats';
|
||||
|
||||
export function getRelatedCollectionLabel(detail: MediaDetailData['detail']): string {
|
||||
if (detail?.channelName?.trim()) {
|
||||
return 'View Channel';
|
||||
}
|
||||
return 'View Anime';
|
||||
}
|
||||
|
||||
interface MediaDetailViewProps {
|
||||
videoId: number;
|
||||
@@ -53,6 +60,7 @@ export function MediaDetailView({
|
||||
totalLookupHits: sessions.reduce((sum, session) => sum + session.lookupHits, 0),
|
||||
totalYomitanLookupCount: sessions.reduce((sum, session) => sum + session.yomitanLookupCount, 0),
|
||||
};
|
||||
const relatedCollectionLabel = getRelatedCollectionLabel(detail);
|
||||
|
||||
const handleDeleteSession = async (session: SessionSummary) => {
|
||||
if (!confirmSessionDelete()) return;
|
||||
@@ -87,7 +95,7 @@ export function MediaDetailView({
|
||||
onClick={() => onNavigateToAnime(animeId)}
|
||||
className="text-sm text-ctp-blue hover:text-ctp-sapphire transition-colors"
|
||||
>
|
||||
View Anime →
|
||||
{relatedCollectionLabel} →
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user