Completed player
This commit is contained in:
+73
-60
@@ -1,13 +1,14 @@
|
||||
import jack # type: ignore
|
||||
import jack
|
||||
import time
|
||||
import numpy as np # type: ignore
|
||||
from pydub import AudioSegment # type: ignore
|
||||
import numpy as np
|
||||
from pydub import AudioSegment
|
||||
import random
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import paho.mqtt.client as mqtt # type: ignore
|
||||
from paho.mqtt.client import Client # type: ignore
|
||||
import yaml
|
||||
import paho.mqtt.client as mqtt
|
||||
import threading
|
||||
import re
|
||||
|
||||
# === load and shuffle .m3u playlist ===
|
||||
@@ -22,7 +23,7 @@ def decode_mp3(path):
|
||||
song = AudioSegment.from_file(path)
|
||||
song = song.set_frame_rate(48000).set_channels(2).set_sample_width(4)
|
||||
samples = np.array(song.get_array_of_samples()).astype(np.float32) / (2**31)
|
||||
return samples.reshape((-1,2)) #stereo:L R
|
||||
return samples.reshape((-1, 2)) # stereo: L R
|
||||
|
||||
def update_now_playing(playlist_name, song_title):
|
||||
path = "/home/media/Music/now_playing.json"
|
||||
@@ -38,8 +39,7 @@ def update_now_playing(playlist_name, song_title):
|
||||
|
||||
# MQTT title broadcast
|
||||
mqtt_topic = f"music/now_playing/{playlist_name.lower()}"
|
||||
mqtt_payload = os.path.basename(song_title)
|
||||
mqtt_payload = os.path.splitext(mqtt_payload)[0]
|
||||
mqtt_payload = os.path.splitext(os.path.basename(song_title))[0]
|
||||
mqtt_payload = re.sub(r'\s*\[[^\]]+\]$', '', mqtt_payload)
|
||||
|
||||
try:
|
||||
@@ -53,60 +53,73 @@ def update_now_playing(playlist_name, song_title):
|
||||
except Exception as e:
|
||||
print(f"[MQTT] failed to publish: {e}")
|
||||
|
||||
# === Get playlist name from CLI ===
|
||||
if len(sys.argv) < 2:
|
||||
print ("Usage: python3 jack_mp3_player.py Praise|Worship|Instrumental")
|
||||
sys.exit(1)
|
||||
# === YAML config ===
|
||||
def load_config():
|
||||
with open("config.yaml") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
playlist_name = sys.argv[1]
|
||||
playlist_file = f"/home/media/Music/{playlist_name}.m3u"
|
||||
# === JACK + player logic ===
|
||||
def run_playlist(playlist_name, playlist_file):
|
||||
client = jack.Client(playlist_name)
|
||||
out_l = client.outports.register("left")
|
||||
out_r = client.outports.register("right")
|
||||
|
||||
# === jack setup ===
|
||||
client = jack.Client(playlist_name)
|
||||
out_l = client.outports.register("left")
|
||||
out_r = client.outports.register("right")
|
||||
playlist = load_playlist(playlist_file)
|
||||
current_index = 0
|
||||
samples = decode_mp3(playlist[current_index])
|
||||
buffer_pos = [0]
|
||||
|
||||
playlist = load_playlist(playlist_file)
|
||||
current_index = 0
|
||||
samples = decode_mp3(playlist[current_index])
|
||||
buffer_pos = [0]
|
||||
|
||||
@client.set_process_callback
|
||||
def process(frames):
|
||||
bufsize = frames
|
||||
end = buffer_pos[0] + bufsize
|
||||
|
||||
#zero fills if out of data
|
||||
if buffer_pos[0] >= len(samples):
|
||||
out_l.get_buffer()[:] = np.zeros(bufsize, dtype=np.float32)
|
||||
out_r.get_buffer()[:] = np.zeros(bufsize, dtype=np.float32)
|
||||
return
|
||||
|
||||
chunk = samples[buffer_pos[0]:end]
|
||||
#pad if last chunk
|
||||
if len(chunk) < bufsize:
|
||||
pad = np.zeros((bufsize - len(chunk), 2), dtype=np.float32)
|
||||
chunk = np.vstack((chunk, pad))
|
||||
|
||||
out_l.get_buffer()[:] = chunk[:, 0].copy()
|
||||
out_r.get_buffer()[:] = chunk[:, 1].copy()
|
||||
buffer_pos[0] += bufsize
|
||||
|
||||
# === Activate jack ===
|
||||
with client:
|
||||
print(f"Playing playlist: {playlist_name}")
|
||||
print(f"Playing song: {playlist[current_index]}")
|
||||
update_now_playing(playlist_name, playlist[current_index])
|
||||
while True:
|
||||
@client.set_process_callback
|
||||
def process(frames):
|
||||
bufsize = frames
|
||||
end = buffer_pos[0] + bufsize
|
||||
if buffer_pos[0] >= len(samples):
|
||||
current_index += 1
|
||||
if current_index >= len(playlist):
|
||||
playlist = load_playlist(playlist_file)
|
||||
current_index = 0
|
||||
out_l.get_buffer()[:] = np.zeros(bufsize, dtype=np.float32)
|
||||
out_r.get_buffer()[:] = np.zeros(bufsize, dtype=np.float32)
|
||||
return
|
||||
chunk = samples[buffer_pos[0]:end]
|
||||
if len(chunk) < bufsize:
|
||||
pad = np.zeros((bufsize - len(chunk), 2), dtype=np.float32)
|
||||
chunk = np.vstack((chunk, pad))
|
||||
out_l.get_buffer()[:] = chunk[:, 0].copy()
|
||||
out_r.get_buffer()[:] = chunk[:, 1].copy()
|
||||
buffer_pos[0] += bufsize
|
||||
|
||||
print(f"Playing: {playlist[current_index]}")
|
||||
update_now_playing(playlist_name, playlist[current_index])
|
||||
samples = decode_mp3(playlist[current_index])
|
||||
buffer_pos[0] = 0
|
||||
|
||||
time.sleep(0.1)
|
||||
with client:
|
||||
print(f"[{playlist_name}] Playing: {playlist[current_index]}")
|
||||
update_now_playing(playlist_name, playlist[current_index])
|
||||
while True:
|
||||
if buffer_pos[0] >= len(samples):
|
||||
current_index += 1
|
||||
if current_index >= len(playlist):
|
||||
playlist = load_playlist(playlist_file)
|
||||
current_index = 0
|
||||
print(f"[{playlist_name}] Playing: {playlist[current_index]}")
|
||||
update_now_playing(playlist_name, playlist[current_index])
|
||||
samples = decode_mp3(playlist[current_index])
|
||||
buffer_pos[0] = 0
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user