[mirror of https://git.0x0.st/mia/0x0] No-bullshit file hosting and URL shortening service https://0x0.st
at master 102 lines 3.6 kB view raw
1import time 2 3import fcntl 4import struct 5import termios 6 7from sys import stdout 8 9from textual import events, log 10from textual.widgets import Static 11 12from fhost import app as fhost_app 13 14 15class MpvWidget(Static): 16 def __init__(self, **kwargs): 17 super().__init__(**kwargs) 18 19 self.mpv = None 20 self.vo = fhost_app.config.get("MOD_PREVIEW_PROTO") 21 22 if self.vo not in ["sixel", "kitty"]: 23 self.update("⚠ Previews not enabled. \n\nSet MOD_PREVIEW_PROTO " 24 "to 'sixel' or 'kitty' in config.py,\nwhichever is " 25 "supported by your terminal.") 26 else: 27 try: 28 import mpv 29 self.mpv = mpv.MPV() 30 self.mpv.profile = "sw-fast" 31 self.mpv["vo"] = self.vo 32 self.mpv[f"vo-{self.vo}-config-clear"] = False 33 self.mpv[f"vo-{self.vo}-alt-screen"] = False 34 self.mpv[f"vo-sixel-buffered"] = True 35 self.mpv["audio"] = False 36 self.mpv["loop-file"] = "inf" 37 self.mpv["image-display-duration"] = 0.5 \ 38 if self.vo == "sixel" else "inf" 39 except Exception as e: 40 self.mpv = None 41 self.update("⚠ Previews require python-mpv with libmpv " 42 "0.36.0 or later \n\nError was:\n" 43 f"{type(e).__name__}: {e}") 44 45 def start_mpv(self, f: str | None = None, 46 pos: float | str | None = None) -> None: 47 self.display = True 48 self.screen._refresh_layout() 49 50 if self.mpv: 51 if self.content_region.x: 52 winsz = fcntl.ioctl(0, termios.TIOCGWINSZ, '12345678') 53 r, c, w, h = struct.unpack('hhhh', winsz) 54 width = int((w / c) * self.content_region.width) 55 height = int((h / r) * (self.content_region.height + 56 (1 if self.vo == "sixel" else 0))) 57 self.mpv[f"vo-{self.vo}-left"] = self.content_region.x + 1 58 self.mpv[f"vo-{self.vo}-top"] = self.content_region.y + 1 59 self.mpv[f"vo-{self.vo}-rows"] = self.content_region.height + \ 60 (1 if self.vo == "sixel" else 0) 61 self.mpv[f"vo-{self.vo}-cols"] = self.content_region.width 62 self.mpv[f"vo-{self.vo}-width"] = width 63 self.mpv[f"vo-{self.vo}-height"] = height 64 65 if pos is not None: 66 self.mpv["start"] = pos 67 68 if f: 69 self.mpv.loadfile(f) 70 else: 71 self.mpv.playlist_play_index(0) 72 73 def stop_mpv(self, wait: bool = False) -> None: 74 if self.mpv: 75 if not self.mpv.idle_active: 76 self.mpv.stop(True) 77 if wait: 78 time.sleep(0.1) 79 self.clear_mpv() 80 self.display = False 81 82 def on_resize(self, size) -> None: 83 if self.mpv: 84 if not self.mpv.idle_active: 85 t = self.mpv.time_pos 86 self.stop_mpv() 87 if t: 88 self.mpv["start"] = t 89 self.start_mpv() 90 91 def clear_mpv(self) -> None: 92 if self.vo == "kitty": 93 stdout.write("\033_Ga=d;\033\\") 94 stdout.flush() 95 96 def shutdown(self) -> None: 97 if self.mpv: 98 self.mpv.stop() 99 del self.mpv 100 if self.vo == "kitty": 101 stdout.write("\033_Ga=d;\033\\\033[?25l") 102 stdout.flush()