Skip to content

Ticker

TickerTrackObserver generates the songticker.xml file.

TickerTrackObserver

Bases: TrackObserver

Writes the new ticker feed file.

The feed file will be consumed by the song-ticker on the RaBe website. This is the successor of the long gone PseudoRssTrackObserver format used by the pre-WordPress website. This version here gets consumed by the WordPress website.

Source code in nowplaying/track/observers/ticker.py
class TickerTrackObserver(TrackObserver):
    """Writes the new ticker feed file.

    The feed file will be consumed by the song-ticker on the RaBe website. This is the
    successor of the long gone PseudoRssTrackObserver format used by the pre-WordPress
    website. This version here gets consumed by the WordPress website.
    """

    name = "Ticker"

    class Options(TrackObserver.Options):
        """TickerTrackObserver options."""

        @classmethod
        def args(
            cls: type[TTrackObserverOptions],
            args: configargparse.ArgParser,
        ) -> None:
            """Build args."""
            args.add_argument(
                "--xml-output",
                dest="ticker_output_file",
                help="ticker XML output format",
                default="/var/www/localhost/htdocs/songticker/0.9.3/current.xml",
            )

        def __init__(self: Self, file_path: str) -> None:
            """Create TickerTrackObserver.Config."""
            self.file_path = file_path

    def __init__(self: Self, options: Options) -> None:
        """Create TickerTrackObserver."""
        warnings.warn(
            "The XML ticker format will be replaced with a JSON variant in the future",
            PendingDeprecationWarning,
            stacklevel=2,
        )
        self.ticker_file_path = options.file_path

    def track_started(self: Self, track: Track) -> None:
        """Track started."""
        logger.info(
            "Updating Ticker XML file for track: %s - %s",
            track.artist,
            track.title,
        )
        try:
            tz = pytz.timezone("Europe/Zurich")
        except (
            pytz.exceptions.UnknownTimeZoneError
        ):  # pragma: no coverage due to not knowing how to trigger
            tz = pytz.timezone("UTC")

        now = isodate.datetime_isoformat(datetime.datetime.now(tz))

        MAIN_NAMESPACE = "http://rabe.ch/schema/ticker.xsd"  # noqa: N806
        XLINK_NAMESPACE = "http://www.w3.org/1999/xlink"  # noqa: N806
        XLINK = "{%s}" % XLINK_NAMESPACE  # noqa: N806, UP031

        E = lxml.builder.ElementMaker(  # noqa: N806
            namespace=MAIN_NAMESPACE,
            nsmap={None: MAIN_NAMESPACE, "xlink": XLINK_NAMESPACE},
        )
        show_ref = E.link(track.show.url)
        show_ref.attrib[XLINK + "type"] = "simple"
        show_ref.attrib[XLINK + "href"] = track.show.url
        show_ref.attrib[XLINK + "show"] = "replace"

        ticker = E.ticker(
            E.identifier(f"ticker-{uuid.uuid4()}"),
            E.creator("now-playing daemon v2"),
            E.date(now),
            E.show(
                E.name(track.show.name),
                show_ref,
                E.startTime(
                    isodate.datetime_isoformat(track.show.starttime.astimezone(tz)),
                ),
                E.endTime(
                    isodate.datetime_isoformat(track.show.endtime.astimezone(tz)),
                ),
                id=track.show.uuid,
            ),
            E.track(
                E.show(track.show.name, ref=track.show.uuid),
                E.artist(track.artist),
                E.title(track.title),
                E.startTime(isodate.datetime_isoformat(track.starttime.astimezone(tz))),
                E.endTime(isodate.datetime_isoformat(track.endtime.astimezone(tz))),
                id=track.uuid,
            ),
        )
        lxml.etree.ElementTree(ticker).write(
            self.ticker_file_path,
            pretty_print=True,
            xml_declaration=True,
            encoding="utf-8",
        )

    def track_finished(self: Self, _: Track) -> None:
        """Track finished."""

Options

Bases: Options

TickerTrackObserver options.

