Exemplo n.º 1
0
 def receive_incoming_messages():
     message_logger = setup_logger("messages", "messages.log")
     try:
         # If all instances have the same ID and this process gets stuck open somehow, it will cause problems as
         # soon as another client with the same client ID connects (reconnecting loop between two processes).
         randomized_client_id = f"{CLIENT_ID_BASE}_{''.join(choices(ascii_letters, k=CLIENT_ID_POST_LENGTH))}"
         auth = {
             "username": CONFIG.username,
             "password": CONFIG.password
         }
         # Blocks forever.
         subscribe_with_callback(callback=callback,
                                 topics=[CONFIG.topic],
                                 hostname=CONFIG.host,
                                 port=CONFIG.port,
                                 client_id=randomized_client_id,
                                 auth=auth,
                                 tls={"tls_version": ssl.PROTOCOL_TLS})
     except ConnectionRefusedError as e:
         message_logger.warning("Could not contact the MQTT server.")
         # Just to give a slightly cleaner message to the user.
         raise ConnectionRefusedError(
             "Could not reach MQTT server.") from e
     except Exception as e:
         message_logger.warning(f"Unknown exception: {e}")
         raise type(e)(f"Unknown Exception: {e}") from e
     except KeyboardInterrupt:
         pass
Exemplo n.º 2
0
 def process_video_requests():
     dl_logger = setup_logger("download", "download.log")
     youtube_id = None  # To make the static analyzer happy in the except block.
     try:
         while True:
             youtube_id = self._receive_queue.get()
             download_audio(youtube_id,
                            DOWNLOAD_DIRECTORY)  # A long, blocking call
             self._downloaded_queue.put(youtube_id)
     except YoutubeDLError as e:
         dl_logger.warning(
             f"Error downloading video ID {youtube_id}: {e}")
     except Exception as e:
         dl_logger.warning(f"Unknown exception: {e}")
         raise type(e)(f"Unknown Exception: {e}") from e
     except KeyboardInterrupt:
         pass
Exemplo n.º 3
0
from __future__ import annotations
import subprocess as sp
from logging_util import setup_logger

player_logger = setup_logger("player", "player.log")

COMMAND = "mpg123 -R"

# Commands are only characters in the ASCII set.
STDIN_ENCODING = "ascii"

END_PLAYBACK_SENTINEL = b"@P 0"
ERROR_SENTINEL = b"@E"


def clamp(n: int, min_n: int, max_n: int) -> int:
    return min(max_n, max(min_n, n))


# TODO: Make play_from_path non-blocking, and provide a wait_for_song_finish() method that relies on an Event?
class SimpleAudioPlayer:
    """An audio player wrapper over mpg123 that allows for control of playback."""
    def __init__(self):
        self._player_process: sp.Popen = sp.Popen(COMMAND.split(),
                                                  stdin=sp.PIPE,
                                                  stdout=sp.PIPE,
                                                  stderr=sp.DEVNULL)

        # mpg123 does absolute volume setting. To avoid needing to do a lookup to set volume, we're maintaining
        #  an internal volume level. This value is relative to and independent of the system volume.
        self._volume = 100
Exemplo n.º 4
0
#!/usr/bin/env python3
from time import sleep
from argparse import ArgumentParser

from managed_audio_player import ManagedAudioPlayer
from logging_util import setup_logger

# To avoid aggressive restarting
RESTART_DELAY_SECS = 3
SONG_BREAK_DELAY_SECS = 2

main_logger = setup_logger("main", "main.log")


def controlless_play_loop(player: ManagedAudioPlayer):
    """Simply plays songs as they're received."""
    while True:
        song_finished = player.play_current_song()  # Will block during playback
        if song_finished:
            player.next_song()
        sleep(SONG_BREAK_DELAY_SECS)


def main(use_controls: bool = True) -> None:
    with ManagedAudioPlayer() as player:
        if use_controls:
            import controls
            controls.main_control_loop(player, SONG_BREAK_DELAY_SECS)
        else:
            controlless_play_loop(player)
Exemplo n.º 5
0
PAYLOAD_ENCODING = "UTF-8"
MUSIC_EXTENSION = "mp3"

CLIENT_ID_BASE = "MusicPiClient"
CLIENT_ID_POST_LENGTH = 5

CONFIG_PATH = "broker.cfg"
CONFIG = Config.from_file(CONFIG_PATH)

# The current folder. Prevents relying on the CWD, which may cause problems if the client
#   is started from a different directory.
PROJECT_PATH = Path(__file__).parent.absolute()
DOWNLOAD_DIRECTORY = PROJECT_PATH / "download"

service_logger = setup_logger("song_service", "song_service.log")


def _song_path(youtube_id: str) -> str:
    return f"{PurePath(DOWNLOAD_DIRECTORY) / youtube_id}.{MUSIC_EXTENSION}"


def _clear_downloaded() -> None:
    """Deletes all regular files in the download directory."""
    if DOWNLOAD_DIRECTORY.is_dir():  # If the download directory exists
        for file in os.listdir(DOWNLOAD_DIRECTORY):
            path = Path(DOWNLOAD_DIRECTORY, file)
            if path.is_file():
                os.remove(path)