webOS: a TV app with the views of arr-tui
A TypeScript web app for LG webOS TVs (webOS 5 / Chromium 68 and newer), driven by the remote: Movies and Series search with the Trakt lists, Library with status, title and file-property filters and download marks, seasons and episodes, release search and grab, Downloads, Calendar, Settings and Log. It talks to Radarr, Sonarr, SABnzbd and Trakt directly (they answer cross-origin requests); the arr-tui config is baked in at build time and can be changed on the TV. dev/screenshots/server.py answers cross-origin requests and accepts writes for it; webos/dev/drive.mjs drives the app in Chromium for screenshots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -220,6 +220,27 @@ def sab(q):
|
||||
def handler(port):
|
||||
class H(BaseHTTPRequestHandler):
|
||||
def log_message(self, *a): pass
|
||||
# Like the real services, answer cross-origin requests (the webOS app
|
||||
# runs in a browser).
|
||||
def end_headers(self):
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
super().end_headers()
|
||||
def do_OPTIONS(self):
|
||||
self.send_response(204)
|
||||
self.send_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
|
||||
self.send_header("Access-Control-Allow-Headers", self.headers.get("Access-Control-Request-Headers", "*"))
|
||||
self.end_headers()
|
||||
# Writes (add, grab, commands, monitoring, removal) are accepted and
|
||||
# change nothing; an added title comes back with an id.
|
||||
def do_POST(self):
|
||||
n = int(self.headers.get("Content-Length") or 0)
|
||||
body = json.loads(self.rfile.read(n) or b"{}") if n else {}
|
||||
out = dict(body, id=900) if isinstance(body, dict) and self.path.split("?")[0].endswith(("/movie", "/series")) else {}
|
||||
b = json.dumps(out).encode()
|
||||
self.send_response(201 if out else 200); self.send_header("Content-Type", "application/json"); self.send_header("Content-Length", str(len(b))); self.end_headers(); self.wfile.write(b)
|
||||
do_PUT = do_POST
|
||||
def do_DELETE(self):
|
||||
self.send_response(200); self.send_header("Content-Length", "0"); self.end_headers()
|
||||
def do_GET(self):
|
||||
u = urllib.parse.urlparse(self.path); q = urllib.parse.parse_qs(u.query); path = u.path
|
||||
if path.startswith("/MediaCover/") or path.startswith("/remote/"):
|
||||
|
||||
Reference in New Issue
Block a user