0x0/modui/mpvwidget.py
2024-09-27 18:30:33 +02:00

102 lines
3.5 KiB
Python

import time
import fcntl
import struct
import termios
from sys import stdout
from textual import events, log
from textual.widgets import Static
from fhost import app as fhost_app
class MpvWidget(Static):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.mpv = None
self.vo = fhost_app.config.get("MOD_PREVIEW_PROTO")
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.")
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"
self.mpv["image-display-duration"] = 0.5 \
if self.vo == "sixel" else "inf"
except Exception as e:
self.mpv = None
self.update("⚠ Previews require python-mpv with libmpv "
"0.36.0 or later \n\nError was:\n"
f"{type(e).__name__}: {e}")
def start_mpv(self, f: str | None = None,
pos: float | str | None = None) -> None:
self.display = True
self.screen._refresh_layout()
if self.mpv:
if self.content_region.x:
winsz = fcntl.ioctl(0, termios.TIOCGWINSZ, '12345678')
r, c, w, h = struct.unpack('hhhh', winsz)
width = int((w / c) * self.content_region.width)
height = int((h / r) * (self.content_region.height +
(1 if self.vo == "sixel" else 0)))
self.mpv[f"vo-{self.vo}-left"] = self.content_region.x + 1
self.mpv[f"vo-{self.vo}-top"] = self.content_region.y + 1
self.mpv[f"vo-{self.vo}-rows"] = self.content_region.height + \
(1 if self.vo == "sixel" else 0)
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
if pos is not None:
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()