Fix video thumbnails (ffmpeg with placeholder fallback), fix right-click restart
- Video thumbnails: try ffmpeg, fall back to play icon placeholder - Right-click no longer restarts video playback on same post - Reset activated index on new search
This commit is contained in:
parent
85ec13bf7c
commit
fad6ab65af
@ -671,6 +671,7 @@ class BooruApp(QMainWindow):
|
||||
self._run_async(_search)
|
||||
|
||||
def _on_search_done(self, posts: list) -> None:
|
||||
self._last_activated_index = -1
|
||||
self._posts = posts
|
||||
self._status.showMessage(f"{len(posts)} results")
|
||||
thumbs = self._grid.set_posts(len(posts))
|
||||
@ -768,6 +769,8 @@ class BooruApp(QMainWindow):
|
||||
|
||||
# -- Post selection / preview --
|
||||
|
||||
_last_activated_index = -1
|
||||
|
||||
def _on_post_selected(self, index: int) -> None:
|
||||
multi = self._grid.selected_indices
|
||||
if len(multi) > 1:
|
||||
@ -780,7 +783,9 @@ class BooruApp(QMainWindow):
|
||||
)
|
||||
if self._info_panel.isVisible():
|
||||
self._info_panel.set_post(post)
|
||||
self._on_post_activated(index)
|
||||
if index != self._last_activated_index:
|
||||
self._last_activated_index = index
|
||||
self._on_post_activated(index)
|
||||
|
||||
def _on_post_activated(self, index: int) -> None:
|
||||
if 0 <= index < len(self._posts):
|
||||
|
||||
@ -7,10 +7,8 @@ import os
|
||||
import threading
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import Qt, Signal, QObject, QUrl, QTimer
|
||||
from PySide6.QtCore import Qt, Signal, QObject
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtMultimedia import QMediaPlayer, QVideoSink, QVideoFrame
|
||||
from PySide6.QtMultimediaWidgets import QVideoWidget
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
@ -241,43 +239,42 @@ class LibraryView(QWidget):
|
||||
threading.Thread(target=_work, daemon=True).start()
|
||||
|
||||
def _capture_video_thumb(self, index: int, source: str, dest: str) -> None:
|
||||
"""Grab first frame from video using Qt's QMediaPlayer + QVideoSink."""
|
||||
from PySide6.QtMultimedia import QAudioOutput
|
||||
player = QMediaPlayer()
|
||||
audio = QAudioOutput()
|
||||
audio.setVolume(0)
|
||||
player.setAudioOutput(audio)
|
||||
sink = QVideoSink()
|
||||
player.setVideoSink(sink)
|
||||
captured = [False]
|
||||
|
||||
def _on_frame(frame: QVideoFrame):
|
||||
if captured[0]:
|
||||
return
|
||||
if frame.isValid():
|
||||
img = frame.toImage()
|
||||
if not img.isNull():
|
||||
captured[0] = True
|
||||
scaled = img.scaled(
|
||||
LIBRARY_THUMB_SIZE, LIBRARY_THUMB_SIZE,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
scaled.save(dest, "JPEG", 85)
|
||||
"""Grab first frame from video. Tries ffmpeg, falls back to placeholder."""
|
||||
def _work():
|
||||
try:
|
||||
import subprocess
|
||||
result = subprocess.run(
|
||||
["ffmpeg", "-y", "-i", source, "-vframes", "1",
|
||||
"-vf", f"scale={LIBRARY_THUMB_SIZE}:{LIBRARY_THUMB_SIZE}:force_original_aspect_ratio=decrease",
|
||||
"-q:v", "5", dest],
|
||||
capture_output=True, timeout=10,
|
||||
)
|
||||
if Path(dest).exists():
|
||||
self._signals.thumb_ready.emit(index, dest)
|
||||
player.stop()
|
||||
player.deleteLater()
|
||||
return
|
||||
except (FileNotFoundError, Exception):
|
||||
pass
|
||||
# Fallback: generate a placeholder
|
||||
from PySide6.QtGui import QPainter, QColor, QFont
|
||||
from PySide6.QtGui import QPolygon
|
||||
from PySide6.QtCore import QPoint as QP
|
||||
pix = QPixmap(LIBRARY_THUMB_SIZE - 4, LIBRARY_THUMB_SIZE - 4)
|
||||
pix.fill(QColor(40, 40, 40))
|
||||
painter = QPainter(pix)
|
||||
painter.setPen(QColor(180, 180, 180))
|
||||
painter.setFont(QFont(painter.font().family(), 9))
|
||||
ext = Path(source).suffix.upper().lstrip(".")
|
||||
painter.drawText(pix.rect(), Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter, ext)
|
||||
painter.setPen(Qt.PenStyle.NoPen)
|
||||
painter.setBrush(QColor(180, 180, 180, 150))
|
||||
cx, cy = pix.width() // 2, pix.height() // 2 - 10
|
||||
painter.drawPolygon(QPolygon([QP(cx - 15, cy - 20), QP(cx - 15, cy + 20), QP(cx + 20, cy)]))
|
||||
painter.end()
|
||||
pix.save(dest, "JPEG", 85)
|
||||
if Path(dest).exists():
|
||||
self._signals.thumb_ready.emit(index, dest)
|
||||
|
||||
def _cleanup():
|
||||
if not captured[0]:
|
||||
player.stop()
|
||||
player.deleteLater()
|
||||
|
||||
sink.videoFrameChanged.connect(_on_frame)
|
||||
player.setSource(QUrl.fromLocalFile(source))
|
||||
player.play()
|
||||
# Timeout cleanup if no frame arrives
|
||||
QTimer.singleShot(5000, _cleanup)
|
||||
threading.Thread(target=_work, daemon=True).start()
|
||||
|
||||
def _on_thumb_ready(self, index: int, path: str) -> None:
|
||||
thumbs = self._grid._thumbs
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user