handle video tagging
This commit is contained in:
@@ -12,10 +12,11 @@ interface Props {
|
||||
onNext?: () => void
|
||||
itemKey?: string
|
||||
onTagsChanged?: () => void
|
||||
onAiTag?: () => Promise<void>
|
||||
context?: 'mixed' | 'movies' | 'tv'
|
||||
}
|
||||
|
||||
export default function VideoPlayerModal({ url, name, onClose, onPrev, onNext, itemKey, onTagsChanged, context = 'mixed' }: Props) {
|
||||
export default function VideoPlayerModal({ url, name, onClose, onPrev, onNext, itemKey, onTagsChanged, onAiTag, context = 'mixed' }: Props) {
|
||||
const settings = useUserSettings()
|
||||
const autoPlay = context === 'mixed' ? settings.mixedAutoplay : context === 'movies' ? settings.moviesAutoplay : settings.tvAutoplay
|
||||
const loop = context === 'mixed' ? settings.mixedLoop : context === 'movies' ? settings.moviesLoop : settings.tvLoop
|
||||
@@ -24,6 +25,9 @@ export default function VideoPlayerModal({ url, name, onClose, onPrev, onNext, i
|
||||
const [showTags, setShowTags] = useState(
|
||||
() => !!itemKey && typeof window !== 'undefined' && window.innerWidth >= 1280
|
||||
)
|
||||
const [aiTagging, setAiTagging] = useState(false)
|
||||
const [aiTagError, setAiTagError] = useState<string | null>(null)
|
||||
const [tagRefreshKey, setTagRefreshKey] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
const handleKey = (e: KeyboardEvent) => {
|
||||
@@ -76,6 +80,43 @@ export default function VideoPlayerModal({ url, name, onClose, onPrev, onNext, i
|
||||
🏷
|
||||
</button>
|
||||
)}
|
||||
{onAiTag && (
|
||||
<button
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation()
|
||||
setAiTagging(true)
|
||||
setAiTagError(null)
|
||||
try {
|
||||
await onAiTag()
|
||||
setTagRefreshKey((k) => k + 1)
|
||||
onTagsChanged?.()
|
||||
} catch (err) {
|
||||
setAiTagError(err instanceof Error ? err.message : 'AI tagging failed')
|
||||
setTimeout(() => setAiTagError(null), 4000)
|
||||
} finally {
|
||||
setAiTagging(false)
|
||||
}
|
||||
}}
|
||||
disabled={aiTagging}
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center text-sm transition-colors disabled:opacity-50"
|
||||
style={{
|
||||
backgroundColor: aiTagError ? '#7f1d1d' : 'var(--surface)',
|
||||
color: aiTagError ? '#fca5a5' : 'var(--text-primary)',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!aiTagging && !aiTagError) (e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface-hover)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!aiTagError) (e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface)'
|
||||
}}
|
||||
aria-label="AI Tag this video"
|
||||
title={aiTagError ?? (aiTagging ? 'Tagging…' : 'AI Tag')}
|
||||
>
|
||||
{aiTagging ? (
|
||||
<span className="animate-spin" style={{ display: 'inline-block' }}>⟳</span>
|
||||
) : '✨'}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center text-sm flex-shrink-0 transition-colors"
|
||||
@@ -134,7 +175,7 @@ export default function VideoPlayerModal({ url, name, onClose, onPrev, onNext, i
|
||||
<p className="text-xs font-semibold uppercase tracking-wider mb-3" style={{ color: 'var(--text-secondary)' }}>
|
||||
Tags
|
||||
</p>
|
||||
<TagSelector itemKey={itemKey!} onTagsChanged={onTagsChanged} />
|
||||
<TagSelector itemKey={itemKey!} onTagsChanged={onTagsChanged} refreshKey={tagRefreshKey} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user