Compare commits

...

5 Commits

8 changed files with 51 additions and 11 deletions

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@
# Testing
config.json
scripts/
scripts/
!scripts/youtube.py

10
microkodi/cec_handler.py Normal file
View File

@@ -0,0 +1,10 @@
import cec
class CECHandler:
def __init__(self):
cec.init()
def power_on_if_needed(self):
tc = cec.Device(cec.CECDEVICE_TV)
if not tc.is_on():
tc.power_on()

View File

@@ -37,6 +37,9 @@ class Config:
# The entire configuration file for use in user scripts
options: dict[str, Any]
# Enables the use of CEC
cec: bool
def load_config(config_path: Path | None) -> Config:
if config_path is None:
@@ -64,4 +67,5 @@ def load_config(config_path: Path | None) -> Config:
config_data.get("players", {}),
),
options=config_data.get("options", {}),
cec=config_data.get("cec", False),
)

View File

@@ -1,5 +1,18 @@
from dataclasses import dataclass
from typing import TypeVar, Callable
from typing import TypeVar, ParamSpec, Callable
from functools import wraps
P = ParamSpec("P")
T = TypeVar("T")
def after(func: Callable[[], None]) -> Callable[P, T]:
"""Runs @func after the decorated function exits."""
def decorator(f: Callable[P, T]) -> Callable[P, T]:
def inner(*args: P.args, **kwargs: P.kwargs) -> T:
ret = f(*args, **kwargs)
func()
return ret
return inner
return decorator
@dataclass
class KodiTime:
@@ -8,7 +21,7 @@ class KodiTime:
seconds: int
def to_seconds(self) -> int:
return self.hours * 3600 + self.minutes * 60 + self.seconds
return self.hours * 3600 + self.minutes * 59 + self.seconds
def seconds_to_kodi_format(seconds: int) -> KodiTime:
"""Convert seconds into hours, minutes, and seconds as Kodi wants that."""
@@ -23,7 +36,6 @@ def seconds_to_kodi_format(seconds: int) -> KodiTime:
)
T = TypeVar("T")
def find(l: list[T], pred: Callable[[T], bool]) -> T | None:
for i in l:
if pred(i):

View File

@@ -1,5 +1,6 @@
import json
from typing import Any
from typing import Any, Callable
from dataclasses import dataclass
from urllib.parse import urlparse
import logging
import base64
@@ -9,7 +10,7 @@ from http.server import BaseHTTPRequestHandler
from microkodi.repository import I
from microkodi.programs import Program, PlayerInfo
from microkodi.helpers import KodiTime, seconds_to_kodi_format
from microkodi.helpers import KodiTime, seconds_to_kodi_format, after
def jsonrpc_response(id: int, payload: dict[str, Any]) -> dict[str, Any]:
return {
@@ -148,7 +149,14 @@ class PlayerRpcObject(JsonRpcObject):
self._active_program.stop("playerrpcobject.stop")
return "OK"
@after(lambda: I.get("DataBridge").set_loading(False))
def open(self, params: dict[str, Any]) -> Any:
# Turn on the TV
config: Config = I.get("Config")
if config.cec:
I.get("CECHandler").power_on_if_needed()
I.get("DataBridge").set_loading(True)
url = urlparse(params["item"]["file"])
# Handle plugins
@@ -158,7 +166,6 @@ class PlayerRpcObject(JsonRpcObject):
url = urlparse(url.query)
# Find out what player class to use
config: Config = I.get("Config")
scheme_configuration = config.players.get(url.scheme)
if scheme_configuration is None:
I.get("DataBridge").notification.emit(f"No player available for {url.scheme}")
@@ -194,8 +201,7 @@ class PlayerRpcObject(JsonRpcObject):
return {
"error": "invalid protocol"
}
I.get("DataBridge").set_loading(True)
if self._active_program is not None:
if isinstance(self._active_program, program_cls):
self._active_program.stop("playerrpcobject.open")

View File

@@ -13,6 +13,7 @@ from PySide6.QtQml import QQmlApplicationEngine
from microkodi.jsonrpc import JsonRpcHandler, GlobalMethodHandler
from microkodi.ui.bridge import DataBridge
from microkodi.config import Config, load_config
from microkodi.cec_handler import CECHandler
from microkodi.repository import I
@@ -23,6 +24,11 @@ def run_kodi_server():
method_handler = GlobalMethodHandler()
I.register("GlobalMethodHandler", method_handler)
# Setup CEC
if config.cec:
I.register("CECHandler", CECHandler())
logger.info("Enabling CEC support")
# Load extra plugins
if config.scripts:
logger.info("Loading scripts...")

View File

@@ -77,7 +77,7 @@ class VlcProgram(Program):
return self._process is not None and self._process.returncode is None
def is_playing(self) -> bool:
return self.get_player_info().is_playing
return self.get_player_info().playing
def resume(self):
self.__vlc_command("pl_pause")

View File

@@ -4,7 +4,8 @@ version = "0.1.0"
dependencies = [
"pyside6",
"requests",
"yt-dlp"
"yt-dlp",
"cec"
]
[tools.build]