コード例 #1
0
    def getSerialPorts(cls) -> List[Optional[str]]:
        """Lists serial port names

        :raises EnvironmentError:
            On unsupported or unknown platforms
        :returns:
            A list of the serial ports available on the system
        """
        if isWindows():
            ports = [port.device for port in comports()]
        elif isLinux():
            # this excludes your current terminal "/dev/tty"
            ports = glob.glob("/dev/tty[A-Za-z]*")
        elif isMacOS():
            ports = glob.glob("/dev/tty.*")
        else:
            raise EnvironmentError("Unsupported platform")

        valid: List[str] = ports

        result: List[Optional[str]] = []

        if len(valid) > 0:
            valid.sort()

        result.append(None)  # Add the None option
        result.extend(valid)

        return result
コード例 #2
0
 def _getSDAudioDevices(cls):
     # To update the list of devices
     # Sadly this only works on Windows. Linux hangs, MacOS crashes.
     if isWindows():
         sd._terminate()
         sd._initialize()
     devices: sd.DeviceList = sd.query_devices()
     return devices
コード例 #3
0
    def getAudioOutputs(cls) -> Tuple[List[Dict]]:

        host_apis = list(sd.query_hostapis())
        devices: sd.DeviceList = cls._getSDAudioDevices()

        for host_api_id in range(len(host_apis)):
            # Linux SDL uses PortAudio, which SoundDevice doesn't find. So mark all as unsable.
            if (isWindows() and host_apis[host_api_id]["name"]
                    not in WINDOWS_APIS) or (isLinux()):
                host_apis[host_api_id]["usable"] = False
            else:
                host_apis[host_api_id]["usable"] = True

            host_api_devices = (device for device in devices
                                if device["hostapi"] == host_api_id)

            outputs: List[Dict] = list(filter(cls._isOutput, host_api_devices))
            outputs = sorted(outputs, key=lambda k: k["name"])

            host_apis[host_api_id]["output_devices"] = outputs

        return host_apis
