import logging import time import json from typing import Optional import pykka import requests from mopidy.core import CoreListener logger = logging.getLogger(__name__) class WebhooksFrontend(pykka.ThreadingActor, CoreListener): def __init__(self, config, core): super().__init__() self.config = config self.webhook_urls = [] self.last_start_time = None def on_start(self): self.webhook_urls = self.config["webhooks"]["urls"].split(",") self.webhook_tokens = self.config["webhooks"]["tokens"].split(",") logger.info(f"Parsing webhook URLs and tokens: {self.webhook_urls}") def _build_post_data(self, track, time_position: Optional[int]=None) -> dict: artists = ", ".join(sorted([a.name for a in track.artists])) artists_list = [a for a in track.artists] try: musicbrainz_artist_id = artists_list[0].musicbrainz_id except IndexError: musicbrainz_artist_id = None duration = track.length and track.length // 1000 or 0 album_name = "" if track.album: album_name = getattr(track.album, "name") return { "name": track.name, "artist": artists, "album": album_name, "track_number": track.track_no, "run_time_ticks": track.length, "run_time": str(duration), "playback_time_ticks": time_position, "musicbrainz_track_id": track.musicbrainz_id if track.album else "", "musicbrainz_album_id": track.album.musicbrainz_id if track.album else "", "musicbrainz_artist_id": musicbrainz_artist_id, "mopidy_uri": track.uri, "primary_artist_mopidy_uri": artists_list[0].uri, } def _post_update_to_webhooks(self, post_data: dict, status: str): post_data["status"] = status for index, webhook_url in enumerate(self.webhook_urls): token = "" headers = {} try: token = self.webhook_tokens[index] except IndexError: logger.info(f"No token found for Webhook URL: {webhook_url}") if token: headers["Authorization"] = f"Token {token}" response = requests.post( webhook_url, json=json.dumps(post_data), headers=headers ) logger.info(response) def track_playback_started(self, tl_track): track = tl_track.track artists = ", ".join(sorted([a.name for a in track.artists])) self.last_start_time = int(time.time()) logger.debug(f"Now playing track: {artists} - {track.name}") post_data = self._build_post_data(tl_track.track, time_position=0) # Build post data to send to urls if not self.webhook_urls: logger.info("No webhook URLS are configured ") return logger.info(f"Scrobbling via webhooks: {artists} - {track.name}") self._post_update_to_webhooks(post_data, "started") def track_playback_ended(self, tl_track, time_position): track = tl_track.track artists = ", ".join(sorted([a.name for a in track.artists])) duration = track.length and track.length // 1000 or 0 time_position_sec = time_position // 1000 post_data = self._build_post_data(tl_track.track, time_position=time_position) if time_position_sec < duration // 2 and time_position_sec < 240: logger.debug( "Track not played long enough to scrobble. (50% or 240s)" ) return if self.last_start_time is None: self.last_start_time = int(time.time()) - duration logger.info( f"Scrobbling finished via webhooks: {artists} - {track.name}" ) self._post_update_to_webhooks(post_data, "stopped") def track_playback_paused(self, tl_track, time_position): track = tl_track.track artists = ", ".join(sorted([a.name for a in track.artists])) duration = track.length and track.length // 1000 or 0 time_position_sec = time_position // 1000 post_data = self._build_post_data(tl_track.track, time_position=time_position) if self.last_start_time is None: self.last_start_time = int(time.time()) - duration logger.info( f"Scrobbling paused via webhooks: {artists} - {track.name}" ) self._post_update_to_webhooks(post_data, "paused") def track_playback_resumed(self, tl_track, time_position): track = tl_track.track artists = ", ".join(sorted([a.name for a in track.artists])) self.last_start_time = int(time.time()) logger.debug(f"Now resuming track: {artists} - {track.name}") post_data = self._build_post_data(tl_track.track, time_position=time_position) # Build post data to send to urls if not self.webhook_urls: logger.info("No webhook URLS are configured ") return logger.info(f"Scrobbling via webhooks: {artists} - {track.name}") self._post_update_to_webhooks(post_data, "resumed")