Completed player
This commit is contained in:
Vendored
+26
-2
@@ -1,4 +1,28 @@
|
|||||||
[
|
[
|
||||||
"CCAAefnX5vo",
|
"1MwfBYzvB2A",
|
||||||
"vzGpVce0Puk"
|
"5BdVtbwQnHs",
|
||||||
|
"5Pju9ITqLaY",
|
||||||
|
"Hk-9QNAD6V4",
|
||||||
|
"Ljl4aFILsOw",
|
||||||
|
"LqzHTDma900",
|
||||||
|
"M5n_-8IYDBk",
|
||||||
|
"OOgnJKo13Hs",
|
||||||
|
"Vyw3I1Kqets",
|
||||||
|
"ZaIxE5leHug",
|
||||||
|
"acktYyOGG6w",
|
||||||
|
"dALKtBPDd_E",
|
||||||
|
"eMxGu_Zk3OI",
|
||||||
|
"frGAB2aVHJA",
|
||||||
|
"iqyxMTAqr5Q",
|
||||||
|
"kQfdo-ghfMY",
|
||||||
|
"kjEFOaGnYcU",
|
||||||
|
"lH4jPkFcj3k",
|
||||||
|
"nPv3Evsc4jo",
|
||||||
|
"nxJeXwN0jiM",
|
||||||
|
"oLixTqKVPfo",
|
||||||
|
"q8ofnoWFpx4",
|
||||||
|
"qCcZRavCOyc",
|
||||||
|
"uIdUC83QOpg",
|
||||||
|
"yixWRocskp0",
|
||||||
|
"ykS7VDAiFAc"
|
||||||
]
|
]
|
||||||
Vendored
+3
@@ -2,8 +2,10 @@
|
|||||||
"0jft6MfWN6k",
|
"0jft6MfWN6k",
|
||||||
"3fZDS_5Jv5E",
|
"3fZDS_5Jv5E",
|
||||||
"5NZFgqoWMq0",
|
"5NZFgqoWMq0",
|
||||||
|
"5aHJSSnjlhk",
|
||||||
"BF5pNOZF934",
|
"BF5pNOZF934",
|
||||||
"RwpTUmzz_oY",
|
"RwpTUmzz_oY",
|
||||||
|
"TlZCx3NTRMA",
|
||||||
"UIzIfEMpFV0",
|
"UIzIfEMpFV0",
|
||||||
"WYcpL6otp_Y",
|
"WYcpL6otp_Y",
|
||||||
"YwsTqkfOpcg",
|
"YwsTqkfOpcg",
|
||||||
@@ -11,6 +13,7 @@
|
|||||||
"ds2H1hbm6xM",
|
"ds2H1hbm6xM",
|
||||||
"f2oxGYpuLkw",
|
"f2oxGYpuLkw",
|
||||||
"hVcUqZ3KryA",
|
"hVcUqZ3KryA",
|
||||||
|
"klPEGFmSyUo",
|
||||||
"qOD9M95_fS0",
|
"qOD9M95_fS0",
|
||||||
"vh70Rbmcg8g",
|
"vh70Rbmcg8g",
|
||||||
"yNQp9toTAGo",
|
"yNQp9toTAGo",
|
||||||
|
|||||||
+8
-3
@@ -1,5 +1,6 @@
|
|||||||
zones:
|
zones:
|
||||||
sanctuary:
|
sanctuary:
|
||||||
|
active: true
|
||||||
jack_input: "sanctuary"
|
jack_input: "sanctuary"
|
||||||
ip: "10.40.0.100"
|
ip: "10.40.0.100"
|
||||||
port: 5004
|
port: 5004
|
||||||
@@ -7,6 +8,7 @@ zones:
|
|||||||
right: "sanctuary:in_r"
|
right: "sanctuary:in_r"
|
||||||
|
|
||||||
prayer:
|
prayer:
|
||||||
|
active: true
|
||||||
jack_input: "prayer"
|
jack_input: "prayer"
|
||||||
ip: "10.40.0.101"
|
ip: "10.40.0.101"
|
||||||
port: 5004
|
port: 5004
|
||||||
@@ -14,6 +16,7 @@ zones:
|
|||||||
right: "prayer:in_r"
|
right: "prayer:in_r"
|
||||||
|
|
||||||
office:
|
office:
|
||||||
|
active: false
|
||||||
jack_input: "office"
|
jack_input: "office"
|
||||||
ip: "10.40.0.102"
|
ip: "10.40.0.102"
|
||||||
port: 5004
|
port: 5004
|
||||||
@@ -21,6 +24,7 @@ zones:
|
|||||||
right: "office:in_r"
|
right: "office:in_r"
|
||||||
|
|
||||||
fellowship:
|
fellowship:
|
||||||
|
active: false
|
||||||
jack_input: "fellowship"
|
jack_input: "fellowship"
|
||||||
ip: "10.40.0.103"
|
ip: "10.40.0.103"
|
||||||
port: 5004
|
port: 5004
|
||||||
@@ -28,6 +32,7 @@ zones:
|
|||||||
right: "fellowship:in_r"
|
right: "fellowship:in_r"
|
||||||
|
|
||||||
Macbook:
|
Macbook:
|
||||||
|
active: true
|
||||||
jack_input: "Mac"
|
jack_input: "Mac"
|
||||||
ip: "192.168.2.1"
|
ip: "192.168.2.1"
|
||||||
port: 5004
|
port: 5004
|
||||||
@@ -35,17 +40,17 @@ zones:
|
|||||||
right: "Mac:in_r"
|
right: "Mac:in_r"
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
praise:
|
Praise:
|
||||||
left: "Praise:left"
|
left: "Praise:left"
|
||||||
right: "Praise:right"
|
right: "Praise:right"
|
||||||
youtube_url: "https://youtube.com/playlist?list=PLJjON0cPmpcvJaax7cNGjYJeSGCPKHxuF&si=cEfJhukvRhrul2oW"
|
youtube_url: "https://youtube.com/playlist?list=PLJjON0cPmpcvJaax7cNGjYJeSGCPKHxuF&si=cEfJhukvRhrul2oW"
|
||||||
id_cache: "/home/media/Scripts/ZonePlayer/cache/praise_ids.json"
|
id_cache: "/home/media/Scripts/ZonePlayer/cache/praise_ids.json"
|
||||||
worship:
|
Worship:
|
||||||
left: "Worship:left"
|
left: "Worship:left"
|
||||||
right: "Worship:right"
|
right: "Worship:right"
|
||||||
youtube_url: "https://youtube.com/playlist?list=PLJjON0cPmpctXc5x8--6lZDKjrprVvTkd&si=0El4d6t2lgtVYS69"
|
youtube_url: "https://youtube.com/playlist?list=PLJjON0cPmpctXc5x8--6lZDKjrprVvTkd&si=0El4d6t2lgtVYS69"
|
||||||
id_cache: "/home/media/Scripts/ZonePlayer/cache/worship_ids.json"
|
id_cache: "/home/media/Scripts/ZonePlayer/cache/worship_ids.json"
|
||||||
instrumental:
|
Instrumental:
|
||||||
left: "Instrumental:left"
|
left: "Instrumental:left"
|
||||||
right: "Instrumental:right"
|
right: "Instrumental:right"
|
||||||
youtube_url: "https://youtube.com/playlist?list=PLJjON0cPmpcuJwDtp6OczGJADZB4HYrAm&si=vtu-vQnY3wIgDzYB"
|
youtube_url: "https://youtube.com/playlist?list=PLJjON0cPmpcuJwDtp6OczGJADZB4HYrAm&si=vtu-vQnY3wIgDzYB"
|
||||||
|
|||||||
+39
-26
@@ -1,13 +1,14 @@
|
|||||||
import jack # type: ignore
|
import jack
|
||||||
import time
|
import time
|
||||||
import numpy as np # type: ignore
|
import numpy as np
|
||||||
from pydub import AudioSegment # type: ignore
|
from pydub import AudioSegment
|
||||||
import random
|
import random
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import paho.mqtt.client as mqtt # type: ignore
|
import yaml
|
||||||
from paho.mqtt.client import Client # type: ignore
|
import paho.mqtt.client as mqtt
|
||||||
|
import threading
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# === load and shuffle .m3u playlist ===
|
# === load and shuffle .m3u playlist ===
|
||||||
@@ -38,8 +39,7 @@ def update_now_playing(playlist_name, song_title):
|
|||||||
|
|
||||||
# MQTT title broadcast
|
# MQTT title broadcast
|
||||||
mqtt_topic = f"music/now_playing/{playlist_name.lower()}"
|
mqtt_topic = f"music/now_playing/{playlist_name.lower()}"
|
||||||
mqtt_payload = os.path.basename(song_title)
|
mqtt_payload = os.path.splitext(os.path.basename(song_title))[0]
|
||||||
mqtt_payload = os.path.splitext(mqtt_payload)[0]
|
|
||||||
mqtt_payload = re.sub(r'\s*\[[^\]]+\]$', '', mqtt_payload)
|
mqtt_payload = re.sub(r'\s*\[[^\]]+\]$', '', mqtt_payload)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -53,15 +53,13 @@ def update_now_playing(playlist_name, song_title):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[MQTT] failed to publish: {e}")
|
print(f"[MQTT] failed to publish: {e}")
|
||||||
|
|
||||||
# === Get playlist name from CLI ===
|
# === YAML config ===
|
||||||
if len(sys.argv) < 2:
|
def load_config():
|
||||||
print ("Usage: python3 jack_mp3_player.py Praise|Worship|Instrumental")
|
with open("config.yaml") as f:
|
||||||
sys.exit(1)
|
return yaml.safe_load(f)
|
||||||
|
|
||||||
playlist_name = sys.argv[1]
|
# === JACK + player logic ===
|
||||||
playlist_file = f"/home/media/Music/{playlist_name}.m3u"
|
def run_playlist(playlist_name, playlist_file):
|
||||||
|
|
||||||
# === jack setup ===
|
|
||||||
client = jack.Client(playlist_name)
|
client = jack.Client(playlist_name)
|
||||||
out_l = client.outports.register("left")
|
out_l = client.outports.register("left")
|
||||||
out_r = client.outports.register("right")
|
out_r = client.outports.register("right")
|
||||||
@@ -75,27 +73,20 @@ buffer_pos = [0]
|
|||||||
def process(frames):
|
def process(frames):
|
||||||
bufsize = frames
|
bufsize = frames
|
||||||
end = buffer_pos[0] + bufsize
|
end = buffer_pos[0] + bufsize
|
||||||
|
|
||||||
#zero fills if out of data
|
|
||||||
if buffer_pos[0] >= len(samples):
|
if buffer_pos[0] >= len(samples):
|
||||||
out_l.get_buffer()[:] = np.zeros(bufsize, dtype=np.float32)
|
out_l.get_buffer()[:] = np.zeros(bufsize, dtype=np.float32)
|
||||||
out_r.get_buffer()[:] = np.zeros(bufsize, dtype=np.float32)
|
out_r.get_buffer()[:] = np.zeros(bufsize, dtype=np.float32)
|
||||||
return
|
return
|
||||||
|
|
||||||
chunk = samples[buffer_pos[0]:end]
|
chunk = samples[buffer_pos[0]:end]
|
||||||
#pad if last chunk
|
|
||||||
if len(chunk) < bufsize:
|
if len(chunk) < bufsize:
|
||||||
pad = np.zeros((bufsize - len(chunk), 2), dtype=np.float32)
|
pad = np.zeros((bufsize - len(chunk), 2), dtype=np.float32)
|
||||||
chunk = np.vstack((chunk, pad))
|
chunk = np.vstack((chunk, pad))
|
||||||
|
|
||||||
out_l.get_buffer()[:] = chunk[:, 0].copy()
|
out_l.get_buffer()[:] = chunk[:, 0].copy()
|
||||||
out_r.get_buffer()[:] = chunk[:, 1].copy()
|
out_r.get_buffer()[:] = chunk[:, 1].copy()
|
||||||
buffer_pos[0] += bufsize
|
buffer_pos[0] += bufsize
|
||||||
|
|
||||||
# === Activate jack ===
|
|
||||||
with client:
|
with client:
|
||||||
print(f"Playing playlist: {playlist_name}")
|
print(f"[{playlist_name}] Playing: {playlist[current_index]}")
|
||||||
print(f"Playing song: {playlist[current_index]}")
|
|
||||||
update_now_playing(playlist_name, playlist[current_index])
|
update_now_playing(playlist_name, playlist[current_index])
|
||||||
while True:
|
while True:
|
||||||
if buffer_pos[0] >= len(samples):
|
if buffer_pos[0] >= len(samples):
|
||||||
@@ -103,10 +94,32 @@ with client:
|
|||||||
if current_index >= len(playlist):
|
if current_index >= len(playlist):
|
||||||
playlist = load_playlist(playlist_file)
|
playlist = load_playlist(playlist_file)
|
||||||
current_index = 0
|
current_index = 0
|
||||||
|
print(f"[{playlist_name}] Playing: {playlist[current_index]}")
|
||||||
print(f"Playing: {playlist[current_index]}")
|
|
||||||
update_now_playing(playlist_name, playlist[current_index])
|
update_now_playing(playlist_name, playlist[current_index])
|
||||||
samples = decode_mp3(playlist[current_index])
|
samples = decode_mp3(playlist[current_index])
|
||||||
buffer_pos[0] = 0
|
buffer_pos[0] = 0
|
||||||
|
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
# === Main logic ===
|
||||||
|
if __name__ == "__main__":
|
||||||
|
config = load_config()
|
||||||
|
sources = config.get("sources", {})
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
playlist_name = sys.argv[1]
|
||||||
|
if playlist_name in sources and sources[playlist_name].get("active", True):
|
||||||
|
playlist_file = f"/home/media/Music/{playlist_name}.m3u"
|
||||||
|
run_playlist(playlist_name, playlist_file)
|
||||||
|
else:
|
||||||
|
print(f"[!] Source '{playlist_name}' not found or not active.")
|
||||||
|
else:
|
||||||
|
threads = []
|
||||||
|
for playlist_name, source_data in sources.items():
|
||||||
|
if source_data.get("active", True):
|
||||||
|
playlist_file = f"/home/media/Music/{playlist_name}.m3u"
|
||||||
|
t = threading.Thread(target=run_playlist, args=(playlist_name, playlist_file), daemon=True)
|
||||||
|
t.start()
|
||||||
|
threads.append(t)
|
||||||
|
|
||||||
|
for t in threads:
|
||||||
|
t.join()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ with open("config.yaml", "r") as f:
|
|||||||
|
|
||||||
zones = config.get("zones", {})
|
zones = config.get("zones", {})
|
||||||
sources = config.get("sources", {})
|
sources = config.get("sources", {})
|
||||||
DEFAULT_SOURCE = "worship"
|
DEFAULT_SOURCE = "Worship"
|
||||||
|
|
||||||
print("Zones loaded:", zones.keys())
|
print("Zones loaded:", zones.keys())
|
||||||
print("Sources loaded:", sources.keys())
|
print("Sources loaded:", sources.keys())
|
||||||
@@ -73,7 +73,38 @@ def on_connect(client, userdata, flags, rc):
|
|||||||
client.subscribe(topic)
|
client.subscribe(topic)
|
||||||
print(f"subscribed to {topic}")
|
print(f"subscribed to {topic}")
|
||||||
|
|
||||||
time.sleep(1)
|
def get_required_jack_ports():
|
||||||
|
required = []
|
||||||
|
|
||||||
|
# From sources
|
||||||
|
for source_name, source_data in sources.items():
|
||||||
|
if source_data.get("active", True):
|
||||||
|
required.append(source_data["left"])
|
||||||
|
required.append(source_data["right"])
|
||||||
|
|
||||||
|
# From zones
|
||||||
|
for zone_name, zone_data in zones.items():
|
||||||
|
if zone_data.get("active", True):
|
||||||
|
required.append(zone_data["left"])
|
||||||
|
required.append(zone_data["right"])
|
||||||
|
|
||||||
|
return required
|
||||||
|
|
||||||
|
def wait_for_required_jack_ports(required_ports, interval=5):
|
||||||
|
print("[router] Waiting for all required JACK ports to appear...")
|
||||||
|
while True:
|
||||||
|
available = set(p.name for p in client_jack.get_ports())
|
||||||
|
missing = [p for p in required_ports if p not in available]
|
||||||
|
if not missing:
|
||||||
|
print("[router] All required JACK ports found. Proceeding.")
|
||||||
|
return
|
||||||
|
print(f"[router] Still waiting for: {missing}")
|
||||||
|
time.sleep(interval)
|
||||||
|
|
||||||
|
|
||||||
|
required_ports = get_required_jack_ports()
|
||||||
|
wait_for_required_jack_ports(required_ports)
|
||||||
|
|
||||||
active_zones = wait_for_available_zone_ports()
|
active_zones = wait_for_available_zone_ports()
|
||||||
apply_default_connections(active_zones)
|
apply_default_connections(active_zones)
|
||||||
|
|
||||||
|
|||||||
@@ -16,3 +16,8 @@ Saved updated ID cache: /home/media/Scripts/ZonePlayer/cache/instrumental_ids.js
|
|||||||
✅ Updated playlist: /home/media/Music/Praise.m3u with 16 songs
|
✅ Updated playlist: /home/media/Music/Praise.m3u with 16 songs
|
||||||
✅ Updated playlist: /home/media/Music/Instrumental.m3u with 2 songs
|
✅ Updated playlist: /home/media/Music/Instrumental.m3u with 2 songs
|
||||||
✅ Playlist sync complete.
|
✅ Playlist sync complete.
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/media/Scripts/ZonePlayer/yt_sync.py", line 12, in <module>
|
||||||
|
with open(CONFIG_FILE, "r") as f:
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
FileNotFoundError: [Errno 2] No such file or directory: 'config.yaml'
|
||||||
|
|||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import jack
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
"Praise:left", "Praise:right",
|
||||||
|
"Worship:left", "Worship:right",
|
||||||
|
"Instrumental:left", "Instrumental:right",
|
||||||
|
"sanctuary:in_l", "sanctuary:in_r",
|
||||||
|
"prayer:in_l", "prayer:in_r",
|
||||||
|
"Mac:in_l", "Mac:in_r"
|
||||||
|
}
|
||||||
|
|
||||||
|
client = jack.Client("waiter")
|
||||||
|
timeout = 15
|
||||||
|
deadline = time.time() + timeout
|
||||||
|
|
||||||
|
while time.time() < deadline:
|
||||||
|
current = {port.name for port in client.get_ports()}
|
||||||
|
if expected.issubset(current):
|
||||||
|
print("All JACK ports found.")
|
||||||
|
subprocess.run(["systemctl", "--system", "restart", "router.service"])
|
||||||
|
sys.exit(0)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
print("Timeout waiting for JACK ports.")
|
||||||
|
sys.exit(1)
|
||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
REQUIRED_PORTS=(
|
||||||
|
"sanctuary_out:left"
|
||||||
|
"sanctuary_out:right"
|
||||||
|
"prayer_out:left"
|
||||||
|
"prayer_out:right"
|
||||||
|
"Macbook_out:left"
|
||||||
|
"Macbook_out:right"
|
||||||
|
"Praise:left"
|
||||||
|
"Praise:right"
|
||||||
|
"Worship:left"
|
||||||
|
"Worship:right"
|
||||||
|
"Instrumental:left"
|
||||||
|
"Instrumental:right"
|
||||||
|
)
|
||||||
|
|
||||||
|
DEADLINE=$((SECONDS + 10))
|
||||||
|
|
||||||
|
while [ $SECONDS -lt $DEADLINE ]; do
|
||||||
|
ALL_FOUND=true
|
||||||
|
for PORT in "${REQUIRED_PORTS[@]}"; do
|
||||||
|
if ! /usr/bin/jack_lsp | grep -q "$PORT"; then
|
||||||
|
ALL_FOUND=false
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if $ALL_FOUND; then
|
||||||
|
echo "All required JACK ports found."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Timeout waiting for JACK ports"
|
||||||
|
exit 1
|
||||||
@@ -4,26 +4,16 @@ import jack # type: ignore
|
|||||||
import socket
|
import socket
|
||||||
import numpy as np # type: ignore
|
import numpy as np # type: ignore
|
||||||
import time
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
|
def load_config():
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("Usage: python3 zone.py <zone_name>")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
zone_name = sys.argv[1]
|
|
||||||
|
|
||||||
with open("config.yaml", "r") as f:
|
with open("config.yaml", "r") as f:
|
||||||
config = yaml.safe_load(f)
|
return yaml.safe_load(f)
|
||||||
|
|
||||||
zone = config["zones"].get(zone_name)
|
def run_zone(zone_name: str, zone_config: dict):
|
||||||
|
jack_input = zone_config["jack_input"]
|
||||||
if not zone:
|
ip = zone_config["ip"]
|
||||||
print(f"Zone '{zone_name} not found in config")
|
port = zone_config["port"]
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
jack_input = zone["jack_input"]
|
|
||||||
ip = zone["ip"]
|
|
||||||
port = zone["port"]
|
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
|
||||||
@@ -44,9 +34,34 @@ def process(frames):
|
|||||||
send_pcm_frames(data)
|
send_pcm_frames(data)
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
print(f"Streaming {zone_name} to {ip}:{port}")
|
print(f"🎧 Streaming {zone_name} to {ip}:{port}")
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Stopped")
|
print(f"🛑 {zone_name} stopped")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
config = load_config()
|
||||||
|
zones = config.get("zones", {})
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
zone_name = sys.argv[1]
|
||||||
|
zone = zones.get(zone_name)
|
||||||
|
if not zone:
|
||||||
|
print(f"[!] Zone '{zone_name}' not found in config")
|
||||||
|
sys.exit(1)
|
||||||
|
if not zone.get("active", True):
|
||||||
|
print(f"[!] Zone '{zone_name}' is inactive in config")
|
||||||
|
sys.exit(1)
|
||||||
|
run_zone(zone_name, zone)
|
||||||
|
else:
|
||||||
|
threads = []
|
||||||
|
for zone_name, zone_config in zones.items():
|
||||||
|
if zone_config.get("active", True):
|
||||||
|
t = threading.Thread(target=run_zone, args=(zone_name, zone_config), daemon=True)
|
||||||
|
t.start()
|
||||||
|
threads.append(t)
|
||||||
|
|
||||||
|
for t in threads:
|
||||||
|
t.join()
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
|
venv_python = "/home/media/Scripts/ZonePlayer/venv/bin/python3"
|
||||||
|
script_dir = "/home/media/Scripts/ZonePlayer"
|
||||||
|
|
||||||
|
zones = ["sanctuary", "prayer", "office", "fellowship", "Macbook"]
|
||||||
|
players = ["praise", "worship", "instrumental"]
|
||||||
|
|
||||||
|
logfile = "/tmp/zoneplayer_launcher.log"
|
||||||
|
|
||||||
|
def log(msg):
|
||||||
|
with open(logfile, "a") as f:
|
||||||
|
f.write(msg + "\n")
|
||||||
|
print(msg)
|
||||||
|
|
||||||
|
def start_detached(script, *args):
|
||||||
|
try:
|
||||||
|
subprocess.Popen(
|
||||||
|
[venv_python, os.path.join(script_dir, script), *args],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
start_new_session=True
|
||||||
|
)
|
||||||
|
log(f"Started: {script} {' '.join(args)}")
|
||||||
|
except Exception as e:
|
||||||
|
log(f"ERROR: Failed to start {script} {' '.join(args)} - {e}")
|
||||||
|
|
||||||
|
log("=== ZonePlayer Launcher Starting ===")
|
||||||
|
|
||||||
|
# Launch zones
|
||||||
|
for zone in zones:
|
||||||
|
start_detached("zone.py", zone)
|
||||||
|
|
||||||
|
# Launch players
|
||||||
|
for player in players:
|
||||||
|
start_detached("jack_mp3_player.py", player)
|
||||||
|
|
||||||
|
log("Sleeping 10 seconds to allow JACK ports to appear")
|
||||||
|
time.sleep(10)
|
||||||
|
|
||||||
|
# Start router
|
||||||
|
start_detached("router.py")
|
||||||
|
log("Router started")
|
||||||
|
|
||||||
|
log("=== ZonePlayer Launcher Done ===")
|
||||||
Reference in New Issue
Block a user