Skip to content

Dab audio companion

DabAudioCompanionTrackObserver

Bases: TrackObserver

Update track data in a DAB+ transmission through the 'Audio Companion' API.

Source code in nowplaying/track/observers/dab_audio_companion.py
class DabAudioCompanionTrackObserver(TrackObserver):
    """Update track data in a DAB+ transmission through the 'Audio Companion' API."""

    name = "DAB+ Audio Companion"

    class Options(TrackObserver.Options):  # pragma: no coverage
        @classmethod
        def args(cls, args: configargparse.ArgParser):
            args.add_argument(
                "-d",
                "--dab",
                action="append",
                help="DAB audio companion base URL, allowed multiple times (ie. http://dab.example.org:8080)",
                default=[],
            )
            # TODO v3 remove when stable
            args.add_argument(
                "--dab-send-dls",
                type=bool,
                nargs="?",
                dest="dab_send_dls",
                help="Send artist/title to DAB companions dls endpoint (default: True)",
                default=True,
            )

        def __init__(self, url: str, dl_plus: bool = True) -> None:
            self.url: str = url
            self.dl_plus: bool = dl_plus

    def __init__(self, options: Options):
        self._options = options
        self.base_url = options.url + "/api/setDLS"
        self.dls_enabled = self.last_frame_was_dl_plus = self._options.dl_plus
        logger.info(
            "DAB+ Audio Companion initialised with URL: %s, DLS+ enabled: %r"
            % (self.base_url, self.dls_enabled)
        )

    def track_started(self, track: Track):
        logger.info(f"Updating DAB+ DLS for track: {track.artist} - {track.title}")
        # TODO v3 remove _track_started_plain
        if not self.dls_enabled:
            return self._track_started_plain(track)

        params = {}

        if track.get_duration() < timedelta(seconds=5):
            logger.info(
                "Track is less than 5 seconds, not sending to DAB+ Audio Companion"
            )
            return

        if not track.has_default_title() and not track.has_default_artist():
            params["artist"] = track.artist
            params["title"] = track.title
            self.last_frame_was_dl_plus = True
        elif self.last_frame_was_dl_plus:
            logger.info(
                "Track has default info, using show instead. Sending DLS+ delete tags."
            )
            message = DLPlusMessage()
            # track.artist contains station name if no artist is set
            message.add_dlp_object(DLPlusObject("STATIONNAME.LONG", track.artist))
            # TODO complete integration w/o f-string
            #      PROGAMME.NOW for show name needs a carousel + deleting
            # message.add_dlp_object(DLPlusObject("PROGRAMME.NOW", track.show.name))
            message.add_dlp_object(DLPlusObject("ITEM.TITLE", delete=True))
            message.add_dlp_object(DLPlusObject("ITEM.ARTIST", delete=True))
            message.build(f"$STATIONNAME.LONG - {track.show.name}")
            params["dls"] = str(ODRPadEncRenderer(message))
            self.last_frame_was_dl_plus = False
        else:
            logger.info("Track has default info, using show instead")
            # track.artist contains station name if no artist is set
            params["dls"] = f"{track.artist} - {track.show.name}"

        logger.info(
            f"DAB+ Audio Companion URL: {self.base_url} "
            f"data: {params} is DL+: {self.last_frame_was_dl_plus}"
        )

        resp = requests.post(self.base_url, params)
        if resp.status_code != 200:
            logger.error(f"DAB+ Audio Companion API call failed: {resp.text}")

    def _track_started_plain(self, track):
        # TODO v3 remove once we always send DLS with v3
        title = track.title

        if track.has_default_title() and track.has_default_artist():
            logger.info(
                "%s: Track has default info, using show instead" % self.__class__
            )

            title = track.show.name

        # artist is an unicode string which we have to encode into UTF-8
        # http://bugs.python.org/issue216716
        song_string = urllib.parse.quote_plus(
            f"{track.artist.encode('utf8')} - {title.encode('utf8')}"
        )

        update_url = f"{self.base_url}?dls={song_string}"

        logger.info("DAB+ Audio Companion URL: " + update_url)

        urllib.request.urlopen(update_url)

    def track_finished(self, track):
        return True