Add linting for autopep8
This commit is contained in:
parent
2941d90f60
commit
e5e3267e75
10 changed files with 145 additions and 91 deletions
3
build/requirements-dev.txt
Normal file
3
build/requirements-dev.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
flake8
|
||||||
|
black
|
||||||
|
autopep8
|
|
@ -1,4 +1,4 @@
|
||||||
autopep8
|
wheel
|
||||||
pygame==2.0.1
|
pygame==2.0.1
|
||||||
sanic==21.3.4
|
sanic==21.3.4
|
||||||
sanic-Cors==1.0.0
|
sanic-Cors==1.0.0
|
||||||
|
|
|
@ -41,7 +41,7 @@ class FileManager:
|
||||||
while not terminator.terminate:
|
while not terminator.terminate:
|
||||||
# If all channels have received the delete command, reset for the next one.
|
# If all channels have received the delete command, reset for the next one.
|
||||||
if (
|
if (
|
||||||
self.channel_received == None
|
self.channel_received is None
|
||||||
or self.channel_received == [True] * self.channel_count
|
or self.channel_received == [True] * self.channel_count
|
||||||
):
|
):
|
||||||
self.channel_received = [False] * self.channel_count
|
self.channel_received = [False] * self.channel_count
|
||||||
|
@ -60,18 +60,22 @@ class FileManager:
|
||||||
if command == "GETPLAN":
|
if command == "GETPLAN":
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self.channel_received != [False] * self.channel_count
|
self.channel_received != [
|
||||||
|
False] * self.channel_count
|
||||||
and self.channel_received[channel] != True
|
and self.channel_received[channel] != True
|
||||||
):
|
):
|
||||||
# We've already received a delete trigger on a channel, let's not delete the folder more than once.
|
# We've already received a delete trigger on a channel, let's not delete the folder more than once.
|
||||||
# If the channel was already in the process of being deleted, the user has requested it again, so allow it.
|
# If the channel was already in the process of being deleted, the user has
|
||||||
|
# requested it again, so allow it.
|
||||||
|
|
||||||
self.channel_received[channel] = True
|
self.channel_received[channel] = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Delete the previous show files!
|
# Delete the previous show files!
|
||||||
# Note: The players load into RAM. If something is playing over the load, the source file can still be deleted.
|
# Note: The players load into RAM. If something is playing over the load,
|
||||||
path: str = resolve_external_file_path("/music-tmp/")
|
# the source file can still be deleted.
|
||||||
|
path: str = resolve_external_file_path(
|
||||||
|
"/music-tmp/")
|
||||||
|
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
self.logger.log.warning(
|
self.logger.log.warning(
|
||||||
|
@ -102,7 +106,8 @@ class FileManager:
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
self.channel_received[channel] = True
|
self.channel_received[channel] = True
|
||||||
self.known_channels_preloaded = [False] * self.channel_count
|
self.known_channels_preloaded = [
|
||||||
|
False] * self.channel_count
|
||||||
self.known_channels_normalised = [
|
self.known_channels_normalised = [
|
||||||
False
|
False
|
||||||
] * self.channel_count
|
] * self.channel_count
|
||||||
|
@ -142,7 +147,8 @@ class FileManager:
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.log.exception("Received unexpected exception: {}".format(e))
|
self.logger.log.exception(
|
||||||
|
"Received unexpected exception: {}".format(e))
|
||||||
del self.logger
|
del self.logger
|
||||||
|
|
||||||
# Attempt to preload a file onto disk.
|
# Attempt to preload a file onto disk.
|
||||||
|
@ -169,7 +175,8 @@ class FileManager:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Getting the file name will only pull the new file if the file doesn't already exist, so this is not too inefficient.
|
# Getting the file name will only pull the new file if the file doesn't
|
||||||
|
# already exist, so this is not too inefficient.
|
||||||
item_obj.filename, did_download = sync(
|
item_obj.filename, did_download = sync(
|
||||||
self.api.get_filename(item_obj, True)
|
self.api.get_filename(item_obj, True)
|
||||||
)
|
)
|
||||||
|
@ -181,7 +188,8 @@ class FileManager:
|
||||||
if did_download:
|
if did_download:
|
||||||
downloaded_something = True
|
downloaded_something = True
|
||||||
self.logger.log.info(
|
self.logger.log.info(
|
||||||
"File successfully preloaded: {}".format(item_obj.filename)
|
"File successfully preloaded: {}".format(
|
||||||
|
item_obj.filename)
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -189,7 +197,8 @@ class FileManager:
|
||||||
# Let's try the next one.
|
# Let's try the next one.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Tell the file manager that this channel is fully downloaded, this is so it can consider normalising once all channels have files.
|
# Tell the file manager that this channel is fully downloaded, this is so
|
||||||
|
# it can consider normalising once all channels have files.
|
||||||
self.known_channels_preloaded[channel] = not downloaded_something
|
self.known_channels_preloaded[channel] = not downloaded_something
|
||||||
|
|
||||||
self.next_channel_preload += 1
|
self.next_channel_preload += 1
|
||||||
|
@ -241,7 +250,8 @@ class FileManager:
|
||||||
normalised_something = True
|
normalised_something = True
|
||||||
break # Now go let another channel have a go.
|
break # Now go let another channel have a go.
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.log.exception("Failed to generate normalised file.", str(e))
|
self.logger.log.exception(
|
||||||
|
"Failed to generate normalised file.", str(e))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.known_channels_normalised[channel] = not normalised_something
|
self.known_channels_normalised[channel] = not normalised_something
|
||||||
|
|
|
@ -39,7 +39,8 @@ def startServer(notifications=False):
|
||||||
|
|
||||||
# Catch the handler being killed externally.
|
# Catch the handler being killed externally.
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
printer("Received Exception {} with args: {}".format(type(e).__name__, e.args))
|
printer("Received Exception {} with args: {}".format(
|
||||||
|
type(e).__name__, e.args))
|
||||||
if server and server.is_alive():
|
if server and server.is_alive():
|
||||||
server.terminate()
|
server.terminate()
|
||||||
server.join()
|
server.join()
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"presenter-install": "cd presenter && git submodule update --init && yarn --network-timeout 100000",
|
"presenter-install": "cd presenter && git submodule update --init && yarn --network-timeout 100000",
|
||||||
"presenter-make": "npm run presenter-install && (rm -r presenter-build || true) && cd presenter && yarn build-baps && cp -r build ../presenter-build && cd ../ && npm install",
|
"presenter-make": "npm run presenter-install && (rm -r presenter-build || true) && cd presenter && yarn build-baps && cp -r build ../presenter-build && cd ../ && npm install",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"presenter-start": "cd presenter && yarn start"
|
"presenter-start": "cd presenter && yarn start",
|
||||||
|
"lint": "autopep8 -r ./*.py -a -a --ignore E402,E226,E24,W50,W690 --max-line-length 127 --in-place"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
125
player.py
125
player.py
|
@ -20,29 +20,28 @@
|
||||||
# that we respond with something, FAIL or OKAY. The server doesn't like to be kept waiting.
|
# that we respond with something, FAIL or OKAY. The server doesn't like to be kept waiting.
|
||||||
|
|
||||||
# Stop the Pygame Hello message.
|
# Stop the Pygame Hello message.
|
||||||
|
import package
|
||||||
|
from baps_types.marker import Marker
|
||||||
|
from baps_types.plan import PlanItem
|
||||||
|
from helpers.logging_manager import LoggingManager
|
||||||
|
from helpers.state_manager import StateManager
|
||||||
|
from helpers.myradio_api import MyRadioAPI
|
||||||
|
from helpers.normalisation import get_normalised_filename_if_available
|
||||||
|
from threading import Timer
|
||||||
|
from syncer import sync
|
||||||
|
from mutagen.mp3 import MP3
|
||||||
|
from pygame import mixer
|
||||||
|
from typing import Any, Callable, Dict, List, Optional
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import copy
|
||||||
|
import setproctitle
|
||||||
|
import multiprocessing
|
||||||
|
from queue import Empty
|
||||||
import os
|
import os
|
||||||
|
|
||||||
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
|
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
|
||||||
|
|
||||||
from queue import Empty
|
|
||||||
import multiprocessing
|
|
||||||
import setproctitle
|
|
||||||
import copy
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
from typing import Any, Callable, Dict, List, Optional
|
|
||||||
from pygame import mixer
|
|
||||||
from mutagen.mp3 import MP3
|
|
||||||
from syncer import sync
|
|
||||||
from threading import Timer
|
|
||||||
|
|
||||||
from helpers.normalisation import get_normalised_filename_if_available
|
|
||||||
from helpers.myradio_api import MyRadioAPI
|
|
||||||
from helpers.state_manager import StateManager
|
|
||||||
from helpers.logging_manager import LoggingManager
|
|
||||||
from baps_types.plan import PlanItem
|
|
||||||
from baps_types.marker import Marker
|
|
||||||
import package
|
|
||||||
|
|
||||||
# TODO ENUM
|
# TODO ENUM
|
||||||
VALID_MESSAGE_SOURCES = ["WEBSOCKET", "UI", "CONTROLLER", "TEST", "ALL"]
|
VALID_MESSAGE_SOURCES = ["WEBSOCKET", "UI", "CONTROLLER", "TEST", "ALL"]
|
||||||
|
@ -329,25 +328,28 @@ class Player:
|
||||||
# Right. So this may be confusing.
|
# Right. So this may be confusing.
|
||||||
# So... If the user has just moved the loaded item in the channel (by removing above and readding)
|
# So... If the user has just moved the loaded item in the channel (by removing above and readding)
|
||||||
# Then we want to re-associate the loaded_item object reference with the new one.
|
# Then we want to re-associate the loaded_item object reference with the new one.
|
||||||
# The loaded item object before this change is now an ophan, which was kept around while the loaded item was potentially moved to another channel.
|
# The loaded item object before this change is now an ophan, which was
|
||||||
|
# kept around while the loaded item was potentially moved to another
|
||||||
|
# channel.
|
||||||
if loaded_item.timeslotitemid == new_item_obj.timeslotitemid:
|
if loaded_item.timeslotitemid == new_item_obj.timeslotitemid:
|
||||||
self.state.update("loaded_item", new_item_obj)
|
self.state.update("loaded_item", new_item_obj)
|
||||||
|
|
||||||
# NOPE NOPE NOPE
|
# NOPE NOPE NOPE
|
||||||
# THIS IS AN EXAMPLE OF WHAT NOT TO DO!
|
# THIS IS AN EXAMPLE OF WHAT NOT TO DO!
|
||||||
# ONCE AGAIN, THE LOADED ITEM IS THE SAME OBJECT INSTANCE AS THE ONE IN THE SHOW PLAN (AS LONG AS IT HASN'T BEEN RE/MOVED)
|
# ONCE AGAIN, THE LOADED ITEM IS THE SAME OBJECT INSTANCE AS THE ONE IN
|
||||||
|
# THE SHOW PLAN (AS LONG AS IT HASN'T BEEN RE/MOVED)
|
||||||
|
|
||||||
## loaded_item.weight = new_item_obj.weight
|
# loaded_item.weight = new_item_obj.weight
|
||||||
|
|
||||||
# Bump the loaded_item's weight if we just added a new item above it.
|
# Bump the loaded_item's weight if we just added a new item above it.
|
||||||
##elif loaded_item.weight >= new_item_obj.weight:
|
# elif loaded_item.weight >= new_item_obj.weight:
|
||||||
## loaded_item.weight += 1
|
# loaded_item.weight += 1
|
||||||
|
|
||||||
# Else, new weight stays the same.
|
# Else, new weight stays the same.
|
||||||
##else:
|
# else:
|
||||||
## return True
|
# return True
|
||||||
|
|
||||||
##self.state.update("loaded_item", loaded_item)
|
# self.state.update("loaded_item", loaded_item)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -422,12 +424,14 @@ class Player:
|
||||||
break
|
break
|
||||||
|
|
||||||
if loaded_item is None:
|
if loaded_item is None:
|
||||||
self.logger.log.error("Failed to find weight: {}".format(weight))
|
self.logger.log.error(
|
||||||
|
"Failed to find weight: {}".format(weight))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
reload = False
|
reload = False
|
||||||
if loaded_item.filename == "" or loaded_item.filename is None:
|
if loaded_item.filename == "" or loaded_item.filename is None:
|
||||||
self.logger.log.info("Filename is not specified, loading from API.")
|
self.logger.log.info(
|
||||||
|
"Filename is not specified, loading from API.")
|
||||||
reload = True
|
reload = True
|
||||||
elif not os.path.exists(loaded_item.filename):
|
elif not os.path.exists(loaded_item.filename):
|
||||||
self.logger.log.warn(
|
self.logger.log.warn(
|
||||||
|
@ -436,7 +440,8 @@ class Player:
|
||||||
reload = True
|
reload = True
|
||||||
|
|
||||||
if reload:
|
if reload:
|
||||||
loaded_item.filename = sync(self.api.get_filename(item=loaded_item))
|
loaded_item.filename = sync(
|
||||||
|
self.api.get_filename(item=loaded_item))
|
||||||
|
|
||||||
if not loaded_item.filename:
|
if not loaded_item.filename:
|
||||||
return False
|
return False
|
||||||
|
@ -462,7 +467,8 @@ class Player:
|
||||||
while load_attempt < 5:
|
while load_attempt < 5:
|
||||||
load_attempt += 1
|
load_attempt += 1
|
||||||
try:
|
try:
|
||||||
self.logger.log.info("Loading file: " + str(loaded_item.filename))
|
self.logger.log.info(
|
||||||
|
"Loading file: " + str(loaded_item.filename))
|
||||||
mixer.music.load(loaded_item.filename)
|
mixer.music.load(loaded_item.filename)
|
||||||
except Exception:
|
except Exception:
|
||||||
# We couldn't load that file.
|
# We couldn't load that file.
|
||||||
|
@ -487,10 +493,12 @@ class Player:
|
||||||
# WARNING! Pygame / SDL can't seek .wav files :/
|
# WARNING! Pygame / SDL can't seek .wav files :/
|
||||||
self.state.update(
|
self.state.update(
|
||||||
"length",
|
"length",
|
||||||
mixer.Sound(loaded_item.filename).get_length() / 1000,
|
mixer.Sound(
|
||||||
|
loaded_item.filename).get_length() / 1000,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.log.exception("Failed to update the length of item.")
|
self.logger.log.exception(
|
||||||
|
"Failed to update the length of item.")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue # Try loading again.
|
continue # Try loading again.
|
||||||
|
|
||||||
|
@ -505,7 +513,8 @@ class Player:
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
self.logger.log.error("Failed to load track after numerous retries.")
|
self.logger.log.error(
|
||||||
|
"Failed to load track after numerous retries.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -610,7 +619,8 @@ class Player:
|
||||||
if set_loaded:
|
if set_loaded:
|
||||||
try:
|
try:
|
||||||
self.state.update(
|
self.state.update(
|
||||||
"loaded_item", self.state.get()["loaded_item"].set_marker(marker)
|
"loaded_item", self.state.get(
|
||||||
|
)["loaded_item"].set_marker(marker)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.log.error(
|
self.logger.log.error(
|
||||||
|
@ -677,7 +687,8 @@ class Player:
|
||||||
def _potentially_end_tracklist(self):
|
def _potentially_end_tracklist(self):
|
||||||
|
|
||||||
if self.tracklist_start_timer:
|
if self.tracklist_start_timer:
|
||||||
self.logger.log.info("A tracklist start timer was running, cancelling.")
|
self.logger.log.info(
|
||||||
|
"A tracklist start timer was running, cancelling.")
|
||||||
self.tracklist_start_timer.cancel()
|
self.tracklist_start_timer.cancel()
|
||||||
self.tracklist_start_timer = None
|
self.tracklist_start_timer = None
|
||||||
|
|
||||||
|
@ -708,7 +719,8 @@ class Player:
|
||||||
return
|
return
|
||||||
self.state.update("tracklist_id", None)
|
self.state.update("tracklist_id", None)
|
||||||
# This threads it, so it won't hang track loading if it fails.
|
# This threads it, so it won't hang track loading if it fails.
|
||||||
self.tracklist_end_timer = Timer(1, self._tracklist_end, [tracklist_id])
|
self.tracklist_end_timer = Timer(
|
||||||
|
1, self._tracklist_end, [tracklist_id])
|
||||||
self.tracklist_end_timer.start()
|
self.tracklist_end_timer.start()
|
||||||
else:
|
else:
|
||||||
self.logger.log.warning(
|
self.logger.log.warning(
|
||||||
|
@ -731,7 +743,8 @@ class Player:
|
||||||
tracklist_id = state["tracklist_id"]
|
tracklist_id = state["tracklist_id"]
|
||||||
if not tracklist_id:
|
if not tracklist_id:
|
||||||
if state["tracklist_mode"] == "fader-live" and not state["live"]:
|
if state["tracklist_mode"] == "fader-live" and not state["live"]:
|
||||||
self.logger.log.info("Not tracklisting since fader is not live.")
|
self.logger.log.info(
|
||||||
|
"Not tracklisting since fader is not live.")
|
||||||
else:
|
else:
|
||||||
self.logger.log.info(
|
self.logger.log.info(
|
||||||
"Tracklisting item: '{}'".format(loaded_item.name)
|
"Tracklisting item: '{}'".format(loaded_item.name)
|
||||||
|
@ -742,7 +755,8 @@ class Player:
|
||||||
"Failed to tracklist '{}'".format(loaded_item.name)
|
"Failed to tracklist '{}'".format(loaded_item.name)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.logger.log.info("Tracklist id: '{}'".format(tracklist_id))
|
self.logger.log.info(
|
||||||
|
"Tracklist id: '{}'".format(tracklist_id))
|
||||||
self.state.update("tracklist_id", tracklist_id)
|
self.state.update("tracklist_id", tracklist_id)
|
||||||
else:
|
else:
|
||||||
self.logger.log.info(
|
self.logger.log.info(
|
||||||
|
@ -857,7 +871,8 @@ class Player:
|
||||||
|
|
||||||
self.state.update(
|
self.state.update(
|
||||||
"remaining",
|
"remaining",
|
||||||
max(0, (self.state.get()["length"] - self.state.get()["pos_true"])),
|
max(0, (self.state.get()["length"] -
|
||||||
|
self.state.get()["pos_true"])),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _ping_times(self):
|
def _ping_times(self):
|
||||||
|
@ -904,7 +919,8 @@ class Player:
|
||||||
|
|
||||||
def _send_status(self):
|
def _send_status(self):
|
||||||
# TODO This is hacky
|
# TODO This is hacky
|
||||||
self._retMsg(str(self.status), okay_str=True, custom_prefix="ALL:STATUS:")
|
self._retMsg(str(self.status), okay_str=True,
|
||||||
|
custom_prefix="ALL:STATUS:")
|
||||||
|
|
||||||
def _fix_and_update_weights(self, plan):
|
def _fix_and_update_weights(self, plan):
|
||||||
def _sort_weight(e: PlanItem):
|
def _sort_weight(e: PlanItem):
|
||||||
|
@ -949,7 +965,8 @@ class Player:
|
||||||
self.running = True
|
self.running = True
|
||||||
self.out_q = out_q
|
self.out_q = out_q
|
||||||
|
|
||||||
self.logger = LoggingManager("Player" + str(channel), debug=package.build_beta)
|
self.logger = LoggingManager(
|
||||||
|
"Player" + str(channel), debug=package.build_beta)
|
||||||
|
|
||||||
self.api = MyRadioAPI(self.logger, server_state)
|
self.api = MyRadioAPI(self.logger, server_state)
|
||||||
|
|
||||||
|
@ -963,7 +980,8 @@ class Player:
|
||||||
self.state.add_callback(self._send_status)
|
self.state.add_callback(self._send_status)
|
||||||
|
|
||||||
self.state.update("channel", channel)
|
self.state.update("channel", channel)
|
||||||
self.state.update("tracklist_mode", server_state.get()["tracklist_mode"])
|
self.state.update("tracklist_mode", server_state.get()[
|
||||||
|
"tracklist_mode"])
|
||||||
self.state.update(
|
self.state.update(
|
||||||
"live", True
|
"live", True
|
||||||
) # Channel is live until controller says it isn't.
|
) # Channel is live until controller says it isn't.
|
||||||
|
@ -975,7 +993,8 @@ class Player:
|
||||||
loaded_state = copy.copy(self.state.state)
|
loaded_state = copy.copy(self.state.state)
|
||||||
|
|
||||||
if loaded_state["output"]:
|
if loaded_state["output"]:
|
||||||
self.logger.log.info("Setting output to: " + str(loaded_state["output"]))
|
self.logger.log.info("Setting output to: " +
|
||||||
|
str(loaded_state["output"]))
|
||||||
self.output(loaded_state["output"])
|
self.output(loaded_state["output"])
|
||||||
else:
|
else:
|
||||||
self.logger.log.info("Using default output device.")
|
self.logger.log.info("Using default output device.")
|
||||||
|
@ -996,7 +1015,8 @@ class Player:
|
||||||
|
|
||||||
if loaded_state["playing"] is True:
|
if loaded_state["playing"] is True:
|
||||||
self.logger.log.info("Resuming playback on init.")
|
self.logger.log.info("Resuming playback on init.")
|
||||||
self.unpause() # Use un-pause as we don't want to jump to a new position.
|
# Use un-pause as we don't want to jump to a new position.
|
||||||
|
self.unpause()
|
||||||
else:
|
else:
|
||||||
self.logger.log.info("No file was previously loaded to resume.")
|
self.logger.log.info("No file was previously loaded to resume.")
|
||||||
|
|
||||||
|
@ -1012,7 +1032,8 @@ class Player:
|
||||||
self.last_msg_source = ""
|
self.last_msg_source = ""
|
||||||
self.last_msg = ""
|
self.last_msg = ""
|
||||||
self.logger.log.warn(
|
self.logger.log.warn(
|
||||||
"Message from unknown sender source: {}".format(source)
|
"Message from unknown sender source: {}".format(
|
||||||
|
source)
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -1080,11 +1101,13 @@ class Player:
|
||||||
"UNLOAD": lambda: self._retMsg(self.unload()),
|
"UNLOAD": lambda: self._retMsg(self.unload()),
|
||||||
"ADD": lambda: self._retMsg(
|
"ADD": lambda: self._retMsg(
|
||||||
self.add_to_plan(
|
self.add_to_plan(
|
||||||
json.loads(":".join(self.last_msg.split(":")[1:]))
|
json.loads(
|
||||||
|
":".join(self.last_msg.split(":")[1:]))
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"REMOVE": lambda: self._retMsg(
|
"REMOVE": lambda: self._retMsg(
|
||||||
self.remove_from_plan(int(self.last_msg.split(":")[1]))
|
self.remove_from_plan(
|
||||||
|
int(self.last_msg.split(":")[1]))
|
||||||
),
|
),
|
||||||
"CLEAR": lambda: self._retMsg(self.clear_channel_plan()),
|
"CLEAR": lambda: self._retMsg(self.clear_channel_plan()),
|
||||||
"SETMARKER": lambda: self._retMsg(
|
"SETMARKER": lambda: self._retMsg(
|
||||||
|
@ -1105,7 +1128,8 @@ class Player:
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"SETLIVE": lambda: self._retMsg(
|
"SETLIVE": lambda: self._retMsg(
|
||||||
self.set_live(self.last_msg.split(":")[1] == "True")
|
self.set_live(
|
||||||
|
self.last_msg.split(":")[1] == "True")
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1134,7 +1158,8 @@ class Player:
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
self.logger.log.info("Received SystemExit")
|
self.logger.log.info("Received SystemExit")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.log.exception("Received unexpected Exception: {}".format(e))
|
self.logger.log.exception(
|
||||||
|
"Received unexpected Exception: {}".format(e))
|
||||||
|
|
||||||
self.logger.log.info("Quiting player " + str(channel))
|
self.logger.log.info("Quiting player " + str(channel))
|
||||||
self.quit()
|
self.quit()
|
||||||
|
|
|
@ -47,6 +47,7 @@ class PlayerHandler:
|
||||||
|
|
||||||
sleep(0.02)
|
sleep(0.02)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.log.exception("Received unexpected exception: {}".format(e))
|
self.logger.log.exception(
|
||||||
|
"Received unexpected exception: {}".format(e))
|
||||||
del self.logger
|
del self.logger
|
||||||
_exit(0)
|
_exit(0)
|
||||||
|
|
17
server.py
17
server.py
|
@ -22,15 +22,12 @@ import json
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
from helpers.os_environment import isBundelled, isMacOS
|
from helpers.os_environment import isMacOS
|
||||||
|
|
||||||
if not isMacOS():
|
if not isMacOS():
|
||||||
# Rip, this doesn't like threading on MacOS.
|
# Rip, this doesn't like threading on MacOS.
|
||||||
import pyttsx3
|
import pyttsx3
|
||||||
|
|
||||||
if isBundelled():
|
|
||||||
import build
|
|
||||||
|
|
||||||
import package
|
import package
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
from helpers.state_manager import StateManager
|
from helpers.state_manager import StateManager
|
||||||
|
@ -111,7 +108,8 @@ class BAPSicleServer:
|
||||||
log_function = self.logger.log.info
|
log_function = self.logger.log.info
|
||||||
|
|
||||||
while (
|
while (
|
||||||
not terminator.terminate and self.state.get()["running_state"] == "running"
|
not terminator.terminate and self.state.get()[
|
||||||
|
"running_state"] == "running"
|
||||||
):
|
):
|
||||||
|
|
||||||
for channel in range(self.state.get()["num_channels"]):
|
for channel in range(self.state.get()["num_channels"]):
|
||||||
|
@ -122,7 +120,8 @@ class BAPSicleServer:
|
||||||
or not self.player[channel].is_alive()
|
or not self.player[channel].is_alive()
|
||||||
or not psutil.pid_exists(self.player[channel].pid)
|
or not psutil.pid_exists(self.player[channel].pid)
|
||||||
):
|
):
|
||||||
log_function("Player {} not running, (re)starting.".format(channel))
|
log_function(
|
||||||
|
"Player {} not running, (re)starting.".format(channel))
|
||||||
self.player[channel] = multiprocessing.Process(
|
self.player[channel] = multiprocessing.Process(
|
||||||
target=player.Player,
|
target=player.Player,
|
||||||
args=(
|
args=(
|
||||||
|
@ -183,7 +182,8 @@ class BAPSicleServer:
|
||||||
):
|
):
|
||||||
log_function("Webserver not running, (re)starting.")
|
log_function("Webserver not running, (re)starting.")
|
||||||
self.webserver = multiprocessing.Process(
|
self.webserver = multiprocessing.Process(
|
||||||
target=WebServer, args=(self.player_to_q, self.ui_to_q, self.state)
|
target=WebServer, args=(
|
||||||
|
self.player_to_q, self.ui_to_q, self.state)
|
||||||
)
|
)
|
||||||
self.webserver.start()
|
self.webserver.start()
|
||||||
|
|
||||||
|
@ -320,7 +320,8 @@ class BAPSicleServer:
|
||||||
self.player_handler.join(timeout=PROCESS_KILL_TIMEOUT_S)
|
self.player_handler.join(timeout=PROCESS_KILL_TIMEOUT_S)
|
||||||
del self.player_handler
|
del self.player_handler
|
||||||
|
|
||||||
# Now we've stopped everything else, now is the time to stop the players. This is to keep playing for as long as possible during a restart.
|
# Now we've stopped everything else, now is the time to stop the players.
|
||||||
|
# This is to keep playing for as long as possible during a restart.
|
||||||
print("Stopping Players")
|
print("Stopping Players")
|
||||||
for q in self.player_to_q:
|
for q in self.player_to_q:
|
||||||
q.put("ALL:QUIT")
|
q.put("ALL:QUIT")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from sanic import Sanic, log
|
from sanic import Sanic
|
||||||
from sanic.exceptions import NotFound, abort
|
from sanic.exceptions import NotFound, abort
|
||||||
from sanic.response import html, file, redirect
|
from sanic.response import html, file, redirect
|
||||||
from sanic.response import json as resp_json
|
from sanic.response import json as resp_json
|
||||||
|
@ -131,7 +131,8 @@ def ui_status(request):
|
||||||
for i in range(server_state.get()["num_channels"]):
|
for i in range(server_state.get()["num_channels"]):
|
||||||
channel_states.append(status(i))
|
channel_states.append(status(i))
|
||||||
|
|
||||||
data = {"channels": channel_states, "ui_page": "status", "ui_title": "Status"}
|
data = {"channels": channel_states,
|
||||||
|
"ui_page": "status", "ui_title": "Status"}
|
||||||
return render_template("status.html", data=data)
|
return render_template("status.html", data=data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -175,16 +176,20 @@ def ui_config_server_update(request):
|
||||||
server_state.update("ws_port", int(request.form.get("ws_port")))
|
server_state.update("ws_port", int(request.form.get("ws_port")))
|
||||||
|
|
||||||
serial_port = request.form.get("serial_port")
|
serial_port = request.form.get("serial_port")
|
||||||
server_state.update("serial_port", None if serial_port == "None" else serial_port)
|
server_state.update("serial_port", None if serial_port ==
|
||||||
|
"None" else serial_port)
|
||||||
|
|
||||||
# Because we're not showing the api key once it's set.
|
# Because we're not showing the api key once it's set.
|
||||||
if "myradio_api_key" in request.form and request.form.get("myradio_api_key") != "":
|
if "myradio_api_key" in request.form and request.form.get("myradio_api_key") != "":
|
||||||
server_state.update("myradio_api_key", request.form.get("myradio_api_key"))
|
server_state.update("myradio_api_key",
|
||||||
|
request.form.get("myradio_api_key"))
|
||||||
|
|
||||||
server_state.update("myradio_base_url", request.form.get("myradio_base_url"))
|
server_state.update("myradio_base_url",
|
||||||
|
request.form.get("myradio_base_url"))
|
||||||
server_state.update("myradio_api_url", request.form.get("myradio_api_url"))
|
server_state.update("myradio_api_url", request.form.get("myradio_api_url"))
|
||||||
server_state.update(
|
server_state.update(
|
||||||
"myradio_api_tracklist_source", request.form.get("myradio_api_tracklist_source")
|
"myradio_api_tracklist_source", request.form.get(
|
||||||
|
"myradio_api_tracklist_source")
|
||||||
)
|
)
|
||||||
server_state.update("tracklist_mode", request.form.get("tracklist_mode"))
|
server_state.update("tracklist_mode", request.form.get("tracklist_mode"))
|
||||||
|
|
||||||
|
@ -195,9 +200,9 @@ def ui_config_server_update(request):
|
||||||
def ui_logs_list(request):
|
def ui_logs_list(request):
|
||||||
files = os.listdir(resolve_external_file_path("/logs"))
|
files = os.listdir(resolve_external_file_path("/logs"))
|
||||||
log_files = []
|
log_files = []
|
||||||
for file in files:
|
for file_name in files:
|
||||||
if file.endswith(".log"):
|
if file_name.endswith(".log"):
|
||||||
log_files.append(file.rstrip(".log"))
|
log_files.append(file_name.rstrip(".log"))
|
||||||
|
|
||||||
log_files.sort()
|
log_files.sort()
|
||||||
data = {"ui_page": "logs", "ui_title": "Logs", "logs": log_files}
|
data = {"ui_page": "logs", "ui_title": "Logs", "logs": log_files}
|
||||||
|
@ -215,7 +220,7 @@ def ui_logs_render(request, path):
|
||||||
log_file = open(resolve_external_file_path("/logs/{}.log").format(path))
|
log_file = open(resolve_external_file_path("/logs/{}.log").format(path))
|
||||||
data = {
|
data = {
|
||||||
"logs": log_file.read().splitlines()[
|
"logs": log_file.read().splitlines()[
|
||||||
-300 * page : (-300 * (page - 1) if page > 1 else None)
|
-300 * page:(-300 * (page - 1) if page > 1 else None)
|
||||||
][::-1],
|
][::-1],
|
||||||
"ui_page": "logs",
|
"ui_page": "logs",
|
||||||
"ui_title": "Logs - {}".format(path),
|
"ui_title": "Logs - {}".format(path),
|
||||||
|
@ -380,7 +385,8 @@ def json_status(request):
|
||||||
async def audio_file(request, type: str, id: int):
|
async def audio_file(request, type: str, id: int):
|
||||||
if type not in ["managed", "track"]:
|
if type not in ["managed", "track"]:
|
||||||
abort(404)
|
abort(404)
|
||||||
filename = resolve_external_file_path("music-tmp/{}-{}.mp3".format(type, id))
|
filename = resolve_external_file_path(
|
||||||
|
"music-tmp/{}-{}.mp3".format(type, id))
|
||||||
|
|
||||||
# Swap with a normalised version if it's ready, else returns original.
|
# Swap with a normalised version if it's ready, else returns original.
|
||||||
filename = get_normalised_filename_if_available(filename)
|
filename = get_normalised_filename_if_available(filename)
|
||||||
|
@ -411,7 +417,8 @@ app.static(
|
||||||
|
|
||||||
def status(channel: int):
|
def status(channel: int):
|
||||||
while not player_from_q[channel].empty():
|
while not player_from_q[channel].empty():
|
||||||
player_from_q[channel].get() # Just waste any previous status responses.
|
# Just waste any previous status responses.
|
||||||
|
player_from_q[channel].get()
|
||||||
|
|
||||||
player_to_q[channel].put("UI:STATUS")
|
player_to_q[channel].put("UI:STATUS")
|
||||||
retries = 0
|
retries = 0
|
||||||
|
@ -421,7 +428,7 @@ def status(channel: int):
|
||||||
if response.startswith("UI:STATUS:"):
|
if response.startswith("UI:STATUS:"):
|
||||||
response = response.split(":", 2)[2]
|
response = response.split(":", 2)[2]
|
||||||
# TODO: Handle OKAY / FAIL
|
# TODO: Handle OKAY / FAIL
|
||||||
response = response[response.index(":") + 1 :]
|
response = response[response.index(":") + 1:]
|
||||||
try:
|
try:
|
||||||
response = json.loads(response)
|
response = json.loads(response)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -71,7 +71,8 @@ class WebsocketServer:
|
||||||
for channel in self.channel_to_q:
|
for channel in self.channel_to_q:
|
||||||
channel.put("WEBSOCKET:STATUS")
|
channel.put("WEBSOCKET:STATUS")
|
||||||
|
|
||||||
self.from_webstudio = asyncio.create_task(self.handle_from_webstudio(websocket))
|
self.from_webstudio = asyncio.create_task(
|
||||||
|
self.handle_from_webstudio(websocket))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.threads = await shield(asyncio.gather(self.from_webstudio))
|
self.threads = await shield(asyncio.gather(self.from_webstudio))
|
||||||
|
@ -93,7 +94,8 @@ class WebsocketServer:
|
||||||
await asyncio.wait([conn.send(message) for conn in self.baps_clients])
|
await asyncio.wait([conn.send(message) for conn in self.baps_clients])
|
||||||
|
|
||||||
except websockets.exceptions.ConnectionClosedError as e:
|
except websockets.exceptions.ConnectionClosedError as e:
|
||||||
self.logger.log.error("Client Disconncted {}, {}".format(websocket, e))
|
self.logger.log.error(
|
||||||
|
"Client Disconncted {}, {}".format(websocket, e))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.log.exception(
|
self.logger.log.exception(
|
||||||
|
@ -176,13 +178,15 @@ class WebsocketServer:
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
self.logger.log.exception(
|
self.logger.log.exception(
|
||||||
"Error decoding extra data {} for command {} ".format(e, command)
|
"Error decoding extra data {} for command {} ".format(
|
||||||
|
e, command)
|
||||||
)
|
)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Stick the message together and send!
|
# Stick the message together and send!
|
||||||
message += (
|
message += (
|
||||||
command # Put the command in at the end, in case MOVE etc changed it.
|
# Put the command in at the end, in case MOVE etc changed it.
|
||||||
|
command
|
||||||
)
|
)
|
||||||
if extra != "":
|
if extra != "":
|
||||||
message += ":" + extra
|
message += ":" + extra
|
||||||
|
@ -197,7 +201,8 @@ class WebsocketServer:
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logger.log.error("Command missing from message. Data: {}".format(data))
|
self.logger.log.error(
|
||||||
|
"Command missing from message. Data: {}".format(data))
|
||||||
|
|
||||||
async def handle_to_webstudio(self):
|
async def handle_to_webstudio(self):
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue