From 96503ca1c221ce4547e9ffe41484660837280507 Mon Sep 17 00:00:00 2001 From: plsdoant Date: Wed, 3 Jun 2026 23:16:57 -0700 Subject: [PATCH 1/6] weather idle screen switcher for when the tv is not in use --- receive.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 receive.py diff --git a/receive.py b/receive.py new file mode 100644 index 0000000..09ab483 --- /dev/null +++ b/receive.py @@ -0,0 +1,98 @@ +import argparse +import json +import threading +import time +import urllib.request +from urllib.parse import urlparse +import vlc + +# Set when the sce-tv server reports "idle", cleared when it is "playing" +idle_event = threading.Event() + +def get_stream_state(ip, port=5001, interval=5): + + # Continuously ask the sce-tv server at the given IP for the current state, "idle" or "playing" + url = f"http://{ip}:{port}/state" + while True: + try: + with urllib.request.urlopen(url, timeout=5) as response: + data = json.loads(response.read()) + state = "playing" if data.get("state") == "playing" else "idle" + print(f"sce-tv state: {state}") + if state == "idle": + idle_event.set() + else: + idle_event.clear() + except Exception as e: + print(f"Could not reach sce-tv server at {url}: {e}") + time.sleep(interval) + +def play_stream(url): + instance = vlc.Instance() + player = instance.media_player_new() + + switch_stream(instance, player, url) + print("Starting playback...") + + # Buffer + time.sleep(5) + + return instance, player + +def switch_stream(instance, player, url): + media = instance.media_new(url) + player.set_media(media) + player.play() + +def stream_switcher(instance, player, sce_tv_url, weather_url): + + # Swap the active stream depending on playing state + # idle: weather channel, playing: sce-tv + while True: + # Block until the server reports idle, then switch to weather + idle_event.wait() + print("sce-tv idle, switching to the weather channel...") + switch_stream(instance, player, weather_url) + + # Block until the server is playing, then switch to sce-tv + while idle_event.is_set(): + time.sleep(1) + print("sce-tv playing, switching back to sce-tv...") + switch_stream(instance, player, sce_tv_url) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--sce-tv-rtmp-url", + required=True, + help="RTMP stream URL (e.g. rtmp://server/live/streamkey)" + ) + parser.add_argument( + "--weather-rtmp-url", + help="weather channel RTMP stream URL (e.g. rtmp://server/live/weather)" + ) + args = parser.parse_args() + + # State API runs on the same host as the RTMP server, so derive it from the stream URL rather than passing the host separately + sce_tv_ip = urlparse(args.sce_tv_rtmp_url).hostname + + state_thread = threading.Thread( + target=get_stream_state, + args=(sce_tv_ip,), + daemon=True, + ) + state_thread.start() + + instance, player = play_stream(args.sce_tv_rtmp_url) + + # Only enable idle-based switching when a weather channel URL is provided + if args.weather_rtmp_url: + switcher_thread = threading.Thread( + target=stream_switcher, + args=(instance, player, args.sce_tv_rtmp_url, args.weather_rtmp_url), + daemon=True, + ) + switcher_thread.start() + + while True: + time.sleep(1) From 2adaf8e207f5d25b1591332337612c728103b822 Mon Sep 17 00:00:00 2001 From: plsdoant Date: Thu, 4 Jun 2026 21:46:41 -0700 Subject: [PATCH 2/6] change 'stream_switcher' function name to 'work' per evan request --- receive.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/receive.py b/receive.py index 09ab483..d4352e7 100644 --- a/receive.py +++ b/receive.py @@ -44,7 +44,7 @@ def switch_stream(instance, player, url): player.set_media(media) player.play() -def stream_switcher(instance, player, sce_tv_url, weather_url): +def work(instance, player, sce_tv_url, weather_url): # Swap the active stream depending on playing state # idle: weather channel, playing: sce-tv @@ -88,7 +88,7 @@ def stream_switcher(instance, player, sce_tv_url, weather_url): # Only enable idle-based switching when a weather channel URL is provided if args.weather_rtmp_url: switcher_thread = threading.Thread( - target=stream_switcher, + target=work, args=(instance, player, args.sce_tv_rtmp_url, args.weather_rtmp_url), daemon=True, ) From 3d0ffaa1bbf4accddf76e92e1edd894c76ebe7df Mon Sep 17 00:00:00 2001 From: plsdoant Date: Sun, 7 Jun 2026 22:04:01 -0700 Subject: [PATCH 3/6] refactor to improve switching between sce-tv and weather channel --- receive.py | 116 ++++++++++++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/receive.py b/receive.py index d4352e7..ac73a7c 100644 --- a/receive.py +++ b/receive.py @@ -1,64 +1,66 @@ import argparse import json +import queue import threading import time import urllib.request from urllib.parse import urlparse import vlc -# Set when the sce-tv server reports "idle", cleared when it is "playing" -idle_event = threading.Event() +receive_weather = threading.Event() +receive_sce_tv = threading.Event() -def get_stream_state(ip, port=5001, interval=5): +commands = queue.Queue() - # Continuously ask the sce-tv server at the given IP for the current state, "idle" or "playing" +def get_stream_state(ip, port=5001, timeout=5): + + # Ask the sce-tv server at the given IP for the current state, "idle" or "playing" url = f"http://{ip}:{port}/state" - while True: - try: - with urllib.request.urlopen(url, timeout=5) as response: - data = json.loads(response.read()) - state = "playing" if data.get("state") == "playing" else "idle" - print(f"sce-tv state: {state}") - if state == "idle": - idle_event.set() - else: - idle_event.clear() - except Exception as e: - print(f"Could not reach sce-tv server at {url}: {e}") - time.sleep(interval) + try: + with urllib.request.urlopen(url, timeout=timeout) as response: + data = json.loads(response.read()) + return "playing" if data.get("state") == "playing" else "idle" + except Exception as e: + print(f"Could not reach sce-tv server at {url}: {e}") + return None -def play_stream(url): - instance = vlc.Instance() - player = instance.media_player_new() +def receive_stream(instance, player, urls): - switch_stream(instance, player, url) + media = instance.media_new(urls[receive_sce_tv]) + player.set_media(media) + player.play() print("Starting playback...") # Buffer time.sleep(5) - return instance, player - -def switch_stream(instance, player, url): - media = instance.media_new(url) - player.set_media(media) - player.play() + # Consume state commands and switch the active stream to match + while True: + command = commands.get() + url = urls[command] + print(f"switching stream to {url}...") + media = instance.media_new(url) + player.set_media(media) + player.play() -def work(instance, player, sce_tv_url, weather_url): +def signal(ip, weather_url, interval=5): - # Swap the active stream depending on playing state - # idle: weather channel, playing: sce-tv + # Defaults to sce-tv, only switches to weather when server is idle and weather url is provided, pushes command when the state changes while True: - # Block until the server reports idle, then switch to weather - idle_event.wait() - print("sce-tv idle, switching to the weather channel...") - switch_stream(instance, player, weather_url) + state = get_stream_state(ip) - # Block until the server is playing, then switch to sce-tv - while idle_event.is_set(): - time.sleep(1) - print("sce-tv playing, switching back to sce-tv...") - switch_stream(instance, player, sce_tv_url) + if weather_url and state == "idle": + target = receive_weather + else: + target = receive_sce_tv + + if not target.is_set(): + receive_weather.clear() + receive_sce_tv.clear() + target.set() + commands.put(target) + + time.sleep(interval) if __name__ == "__main__": parser = argparse.ArgumentParser() @@ -76,23 +78,29 @@ def work(instance, player, sce_tv_url, weather_url): # State API runs on the same host as the RTMP server, so derive it from the stream URL rather than passing the host separately sce_tv_ip = urlparse(args.sce_tv_rtmp_url).hostname - state_thread = threading.Thread( - target=get_stream_state, - args=(sce_tv_ip,), + urls = { + receive_sce_tv: args.sce_tv_rtmp_url, + receive_weather: args.weather_rtmp_url, + } + + receive_sce_tv.set() + + instance = vlc.Instance() + player = instance.media_player_new() + + receive_stream_thread = threading.Thread( + target=receive_stream, + args=(instance, player, urls), + daemon=True, + ) + receive_stream_thread.start() + + signal_thread = threading.Thread( + target=signal, + args=(sce_tv_ip, args.weather_rtmp_url), daemon=True, ) - state_thread.start() - - instance, player = play_stream(args.sce_tv_rtmp_url) - - # Only enable idle-based switching when a weather channel URL is provided - if args.weather_rtmp_url: - switcher_thread = threading.Thread( - target=work, - args=(instance, player, args.sce_tv_rtmp_url, args.weather_rtmp_url), - daemon=True, - ) - switcher_thread.start() + signal_thread.start() while True: time.sleep(1) From 1045693dd24a46b3d78f65bb7f49105715e216f1 Mon Sep 17 00:00:00 2001 From: plsdoant Date: Mon, 8 Jun 2026 23:16:41 -0700 Subject: [PATCH 4/6] changed exception message to log instead of print --- receive.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/receive.py b/receive.py index ac73a7c..ea6dfb9 100644 --- a/receive.py +++ b/receive.py @@ -1,5 +1,6 @@ import argparse import json +import logging import queue import threading import time @@ -20,8 +21,8 @@ def get_stream_state(ip, port=5001, timeout=5): with urllib.request.urlopen(url, timeout=timeout) as response: data = json.loads(response.read()) return "playing" if data.get("state") == "playing" else "idle" - except Exception as e: - print(f"Could not reach sce-tv server at {url}: {e}") + except Exception: + logging.exception("Could not reach sce-tv server at %s", url) return None def receive_stream(instance, player, urls): From cb8bc83afa91c2130611ae30feac660f27bc816e Mon Sep 17 00:00:00 2001 From: plsdoant Date: Mon, 8 Jun 2026 23:26:04 -0700 Subject: [PATCH 5/6] small change to log errors --- receive.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/receive.py b/receive.py index ea6dfb9..2036fc4 100644 --- a/receive.py +++ b/receive.py @@ -64,6 +64,13 @@ def signal(ip, weather_url, interval=5): time.sleep(interval) if __name__ == "__main__": + logging.Formatter.converter = time.gmtime + + logging.basicConfig( + format="%(asctime)s.%(msecs)03dZ %(levelname)s:%(name)s:%(message)s", + datefmt="%Y-%m-%dT%H:%M:%S", + level=logging.ERROR + ) parser = argparse.ArgumentParser() parser.add_argument( "--sce-tv-rtmp-url", From 971d955e3d015d5d988ef61a8f79b01a1b7d875d Mon Sep 17 00:00:00 2001 From: plsdoant Date: Wed, 10 Jun 2026 14:16:53 -0700 Subject: [PATCH 6/6] changed other print statements to use logging as well --- receive.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/receive.py b/receive.py index 2036fc4..7539e5f 100644 --- a/receive.py +++ b/receive.py @@ -30,7 +30,7 @@ def receive_stream(instance, player, urls): media = instance.media_new(urls[receive_sce_tv]) player.set_media(media) player.play() - print("Starting playback...") + logging.info("Starting playback...") # Buffer time.sleep(5) @@ -39,7 +39,7 @@ def receive_stream(instance, player, urls): while True: command = commands.get() url = urls[command] - print(f"switching stream to {url}...") + logging.info("switching stream to %s...", url) media = instance.media_new(url) player.set_media(media) player.play()