2022-12-20 09:45:55 -05:00
|
|
|
import time
|
2024-09-27 11:39:18 -04:00
|
|
|
|
|
|
|
import fcntl
|
|
|
|
import struct
|
|
|
|
import termios
|
|
|
|
|
2022-12-20 09:45:55 -05:00
|
|
|
from sys import stdout
|
|
|
|
|
|
|
|
from textual import events, log
|
|
|
|
from textual.widgets import Static
|
|
|
|
|
|
|
|
from fhost import app as fhost_app
|
|
|
|
|
2024-09-27 11:39:18 -04:00
|
|
|
|
2022-12-20 09:45:55 -05:00
|
|
|
class MpvWidget(Static):
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
|
|
self.mpv = None
|
|
|
|
self.vo = fhost_app.config.get("MOD_PREVIEW_PROTO")
|
|
|
|
|
2024-09-27 11:39:18 -04:00
|
|
|
if self.vo not in ["sixel", "kitty"]:
|
|
|
|
self.update("⚠ Previews not enabled. \n\nSet MOD_PREVIEW_PROTO "
|
|
|
|
"to 'sixel' or 'kitty' in config.py,\nwhichever is "
|
|
|
|
"supported by your terminal.")
|
2022-12-20 09:45:55 -05:00
|
|
|
else:
|
|
|
|
try:
|
|
|
|
import mpv
|
|
|
|
self.mpv = mpv.MPV()
|
|
|
|
self.mpv.profile = "sw-fast"
|
|
|
|
self.mpv["vo"] = self.vo
|
|
|
|
self.mpv[f"vo-{self.vo}-config-clear"] = False
|
|
|
|
self.mpv[f"vo-{self.vo}-alt-screen"] = False
|
|
|
|
self.mpv[f"vo-sixel-buffered"] = True
|
|
|
|
self.mpv["audio"] = False
|
|
|
|
self.mpv["loop-file"] = "inf"
|
2024-09-27 11:39:18 -04:00
|
|
|
self.mpv["image-display-duration"] = 0.5 \
|
|
|
|
if self.vo == "sixel" else "inf"
|
2022-12-20 09:45:55 -05:00
|
|
|
except Exception as e:
|
|
|
|
self.mpv = None
|
2024-09-27 11:39:18 -04:00
|
|
|
self.update("⚠ Previews require python-mpv with libmpv "
|
|
|
|
"0.36.0 or later \n\nError was:\n"
|
|
|
|
f"{type(e).__name__}: {e}")
|
2022-12-20 09:45:55 -05:00
|
|
|
|
2024-09-27 11:39:18 -04:00
|
|
|
def start_mpv(self, f: str | None = None,
|
|
|
|
pos: float | str | None = None) -> None:
|
2022-12-20 09:45:55 -05:00
|
|
|
self.display = True
|
|
|
|
self.screen._refresh_layout()
|
|
|
|
|
|
|
|
if self.mpv:
|
|
|
|
if self.content_region.x:
|
2024-09-27 11:39:18 -04:00
|
|
|
winsz = fcntl.ioctl(0, termios.TIOCGWINSZ, '12345678')
|
|
|
|
r, c, w, h = struct.unpack('hhhh', winsz)
|
2022-12-20 09:45:55 -05:00
|
|
|
width = int((w / c) * self.content_region.width)
|
2024-09-27 11:39:18 -04:00
|
|
|
height = int((h / r) * (self.content_region.height +
|
|
|
|
(1 if self.vo == "sixel" else 0)))
|
2022-12-20 09:45:55 -05:00
|
|
|
self.mpv[f"vo-{self.vo}-left"] = self.content_region.x + 1
|
|
|
|
self.mpv[f"vo-{self.vo}-top"] = self.content_region.y + 1
|
2024-09-27 11:39:18 -04:00
|
|
|
self.mpv[f"vo-{self.vo}-rows"] = self.content_region.height + \
|
|
|
|
(1 if self.vo == "sixel" else 0)
|
2022-12-20 09:45:55 -05:00
|
|
|
self.mpv[f"vo-{self.vo}-cols"] = self.content_region.width
|
|
|
|
self.mpv[f"vo-{self.vo}-width"] = width
|
|
|
|
self.mpv[f"vo-{self.vo}-height"] = height
|
|
|
|
|
2024-09-27 11:39:18 -04:00
|
|
|
if pos is not None:
|
2022-12-20 09:45:55 -05:00
|
|
|
self.mpv["start"] = pos
|
|
|
|
|
|
|
|
if f:
|
|
|
|
self.mpv.loadfile(f)
|
|
|
|
else:
|
|
|
|
self.mpv.playlist_play_index(0)
|
|
|
|
|
|
|
|
def stop_mpv(self, wait: bool = False) -> None:
|
|
|
|
if self.mpv:
|
|
|
|
if not self.mpv.idle_active:
|
|
|
|
self.mpv.stop(True)
|
|
|
|
if wait:
|
|
|
|
time.sleep(0.1)
|
|
|
|
self.clear_mpv()
|
|
|
|
self.display = False
|
|
|
|
|
|
|
|
def on_resize(self, size) -> None:
|
|
|
|
if self.mpv:
|
|
|
|
if not self.mpv.idle_active:
|
|
|
|
t = self.mpv.time_pos
|
|
|
|
self.stop_mpv()
|
|
|
|
if t:
|
|
|
|
self.mpv["start"] = t
|
|
|
|
self.start_mpv()
|
|
|
|
|
|
|
|
def clear_mpv(self) -> None:
|
|
|
|
if self.vo == "kitty":
|
|
|
|
stdout.write("\033_Ga=d;\033\\")
|
|
|
|
stdout.flush()
|
|
|
|
|
|
|
|
def shutdown(self) -> None:
|
|
|
|
if self.mpv:
|
|
|
|
self.mpv.stop()
|
|
|
|
del self.mpv
|
|
|
|
if self.vo == "kitty":
|
|
|
|
stdout.write("\033_Ga=d;\033\\\033[?25l")
|
|
|
|
stdout.flush()
|