コード例 #4
0
    def __init__(self, channel_from_q: List[Queue], server_config: StateManager):

        self.logger = LoggingManager("FileManager")
        self.api = MyRadioAPI(self.logger, server_config)

        process_title = "File Manager"
        setproctitle(process_title)
        current_process().name = process_title

        terminator = Terminator()

        self.normalisation_mode = server_config.get()["normalisation_mode"]

        if self.normalisation_mode != "on":
            self.logger.log.info("Normalisation is disabled.")
        else:
            self.logger.log.info("Normalisation is enabled.")

        self.channel_count = len(channel_from_q)
        self.channel_received = None
        self.last_known_show_plan = [[]] * self.channel_count
        self.next_channel_preload = 0
        self.known_channels_preloaded = [False] * self.channel_count
        self.known_channels_normalised = [False] * self.channel_count
        self.last_known_item_ids = [[]] * self.channel_count
        try:

            while not terminator.terminate:
                # If all channels have received the delete command, reset for the next one.
                if (
                    self.channel_received is None
                    or self.channel_received == [True] * self.channel_count
                ):
                    self.channel_received = [False] * self.channel_count

                for channel in range(self.channel_count):
                    try:
                        message = channel_from_q[channel].get_nowait()
                    except Exception:
                        continue

                    try:
                        # source = message.split(":")[0]
                        command = message.split(":", 2)[1]

                        # If we have requested a new show plan, empty the music-tmp directory for the previous show.
                        if command == "GETPLAN":

                            if (
                                self.channel_received != [
                                    False] * self.channel_count
                                and self.channel_received[channel] is False
                            ):
                                # 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.

                                self.channel_received[channel] = True
                                continue

                            # 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.
                            path: str = resolve_external_file_path(
                                "/music-tmp/")

                            if not os.path.isdir(path):
                                self.logger.log.warning(
                                    "Music-tmp folder is missing, not handling."
                                )
                                continue

                            files = [
                                f
                                for f in os.listdir(path)
                                if os.path.isfile(os.path.join(path, f))
                            ]
                            for file in files:
                                if isWindows():
                                    filepath = path + "\\" + file
                                else:
                                    filepath = path + "/" + file
                                self.logger.log.info(
                                    "Removing file {} on new show load.".format(
                                        filepath
                                    )
                                )
                                try:
                                    os.remove(filepath)
                                except Exception:
                                    self.logger.log.warning(
                                        "Failed to remove, skipping. Likely file is still in use."
                                    )
                                    continue
                            self.channel_received[channel] = True
                            self.known_channels_preloaded = [
                                False] * self.channel_count
                            self.known_channels_normalised = [
                                False
                            ] * self.channel_count

                        # If we receive a new status message, let's check for files which have not been pre-loaded.
                        if command == "STATUS":
                            extra = message.split(":", 3)
                            if extra[2] != "OKAY":
                                continue

                            status = json.loads(extra[3])
                            show_plan = status["show_plan"]
                            item_ids = []
                            for item in show_plan:
                                item_ids += item["timeslotitemid"]

                            # If the new status update has a different order / list of items,
                            # let's update the show plan we know about
                            # This will trigger the chunk below to do the rounds again and preload any new files.
                            if item_ids != self.last_known_item_ids[channel]:
                                self.last_known_item_ids[channel] = item_ids
                                self.last_known_show_plan[channel] = show_plan
                                self.known_channels_preloaded[channel] = False

                    except Exception:
                        self.logger.log.exception(
                            "Failed to handle message {} on channel {}.".format(
                                message, channel
                            )
                        )

                # Let's try preload / normalise some files now we're free of messages.
                preloaded = self.do_preload()
                normalised = self.do_normalise()

                if not preloaded and not normalised:
                    # We didn't do any hard work, let's sleep.
                    sleep(0.2)

        except Exception as e:
            self.logger.log.exception(
                "Received unexpected exception: {}".format(e))
        del self.logger
コード例 #5
0
import json
import os
from helpers.os_environment import isWindows

dir_path = os.path.dirname(os.path.realpath(__file__))
parent_path = os.path.dirname(dir_path)

in_file = open('build-exe-config.template.json', 'r')
config = json.loads(in_file.read())
in_file.close()

for option in config["pyinstallerOptions"]:
    if option["optionDest"] in ["datas", "filenames", "icon_file"]:
        # If we wanted a relative output directory, this will go missing in abspath on windows.
        relative_fix = False
        split = option["value"].split(";")
        if len(split) > 1 and split[1] == "./":
            relative_fix = True

        option["value"] = os.path.abspath(parent_path + option["value"])
        if not isWindows():
            option["value"] = option["value"].replace(";", ":")
        elif relative_fix:
            # Add the windows relative path.
            option["value"] += "./"

out_file = open('build-exe-config.json', 'w')
out_file.write(json.dumps(config, indent=2))
out_file.close()
コード例 #6
0
from typing import Any, Dict, List, Optional, Tuple
import sounddevice as sd
from helpers.os_environment import isLinux, isMacOS, isWindows
import os

os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
if isLinux():
    os.putenv('SDL_AUDIODRIVER', 'pulseaudio')
import pygame._sdl2 as sdl2
from pygame import mixer
import glob

if isWindows():
    from serial.tools.list_ports_windows import comports

# TODO: https://wiki.libsdl.org/FAQUsingSDL maybe try setting some of these env variables for choosing different host APIs?
WINDOWS_APIS = ["Windows DirectSound"]


class DeviceManager:
    @classmethod
    def _isOutput(cls, device: Dict[str, Any]) -> bool:
        return device["max_output_channels"] > 0

    @classmethod
    def _isHostAPI(cls, host_api) -> bool:
        return host_api

    @classmethod
    def _getSDAudioDevices(cls):
        # To update the list of devices