This commit is contained in:
@@ -12,7 +12,7 @@ an AVR input, not something the rest of the panel depends on.
|
||||
"""
|
||||
|
||||
import json
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
|
||||
@@ -20,20 +20,74 @@ class ZidooClient:
|
||||
def __init__(self, host, port=9529, timeout=1.5):
|
||||
self.base_url = f"http://{host}:{port}"
|
||||
self.timeout = timeout
|
||||
self._film_for = (None, {}) # (file path, _film()'s answer) last looked up
|
||||
|
||||
def _get(self, route, **params):
|
||||
"""The raw body of a GET, or None if the Zidoo did not answer."""
|
||||
query = f"?{urllib.parse.urlencode(params)}" if params else ""
|
||||
try:
|
||||
with urllib.request.urlopen(f"{self.base_url}/{route}{query}", timeout=self.timeout) as response:
|
||||
return response.read()
|
||||
except OSError: # URLError, and a timeout mid-read, which urlopen does not wrap
|
||||
return None
|
||||
|
||||
def _get_json(self, route, **params):
|
||||
body = self._get(route, **params)
|
||||
try:
|
||||
return json.loads(body) if body is not None else None
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def now_playing(self):
|
||||
"""{"song", "artist", "image"} for whatever video is loaded, in the
|
||||
same shape a room's now-playing card already expects -- or None."""
|
||||
try:
|
||||
with urllib.request.urlopen(
|
||||
f"{self.base_url}/ZidooVideoPlay/getPlayStatus", timeout=self.timeout
|
||||
) as response:
|
||||
payload = json.loads(response.read())
|
||||
except (urllib.error.URLError, ValueError):
|
||||
same shape a room's now-playing card already expects -- or None.
|
||||
A film has no artist, so that line under the title carries its year
|
||||
instead. "image" is always None: the poster lives on the Zidoo, which
|
||||
the phone cannot load over plain http, so "poster_id" names it
|
||||
instead for app.py to serve. "position_ms" and "duration_ms" come
|
||||
along whenever the length is known, as a room's do."""
|
||||
payload = self._get_json("ZidooVideoPlay/getPlayStatus")
|
||||
if not payload or payload.get("status") != 200:
|
||||
return None
|
||||
if payload.get("status") != 200:
|
||||
return None
|
||||
title = (payload.get("video") or {}).get("title")
|
||||
video = payload.get("video") or {}
|
||||
title = video.get("title")
|
||||
if not title:
|
||||
return None
|
||||
return {"song": title, "artist": None, "image": None}
|
||||
film = self._film(video.get("path"))
|
||||
year = film.get("year")
|
||||
track = {"song": title, "artist": str(year) if year else None, "image": None,
|
||||
"poster_id": film.get("id")}
|
||||
if video.get("duration"):
|
||||
track["position_ms"] = video.get("currentPosition") or 0
|
||||
track["duration_ms"] = video["duration"]
|
||||
return track
|
||||
|
||||
def _film(self, path):
|
||||
"""The poster wall's entry for the film a file belongs to -- its
|
||||
"id" (which is also its poster's) and "year" among the rest -- or {}
|
||||
when the file is not in the library. Looked up once per file rather
|
||||
than on every poll: the answer comes wrapped in the film's whole cast
|
||||
and crew, and does not change halfway through it."""
|
||||
if not path:
|
||||
return {}
|
||||
if self._film_for[0] != path:
|
||||
payload = self._get_json("ZidooPoster/v2/getAggregationOfFile", path=path)
|
||||
if payload is None:
|
||||
return {} # unreachable for now -- ask again next poll
|
||||
# "type" names the key holding the item itself: "movie" for a
|
||||
# film. Anything else without one simply gets no poster or year.
|
||||
self._film_for = (path, payload.get(payload.get("type")) or {})
|
||||
return self._film_for[1]
|
||||
|
||||
def poster(self, poster_id, width=200, height=300):
|
||||
"""(bytes, mimetype) for a film's poster, or None. The Zidoo sends
|
||||
no Content-Type, and answers an id it has no poster for with a JSON
|
||||
error instead, so the image is told apart by its first bytes."""
|
||||
body = self._get("ZidooPoster/getFile/getPoster", id=poster_id, w=width, h=height)
|
||||
if body is None:
|
||||
return None
|
||||
if body.startswith(b"\x89PNG"):
|
||||
return body, "image/png"
|
||||
if body.startswith(b"\xff\xd8"):
|
||||
return body, "image/jpeg"
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user