From 57f996f35269f43da627a4f64925f6b98b3dab25 Mon Sep 17 00:00:00 2001 From: "Arthur K." Date: Sun, 3 May 2026 08:18:13 +0300 Subject: [PATCH] init --- README.md | 3 ++ addon.xml | 16 +++++++ service.py | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 README.md create mode 100644 addon.xml create mode 100644 service.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..c7c5ad8 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# PVR Stretch 4:3 + +Set stretch 4:3 view mode for PVR channels diff --git a/addon.xml b/addon.xml new file mode 100644 index 0000000..c04c0b3 --- /dev/null +++ b/addon.xml @@ -0,0 +1,16 @@ + + + + + + + + Set stretch 4:3 view mode for PVR channels + Set stretch 4:3 view mode for PVR channels + all + MIT + + diff --git a/service.py b/service.py new file mode 100644 index 0000000..eded9ec --- /dev/null +++ b/service.py @@ -0,0 +1,126 @@ +import json +import threading + +import xbmc +import xbmcaddon + + +ADDON = xbmcaddon.Addon() +ADDON_ID = ADDON.getAddonInfo("id") +VIEW_MODE = "stretch4x3" + + +def log(message, level=xbmc.LOGINFO): + xbmc.log(f"[{ADDON_ID}] {message}", level) + + +def jsonrpc(method, params=None): + payload = { + "jsonrpc": "2.0", + "id": 1, + "method": method, + } + if params is not None: + payload["params"] = params + + request = json.dumps(payload) + + response = xbmc.executeJSONRPC(request) + try: + data = json.loads(response) + except ValueError: + log(f"Invalid JSON-RPC response from {method}: {response}", xbmc.LOGERROR) + return None + + if "error" in data: + log(f"JSON-RPC {method} failed: {data['error']}", xbmc.LOGWARNING) + return None + + return data.get("result") + + +def get_active_video_player_id(): + result = jsonrpc("Player.GetActivePlayers") + if not isinstance(result, list): + return None + + for player in result: + if player.get("type") == "video": + return player.get("playerid") + + return None + + +def get_current_item(player_id): + result = jsonrpc( + "Player.GetItem", + { + "playerid": player_id, + "properties": ["file"], + }, + ) + if not isinstance(result, dict): + return None + + item = result.get("item") + if not isinstance(item, dict): + return None + + return item + + +class PvrStretchPlayer(xbmc.Player): + def __init__(self, monitor): + super().__init__() + self.monitor = monitor + + def onPlayBackStarted(self): + threading.Thread(target=self._apply, daemon=True).start() + + def _apply(self): + item = None + + for _ in range(100): + if self.monitor.abortRequested(): + return + + player_id = get_active_video_player_id() + if player_id is not None: + item = get_current_item(player_id) + if item: + break + + if self.monitor.waitForAbort(0.1): + return + + if not item: + log("No player item found after playback start", xbmc.LOGWARNING) + return + + item_type = (item or {}).get("type") + if item_type != "channel": + return + + log(f"Applying {VIEW_MODE}: type={item_type}") + result = jsonrpc("Player.SetViewMode", {"viewmode": VIEW_MODE}) + if result is None: + log(f"Failed to apply {VIEW_MODE}", xbmc.LOGWARNING) + return + + log(f"Applied {VIEW_MODE}: result={result}") + + +def run(): + monitor = xbmc.Monitor() + player = PvrStretchPlayer(monitor) + log("Service started") + + while not monitor.abortRequested(): + monitor.waitForAbort(1) + + del player + log("Service stopped") + + +if __name__ == "__main__": + run()