mirror of
https://github.com/damp11113/IDRB.git
synced 2025-04-28 06:58:09 +00:00
Create client.py
This commit is contained in:
parent
83b3ba460a
commit
b1cefb27c2
184
client.py
Normal file
184
client.py
Normal file
@ -0,0 +1,184 @@
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
import dearpygui.dearpygui as dpg
|
||||
import threading
|
||||
import socket
|
||||
import numpy as np
|
||||
import pickle
|
||||
import pyaudio
|
||||
import websockets
|
||||
from pyogg import OpusDecoder
|
||||
|
||||
class App:
|
||||
def __init__(self):
|
||||
self.RDS = None
|
||||
self.device_name_output = "Speakers (4- USB Audio DAC )"
|
||||
self.working = False
|
||||
|
||||
def connecttoserver(self, sender, data):
|
||||
dpg.configure_item("connectbutton", show=False)
|
||||
protocol = dpg.get_value("serverprotocol")
|
||||
dpg.configure_item("serverstatus", default_value='connecting...', color=(255, 255, 0))
|
||||
if protocol == "Websocket":
|
||||
asyncio.create_task(self.WSstream())
|
||||
return
|
||||
|
||||
ip = dpg.get_value("serverip")
|
||||
port = dpg.get_value("serverport")
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.connect((ip, port))
|
||||
except:
|
||||
dpg.configure_item("connectbutton", show=True)
|
||||
self.working = True
|
||||
p = pyaudio.PyAudio()
|
||||
|
||||
device_index_output = 0
|
||||
for i in range(p.get_device_count()):
|
||||
dev = p.get_device_info_by_index(i)
|
||||
if dev['name'] == self.device_name_output:
|
||||
device_index_output = dev['index']
|
||||
break
|
||||
|
||||
thread = threading.Thread(target=self.stream, args=(s, device_index_output))
|
||||
thread.start()
|
||||
|
||||
def disconnectserver(self, sender, data):
|
||||
dpg.configure_item("disconnectbutton", show=False)
|
||||
dpg.configure_item("serverstatus", default_value='disconnecting...', color=(255, 255, 0))
|
||||
self.working = False
|
||||
|
||||
def RDSshow(self):
|
||||
try:
|
||||
dpg.configure_item("RDSinfo",
|
||||
default_value=f'{self.RDS["PS"]} ({self.RDS["ContentInfo"]["Codec"]} {self.RDS["ContentInfo"]["bitrate"] / 1000}Kbps {self.RDS["AudioMode"]})',
|
||||
show=True)
|
||||
dpg.configure_item("RDSPS", default_value="PS: " + self.RDS["PS"])
|
||||
dpg.configure_item("RDSRT", default_value="RT: " + self.RDS["RT"])
|
||||
dpg.configure_item("RDSCTlocal", default_value="Time Local: " + datetime.fromtimestamp(self.RDS["CT"]["Local"]).strftime('%H:%M:%S'))
|
||||
dpg.configure_item("RDSCTUTC", default_value="Time UTC: " + datetime.fromtimestamp(self.RDS["CT"]["UTC"]).strftime('%H:%M:%S'))
|
||||
dpg.configure_item("RDSListener", default_value="Listener: " + str(self.RDS["Listener"]) + " Users")
|
||||
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def stream(self, socket, deviceindex):
|
||||
opus_decoder = OpusDecoder()
|
||||
streamoutput = None
|
||||
firstrun = True
|
||||
while True:
|
||||
try:
|
||||
if self.working:
|
||||
data = socket.recv(650000)
|
||||
|
||||
if len(data) == 0:
|
||||
dpg.configure_item("serverstatus", default_value='lost connected', color=(255, 0, 0))
|
||||
socket.close()
|
||||
dpg.configure_item("showRDS", show=False)
|
||||
dpg.configure_item("RDSinfo", show=False)
|
||||
dpg.configure_item("disconnectbutton", show=False)
|
||||
dpg.configure_item("connectbutton", show=True)
|
||||
break
|
||||
|
||||
try:
|
||||
datadecoded = pickle.loads(data)
|
||||
except:
|
||||
pass
|
||||
|
||||
if datadecoded["RDS"] != self.RDS:
|
||||
self.RDS = datadecoded["RDS"]
|
||||
rdshow = threading.Thread(target=self.RDSshow)
|
||||
rdshow.start()
|
||||
|
||||
if firstrun:
|
||||
p = pyaudio.PyAudio()
|
||||
opus_decoder.set_channels(self.RDS["ContentInfo"]["channel"])
|
||||
opus_decoder.set_sampling_frequency(self.RDS["ContentInfo"]["samplerates"])
|
||||
streamoutput = p.open(format=pyaudio.paInt16, channels=self.RDS["ContentInfo"]["channel"], rate=self.RDS["ContentInfo"]["samplerates"], output=True, output_device_index=deviceindex)
|
||||
dpg.configure_item("showRDS", show=True)
|
||||
dpg.configure_item("serverstatus", default_value='connected', color=(0, 255, 0))
|
||||
dpg.configure_item("disconnectbutton", show=True)
|
||||
firstrun = False
|
||||
|
||||
decoded_pcm = opus_decoder.decode(memoryview(bytearray(datadecoded["Content"])))
|
||||
|
||||
# Check if the decoded PCM is empty or not
|
||||
if len(decoded_pcm) > 0:
|
||||
pcm_to_write = np.frombuffer(decoded_pcm, dtype=np.int16)
|
||||
|
||||
streamoutput.write(pcm_to_write.tobytes())
|
||||
else:
|
||||
print("Decoded PCM is empty")
|
||||
else:
|
||||
streamoutput.close()
|
||||
socket.close()
|
||||
self.working = False
|
||||
dpg.configure_item("showRDS", show=False)
|
||||
dpg.configure_item("RDSinfo", show=False)
|
||||
dpg.configure_item("connectbutton", show=True)
|
||||
dpg.configure_item("serverstatus", default_value='disconnected', color=(255, 0, 0))
|
||||
break
|
||||
except Exception as e:
|
||||
if str(e) == "An error occurred while decoding an Opus-encoded packet: corrupted stream":
|
||||
dpg.configure_item("serverstatus", default_value="Unable to decode audio data", color=(255, 0, 0))
|
||||
else:
|
||||
print("connection lost", e)
|
||||
self.working = False
|
||||
streamoutput.close()
|
||||
socket.close()
|
||||
dpg.configure_item("showRDS", show=False)
|
||||
dpg.configure_item("RDSinfo", show=False)
|
||||
dpg.configure_item("connectbutton", show=True)
|
||||
dpg.configure_item("serverstatus", default_value='disconnected', color=(255, 0, 0))
|
||||
break
|
||||
|
||||
def window(self):
|
||||
with dpg.window(label="IDRB", width=320):
|
||||
dpg.add_text("", tag="RDSinfo", show=False)
|
||||
dpg.add_input_text(label="server ip", tag="serverip", default_value="localhost")
|
||||
dpg.add_input_int(label="port", tag="serverport", max_value=65535, default_value=6980)
|
||||
dpg.add_combo(["TCP", "Websocket"], label="protocol", tag="serverprotocol", default_value="TCP")
|
||||
dpg.add_button(label="connect", callback=self.connecttoserver, tag="connectbutton")
|
||||
dpg.add_button(label="disconnect", callback=self.disconnectserver, tag="disconnectbutton", show=False)
|
||||
dpg.add_spacer()
|
||||
dpg.add_text("not connect", tag="serverstatus", color=(255, 0, 0))
|
||||
dpg.add_spacer()
|
||||
with dpg.collapsing_header(label="RDS", show=False, tag="showRDS"):
|
||||
dpg.add_text("PS: ...", tag="RDSPS")
|
||||
dpg.add_text("RT: ...", tag="RDSRT")
|
||||
dpg.add_text("Listener: ...", tag="RDSListener")
|
||||
dpg.add_text("Time Local: ...", tag="RDSCTlocal")
|
||||
dpg.add_text("Time UTC: ...", tag="RDSCTUTC")
|
||||
|
||||
|
||||
|
||||
def init(self):
|
||||
dpg.create_context()
|
||||
dpg.create_viewport(title='[ThaiSDR] IDRB (Internet Digital Radio Broadcasting) V1 Beta', width=1280, height=720, small_icon="favicon.ico") # set viewport window
|
||||
dpg.setup_dearpygui()
|
||||
# -------------- add code here --------------
|
||||
self.window()
|
||||
# -------------------------------------------
|
||||
dpg.show_viewport()
|
||||
# Start a separate thread for a task
|
||||
self.thread_stop_event = threading.Event()
|
||||
|
||||
while dpg.is_dearpygui_running():
|
||||
self.render()
|
||||
dpg.render_dearpygui_frame()
|
||||
|
||||
# Signal the thread to stop and wait for it to finish
|
||||
self.thread_stop_event.set()
|
||||
dpg.destroy_context()
|
||||
|
||||
def render(self):
|
||||
# insert here any code you would like to run in the render loop
|
||||
# you can manually stop by using stop_dearpygui() or self.exit()
|
||||
pass
|
||||
|
||||
def exit(self):
|
||||
dpg.destroy_context()
|
||||
|
||||
|
||||
app = App()
|
||||
app.init()
|
Loading…
x
Reference in New Issue
Block a user