Source code in nowplaying/track/observers/ticker.py
class Options(TrackObserver.Options):
    """TickerTrackObserver options."""

    @classmethod
    def args(
        cls: type[TTrackObserverOptions],
        args: configargparse.ArgParser,
    ) -> None:
        """Build args."""
        args.add_argument(
            "--xml-output",
            dest="ticker_output_file",
            help="ticker XML output format",
            default="/var/www/localhost/htdocs/songticker/0.9.3/current.xml",
        )

    def __init__(self: Self, file_path: str) -> None:
        """Create TickerTrackObserver.Config."""
        self.file_path = file_path

__init__(file_path)

Create TickerTrackObserver.Config.

Source code in nowplaying/track/observers/ticker.py
def __init__(self: Self, file_path: str) -> None:
    """Create TickerTrackObserver.Config."""
    self.file_path = file_path

args(args) classmethod

Build args.

Source code in nowplaying/track/observers/ticker.py
@classmethod
def args(
    cls: type[TTrackObserverOptions],
    args: configargparse.ArgParser,
) -> None:
    """Build args."""
    args.add_argument(
        "--xml-output",
        dest="ticker_output_file",
        help="ticker XML output format",
        default="/var/www/localhost/htdocs/songticker/0.9.3/current.xml",
    )

__init__(options)

Create TickerTrackObserver.

Source code in nowplaying/track/observers/ticker.py
def __init__(self: Self, options: Options) -> None:
    """Create TickerTrackObserver."""
    warnings.warn(
        "The XML ticker format will be replaced with a JSON variant in the future",
        PendingDeprecationWarning,
        stacklevel=2,
    )
    self.ticker_file_path = options.file_path

track_finished(_)

Track finished.

Source code in nowplaying/track/observers/ticker.py
def track_finished(self: Self, _: Track) -> None:
    """Track finished."""

track_started(track)

Track started.

Source code in nowplaying/track/observers/ticker.py
def track_started(self: Self, track: Track) -> None:
    """Track started."""
    logger.info(
        "Updating Ticker XML file for track: %s - %s",
        track.artist,
        track.title,
    )
    try:
        tz = pytz.timezone("Europe/Zurich")
    except (
        pytz.exceptions.UnknownTimeZoneError
    ):  # pragma: no coverage due to not knowing how to trigger
        tz = pytz.timezone("UTC")

    now = isodate.datetime_isoformat(datetime.datetime.now(tz))

    MAIN_NAMESPACE = "http://rabe.ch/schema/ticker.xsd"  # noqa: N806
    XLINK_NAMESPACE = "http://www.w3.org/1999/xlink"  # noqa: N806
    XLINK = "{%s}" % XLINK_NAMESPACE  # noqa: N806, UP031

    E = lxml.builder.ElementMaker(  # noqa: N806
        namespace=MAIN_NAMESPACE,
        nsmap={None: MAIN_NAMESPACE, "xlink": XLINK_NAMESPACE},
    )
    show_ref = E.link(track.show.url)
    show_ref.attrib[XLINK + "type"] = "simple"
    show_ref.attrib[XLINK + "href"] = track.show.url
    show_ref.attrib[XLINK + "show"] = "replace"

    ticker = E.ticker(
        E.identifier(f"ticker-{uuid.uuid4()}"),
        E.creator("now-playing daemon v2"),
        E.date(now),
        E.show(
            E.name(track.show.name),
            show_ref,
            E.startTime(
                isodate.datetime_isoformat(track.show.starttime.astimezone(tz)),
            ),
            E.endTime(
                isodate.datetime_isoformat(track.show.endtime.astimezone(tz)),
            ),
            id=track.show.uuid,
        ),
        E.track(
            E.show(track.show.name, ref=track.show.uuid),
            E.artist(track.artist),
            E.title(track.title),
            E.startTime(isodate.datetime_isoformat(track.starttime.astimezone(tz))),
            E.endTime(isodate.datetime_isoformat(track.endtime.astimezone(tz))),
            id=track.uuid,
        ),
    )
    lxml.etree.ElementTree(ticker).write(
        self.ticker_file_path,
        pretty_print=True,
        xml_declaration=True,
        encoding="utf-8",
    )