Add subsonic scrobbler plugin (#2168)
authorAndreas <cluster1@gmx.at>
Thu, 15 May 2025 20:10:11 +0000 (22:10 +0200)
committerGitHub <noreply@github.com>
Thu, 15 May 2025 20:10:11 +0000 (22:10 +0200)
music_assistant/providers/subsonic_scrobble/__init__.py [new file with mode: 0644]
music_assistant/providers/subsonic_scrobble/icon.svg [new file with mode: 0644]
music_assistant/providers/subsonic_scrobble/icon_monochrome.svg [new file with mode: 0644]
music_assistant/providers/subsonic_scrobble/manifest.json [new file with mode: 0644]

diff --git a/music_assistant/providers/subsonic_scrobble/__init__.py b/music_assistant/providers/subsonic_scrobble/__init__.py
new file mode 100644 (file)
index 0000000..4648e3f
--- /dev/null
@@ -0,0 +1,167 @@
+"""Allows scrobbling of tracks back to the Subsonic media server."""
+
+import asyncio
+import logging
+import time
+from collections.abc import Callable
+
+from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig
+from music_assistant_models.enums import EventType, MediaType
+from music_assistant_models.errors import SetupFailedError
+from music_assistant_models.media_items import Audiobook, PodcastEpisode, Track
+from music_assistant_models.playback_progress_report import MediaItemPlaybackProgressReport
+from music_assistant_models.provider import ProviderManifest
+
+from music_assistant.helpers.scrobbler import ScrobblerHelper
+from music_assistant.helpers.uri import parse_uri
+from music_assistant.mass import MusicAssistant
+from music_assistant.models import ProviderInstanceType
+from music_assistant.models.plugin import PluginProvider
+from music_assistant.providers.opensubsonic.sonic_provider import OpenSonicProvider
+
+
+async def setup(
+    mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
+) -> ProviderInstanceType:
+    """Initialize provider(instance) with given configuration."""
+    sonic_prov = mass.get_provider("opensubsonic")
+    if not sonic_prov or not isinstance(sonic_prov, OpenSonicProvider):
+        raise SetupFailedError("A Open Subsonic Music provider must be configured first.")
+
+    return SubsonicScrobbleProvider(mass, manifest, config)
+
+
+class SubsonicScrobbleProvider(PluginProvider):
+    """Plugin provider to support scrobbling of tracks."""
+
+    def __init__(
+        self, mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
+    ) -> None:
+        """Initialize MusicProvider."""
+        super().__init__(mass, manifest, config)
+        self._on_unload: list[Callable[[], None]] = []
+
+    async def loaded_in_mass(self) -> None:
+        """Call after the provider has been loaded."""
+        await super().loaded_in_mass()
+
+        handler = SubsonicScrobbleEventHandler(self.mass, self.logger)
+
+        # subscribe to media_item_played event
+        self._on_unload.append(
+            self.mass.subscribe(handler._on_mass_media_item_played, EventType.MEDIA_ITEM_PLAYED)
+        )
+
+    async def unload(self, is_removed: bool = False) -> None:
+        """
+        Handle unload/close of the provider.
+
+        Called when provider is deregistered (e.g. MA exiting or config reloading).
+        """
+        for unload_cb in self._on_unload:
+            unload_cb()
+
+
+class SubsonicScrobbleEventHandler(ScrobblerHelper):
+    """Handles the scrobbling event handling."""
+
+    def __init__(self, mass: MusicAssistant, logger: logging.Logger) -> None:
+        """Initialize."""
+        super().__init__(logger)
+        self.mass = mass
+
+    def _is_scrobblable_media_type(self, media_type: MediaType) -> bool:
+        """Return true if the given OpenSubsonic media type can be scrobbled, false otherwise."""
+        return media_type in (
+            MediaType.TRACK,
+            MediaType.AUDIOBOOK,
+            MediaType.PODCAST_EPISODE,
+        )
+
+    async def _get_subsonic_provider_and_item_id(
+        self, media_type: MediaType, provider_instance_id_or_domain: str, item_id: str
+    ) -> tuple[None | OpenSonicProvider, str]:
+        """Return a OpenSonicProvider or None if no subsonic provider, and the Subsonic item_id.
+
+        Returns:
+            Tuple[OpenSonicProvider | None, str]: The provider or None, and the Subsonic item_id.
+        """
+        if provider_instance_id_or_domain == "library":
+            # unwrap library item to check if we have a subsonic mapping...
+            library_item = await self.mass.music.get_library_item_by_prov_id(
+                media_type, item_id, provider_instance_id_or_domain
+            )
+            if library_item is None:
+                return None, item_id
+            assert isinstance(library_item, Track | Audiobook | PodcastEpisode)
+            for mapping in library_item.provider_mappings:
+                if mapping.provider_domain.startswith("opensubsonic"):
+                    # found a subsonic mapping, proceed...
+                    prov = self.mass.get_provider(mapping.provider_instance)
+                    assert isinstance(prov, OpenSonicProvider)
+                    return prov, mapping.item_id
+            # no subsonic mapping has been found in library item, ignore...
+            return None, item_id
+        elif provider_instance_id_or_domain.startswith("opensubsonic"):
+            # found a subsonic mapping, proceed...
+            prov = self.mass.get_provider(provider_instance_id_or_domain)
+            assert isinstance(prov, OpenSonicProvider)
+            return prov, item_id
+        # not an item from subsonic provider, ignore...
+        return None, item_id
+
+    async def _update_now_playing(self, report: MediaItemPlaybackProgressReport) -> None:
+        def handler(prov: OpenSonicProvider, item_id: str, uri: str) -> None:
+            try:
+                self.logger.info("scrobble play now event")
+                prov._conn.scrobble(item_id, submission=False)
+                self.logger.debug("track %s marked as 'now playing'", uri)
+                self.currently_playing = uri
+            except Exception as err:
+                self.logger.exception(err)
+
+        media_type, provider_instance_id_or_domain, item_id = await parse_uri(report.uri)
+        if not self._is_scrobblable_media_type(media_type):
+            return
+        prov, item_id = await self._get_subsonic_provider_and_item_id(
+            media_type, provider_instance_id_or_domain, item_id
+        )
+        if not prov:
+            return
+
+        # the opensubsonic library is not async friendly,
+        # so we need to run it in a executor thread
+        await asyncio.to_thread(handler, prov, item_id, report.uri)
+
+    async def _scrobble(self, report: MediaItemPlaybackProgressReport) -> None:
+        def handler(prov: OpenSonicProvider, item_id: str, uri: str) -> None:
+            try:
+                prov._conn.scrobble(item_id, submission=True, listen_time=int(time.time()))
+                self.logger.debug("track %s marked as 'played'", uri)
+                self.last_scrobbled = uri
+            except Exception as err:
+                self.logger.exception(err)
+
+        media_type, provider_instance_id_or_domain, item_id = await parse_uri(report.uri)
+        if not self._is_scrobblable_media_type(media_type):
+            return
+        prov, item_id = await self._get_subsonic_provider_and_item_id(
+            media_type, provider_instance_id_or_domain, item_id
+        )
+        if not prov:
+            return
+
+        # the opensubsonic library is not async friendly,
+        # so we need to run it in a executor thread
+        await asyncio.to_thread(handler, prov, item_id, report.uri)
+
+
+async def get_config_entries(
+    mass: MusicAssistant,
+    instance_id: str | None = None,
+    action: str | None = None,
+    values: dict[str, ConfigValueType] | None = None,
+) -> tuple[ConfigEntry, ...]:
+    """Return Config entries to setup this provider."""
+    # ruff: noqa: ARG001
+    return ()
diff --git a/music_assistant/providers/subsonic_scrobble/icon.svg b/music_assistant/providers/subsonic_scrobble/icon.svg
new file mode 100644 (file)
index 0000000..429336a
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg preserveAspectRatio="xMidYMid meet" version="1.0" viewBox="0 0 26.986579 20.7681" id="svg73"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+   <defs id="defs77" />
+   <metadata id="metadata67">Created by potrace 1.16, written by Peter Selinger 2001-2019</metadata>
+   <g fill="#000000" stroke="none" id="g71" transform="matrix(0.1,0,0,0.1,-1.5234206,-4.6112125)">
+      <path d="m 830,2529 c -13,-6 -28,-15 -32,-22 -4,-7 -5,-123 -1,-258 l 6,-246 -27,-24 c -28,-24 -28,-24 -24,-139 l 3,-115 -90,-44 -90,-43 -45,18 c -25,9 -68,19 -96,22 l -51,4 -1,-84 c 0,-69 -6,-101 -37,-188 -20,-58 -40,-120 -45,-139 -11,-42 -14,-45 -77,-65 -56,-19 -82,-41 -66,-57 5,-5 34,-22 63,-37 49,-26 55,-32 66,-76 17,-65 39,-108 74,-146 16,-18 30,-34 30,-37 0,-2 -20,-29 -45,-59 -82,-102 -84,-173 -4,-194 65,-18 169,6 251,57 46,28 44,28 131,-1 301,-100 636,-168 931,-188 348,-24 635,25 844,144 170,97 316,295 338,457 l 7,54 -89,-5 c -56,-3 -129,-17 -199,-36 -228,-65 -330,-83 -520,-91 -185,-8 -373,1 -403,19 -12,6 31,10 138,10 278,1 487,34 720,115 121,42 209,57 288,50 l 73,-7 -6,29 c -30,132 -58,183 -159,291 -63,66 -163,142 -253,192 -25,13 -43,32 -45,44 -4,30 -64,26 -388,-25 -128,-21 -179,-23 -440,-23 -424,0 -451,13 -74,35 311,18 442,36 557,79 l 58,22 -3,66 -3,66 -50,12 c -245,59 -240,58 -335,40 -130,-26 -230,-33 -344,-26 -88,6 -157,23 -144,36 3,2 94,8 204,14 109,5 221,16 247,23 l 48,12 -23,21 c -63,58 -422,78 -581,33 -21,-5 -51,-25 -67,-42 -16,-18 -31,-29 -35,-26 -3,4 -12,80 -20,170 -28,317 -24,297 -58,309 -36,12 -74,12 -107,-1 z M 523,1216 c 23,-59 18,-198 -8,-232 -13,-18 -15,-18 -37,4 -21,20 -23,32 -23,116 0,77 3,98 18,114 20,21 42,20 50,-2 z m 329,-13 c 30,-35 39,-65 39,-128 0,-100 -43,-171 -89,-146 -30,16 -41,35 -52,89 -23,124 46,249 102,185 z m 455,-3 c 114,-69 98,-343 -22,-366 -48,-9 -81,15 -111,79 -51,111 -12,268 76,300 23,8 23,8 57,-13 z" transform="matrix(0.1,0,0,-0.1,0,300)" id="path69" />
+   </g>
+   <path d="m 15.774389,20.742438 c -0.0486,-0.003 -0.23661,-0.0124 -0.41779,-0.0219 -1.98864,-0.10441 -4.37252,-0.47627 -6.6443396,-1.03643 -1.05621,-0.26043 -1.8816,-0.4979 -3.01202,-0.86657 -0.28567,-0.0932 -0.57681,-0.18103 -0.64698,-0.19525 -0.21922,-0.0444 -0.33365,-0.0113 -0.67148,0.19436 -0.44499,0.27088 -0.92696,0.46072 -1.43447,0.565 -0.19236,0.0395 -0.24539,0.0437 -0.55803,0.0438 -0.30639,1.5e-4 -0.36002,-0.004 -0.47403,-0.0351 -0.2776,-0.0762 -0.46947,-0.21041 -0.55171,-0.38585 -0.0386,-0.0824 -0.0428,-0.10955 -0.0428,-0.27615 0,-0.15023 0.007,-0.20582 0.0373,-0.29727 0.10221,-0.30844 0.29913,-0.63087 0.66245,-1.08462 0.11676,-0.14583 0.24751,-0.31507 0.29056,-0.3761 0.0782,-0.11088 0.0782,-0.11101 0.0502,-0.15523 -0.0155,-0.0243 -0.11127,-0.13976 -0.2129,-0.25647 -0.10164,-0.11671 -0.22331,-0.26378 -0.2704,-0.32682 -0.21167,-0.28341 -0.38021,-0.65818 -0.51809,-1.15205 -0.0455,-0.16295 -0.10495,-0.33845 -0.13213,-0.39002 -0.067,-0.12721 -0.17924,-0.22213 -0.42094001,-0.35617 -0.74619,-0.4138 -0.79556,-0.44676 -0.79556,-0.53116 0,-0.15388 0.25915,-0.31544 0.80603,-0.5025 0.32205001,-0.11016 0.41634001,-0.15412 0.49399001,-0.23029 0.0727,-0.0713 0.0976,-0.13135 0.20285,-0.48945 0.049,-0.16685 0.21149,-0.66853 0.36098,-1.11484 0.14949,-0.4463 0.28977,-0.88377 0.31174,-0.97214 0.0957,-0.38495 0.12159,-0.6881505 0.12197,-1.4266405 l 2.4e-4,-0.49466 0.0763,0.008 c 0.042,0.005 0.17033,0.0161 0.28522,0.0253 0.45078,0.0363 0.86347,0.1339 1.29729,0.3068 0.13022,0.0519 0.24674,0.0944 0.25894,0.0944 0.0248,0 0.59115,-0.26825 1.32572,-0.62798 l 0.48775,-0.23886 -0.01,-0.21334 c -0.0216,-0.47754 -0.0458,-1.59604 -0.0388,-1.79036 0.0108,-0.29854 0.0271,-0.3294 0.30728,-0.58151 l 0.2188,-0.19687 -0.008,-0.20419 c -0.01,-0.24922 -0.0662,-2.6984 -0.0791,-3.44201 -0.0119,-0.68278 0.01,-1.34978999 0.0458,-1.40470999 0.0366,-0.0558 0.24448,-0.18018 0.37593,-0.22489 0.30102,-0.10238 0.62538,-0.0976 0.99309,0.0145 0.22209,0.0677 0.25911,0.1237 0.31908,0.48224 0.0439,0.26247 0.0646,0.47201999 0.24263,2.45045999 0.0987,1.09677 0.18328,1.82051 0.21675,1.85422 0.0302,0.0303 0.126,-0.0354 0.31492,-0.21612 0.37764,-0.36118 0.59736,-0.46509 1.2035596,-0.56917 0.4912,-0.0844 0.92157,-0.11905 1.63095,-0.13154 1.60112,-0.0282 3.12332,0.1867 3.59099,0.50693 0.13462,0.0922 0.30763,0.2562 0.28057,0.26599 -0.0127,0.005 -0.14244,0.0377 -0.28827,0.0735 -0.478,0.11743 -1.21572,0.1923 -2.69148,0.27315 -1.08743,0.0596 -1.94049,0.11913 -1.97031,0.13756 -0.0278,0.0172 -0.0134,0.0694 0.0282,0.10207 0.0559,0.044 0.25224,0.10491 0.49346,0.15319 0.44017,0.0881 0.81776,0.12142 1.49691,0.13215 1.06497,0.0168 1.71485,-0.0502 3.09319,-0.31892 0.24372,-0.0475 0.29045,-0.0517 0.57847,-0.0513 0.36866,4.5e-4 0.5135,0.0225 1.25074,0.19069 0.56954,0.12993 1.76773,0.41789 1.77677,0.427 0.005,0.006 0.0474,0.84742 0.0604,1.21084 l 0.003,0.0954 -0.37666,0.14234 c -1.35533,0.5122 -2.36506,0.65962 -6.0328,0.8808 -1.26159,0.0761 -2.2227,0.16127 -2.34533,0.20789 -0.25973,0.0988 0.63341,0.13407 3.39783,0.13437 2.86908,3e-4 2.92927,-0.003 4.76432,-0.28638 1.75196,-0.27025 2.34828,-0.34766 2.86415,-0.3718 0.34046,-0.0159 0.53034,0.0246 0.57662,0.12316 0.0102,0.0216 0.033,0.0719 0.0507,0.11167 0.0699,0.15677 0.18476,0.24856 0.6542,0.52278 0.86218,0.50362 1.81319,1.25343 2.40035,1.8925305 0.53756,0.58511 0.81466,0.96149 1.03144,1.40098 0.20559,0.41678 0.36224,0.92156 0.50542,1.62863 l 0.0109,0.0539 -0.0619,-0.009 c -0.034,-0.005 -0.2246,-0.0248 -0.42344,-0.0435 -0.46224,-0.0436 -0.99229,-0.037 -1.34173,0.0166 -0.55118,0.0845 -0.98895,0.20022 -1.88002,0.49678 -1.42126,0.47302 -2.5305,0.73833 -3.80021,0.90895 -0.9804,0.13173 -1.74288,0.17976 -3.20734,0.20203 -1.20158,0.0183 -1.49233,0.0367 -1.4813,0.094 0.0159,0.0823 0.52051,0.16225 1.35441,0.21448 0.43289,0.0271 2.21905,0.0269 2.74772,-3.7e-4 1.84968,-0.0954 2.80122,-0.25549 4.7563,-0.80043 0.88324,-0.24619 1.39478,-0.35787 1.97643,-0.43151 0.19283,-0.0244 1.26342,-0.0919 1.27495,-0.0803 0.0122,0.0122 -0.0801,0.647 -0.11998,0.8257 -0.0895,0.40107 -0.19664,0.6917 -0.42401,1.15055 -0.31551,0.63675 -0.69981,1.19031 -1.21592,1.75148 -0.68633,0.74625 -1.44784,1.29474 -2.39935,1.72816 -1.41111,0.64278 -3.05174,1.00403 -5.02946,1.10742 -0.27395,0.0143 -1.68553,0.0187 -1.92822,0.006 z m -4.33851,-3.71729 c 0.45377,-0.15271 0.7664,-0.67598 0.86884,-1.45425 0.0314,-0.23844 0.0106,-0.75949 -0.0397,-0.99806 -0.12859,-0.60901 -0.37493,-0.99161 -0.8005,-1.24329 -0.21217,-0.12548 -0.25928,-0.13979 -0.37491,-0.11391 -0.11626,0.026 -0.31272,0.12581 -0.42935,0.21808 -0.32949,0.26069 -0.58529,0.79864 -0.65561,1.37876 -0.0273,0.22556 -0.0108,0.71513 0.0307,0.90664 0.0962,0.44435 0.31246,0.87966 0.55658,1.12021 0.10616,0.1046 0.2591,0.19259 0.37802,0.21747 0.11487,0.024 0.34744,0.008 0.46599,-0.0317 z m -4.5774596,-0.92019 c 0.22342,-0.11565 0.39532,-0.4376 0.49108,-0.9197 0.0375,-0.18907 0.0419,-0.93208 0.007,-1.10874 -0.0482,-0.24038 -0.1427,-0.45269 -0.2824,-0.63424 -0.14374,-0.1868 -0.25421,-0.25783 -0.40002,-0.25721 -0.30818,0.001 -0.59777,0.42986 -0.711,1.05214 -0.0404,0.22195 -0.0407,0.72625 -6.7e-4,0.93198 0.10145,0.52074 0.2242,0.75757 0.46609,0.89924 0.17966,0.10523 0.28106,0.11384 0.4304,0.0365 z m -3.29858,-0.46949 c 0.16836,-0.1521 0.25974,-0.49155 0.29098,-1.08087 0.0336,-0.63444 -0.0604,-1.28075 -0.20914,-1.43709 -0.0536,-0.0564 -0.0654,-0.0613 -0.1452,-0.0612 -0.14852,2.3e-4 -0.3154,0.12196 -0.38596,0.28147 -0.0701,0.15838 -0.0846,0.33076 -0.0853,1.0119 -7.6e-4,0.71065 0.009,0.82109 0.0875,0.98216 0.0648,0.13305 0.29235,0.35168 0.37323,0.35855 0.007,5.2e-4 0.0399,-0.0242 0.0739,-0.0549 z" id="path196" style="fill:#ffcc00;stroke-width:0.0160685" />
+</svg>
diff --git a/music_assistant/providers/subsonic_scrobble/icon_monochrome.svg b/music_assistant/providers/subsonic_scrobble/icon_monochrome.svg
new file mode 100644 (file)
index 0000000..487c6ab
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   preserveAspectRatio="xMidYMid"
+   version="1.0"
+   viewBox="0 0 511.99998 511.99998"
+   id="svg73"
+   sodipodi:docname="icon_monochrome.svg"
+   inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
+   width="512"
+   height="512"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview1"
+     pagecolor="#37ffff"
+     bordercolor="#000000"
+     borderopacity="0.25"
+     inkscape:showpageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:zoom="1.7324219"
+     inkscape:cx="256"
+     inkscape:cy="256"
+     inkscape:window-width="1920"
+     inkscape:window-height="1129"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg73" />
+  <defs
+     id="defs77" />
+  <metadata
+     id="metadata67">Created by potrace 1.16, written by Peter Selinger 2001-2019</metadata>
+  <path
+     d="m 299.5028,449.89869 c -0.92293,-0.057 -4.4933,-0.23548 -7.93396,-0.41589 -37.76485,-1.98278 -83.03544,-9.04451 -126.17796,-19.68211 -20.05774,-4.94564 -35.73213,-9.45527 -57.19914,-16.45642 -5.42496,-1.76989 -10.953793,-3.43781 -12.286342,-3.70785 -4.163052,-0.84317 -6.336111,-0.21459 -12.751602,3.69095 -8.450491,5.14409 -17.603243,8.74921 -27.241007,10.72952 -3.652972,0.75011 -4.660028,0.82987 -10.597153,0.83177 -5.818436,0.003 -6.836886,-0.076 -9.001969,-0.66656 -5.271706,-1.44706 -8.915373,-3.99575 -10.477135,-7.3274 -0.733025,-1.5648 -0.812784,-2.08039 -0.812784,-5.24417 0,-2.85291 0.132932,-3.90858 0.708337,-5.64525 1.940998,-5.85736 5.680567,-11.9804 12.58012,-20.59725 2.217307,-2.76935 4.700288,-5.98327 5.51782,-7.14225 1.485041,-2.10564 1.485041,-2.10811 0.953313,-2.94786 -0.29435,-0.46147 -2.11305,-2.65409 -4.043034,-4.87044 -1.930173,-2.21636 -4.240722,-5.00926 -5.134975,-6.20641 -4.019675,-5.38204 -7.2203,-12.49903 -9.838681,-21.87777 -0.864059,-3.09447 -1.993032,-6.42726 -2.509188,-7.40659 -1.272349,-2.41576 -3.40382,-4.21832 -7.993774,-6.76377 -14.1703673,-7.85819 -15.1079181,-8.48411 -15.1079181,-10.08689 0,-2.92223 4.9213346,-5.9903 15.3067461,-9.54262 6.115825,-2.09197 7.906419,-2.92679 9.381016,-4.37328 1.380594,-1.354 1.853452,-2.49437 3.852181,-9.2948 0.930524,-3.16853 4.016257,-12.69558 6.855116,-21.17113 2.838859,-8.47537 5.502817,-16.78306 5.920034,-18.46123 1.817371,-7.31031 2.30903,-13.06818 2.316246,-27.09232 l 0.0046,-9.39374 1.448959,0.15192 c 0.797593,0.0949 3.234617,0.30575 5.416412,0.48046 8.560444,0.68934 16.397549,2.5428 24.635918,5.82622 2.472916,0.9856 4.685665,1.79268 4.917346,1.79268 0.47096,0 11.226113,-5.09414 25.175808,-11.92552 l 9.26252,-4.53602 -0.1899,-4.05139 c -0.4102,-9.06863 -0.86976,-30.30927 -0.73683,-33.99946 0.2051,-5.66937 0.51464,-6.25541 5.83534,-11.04305 l 4.15508,-3.73862 -0.15193,-3.87763 c -0.1899,-4.73276 -1.25715,-51.24341 -1.50213,-65.364778 -0.22598,-12.966193 0.1899,-25.632908 0.86976,-26.675855 0.69504,-1.059658 4.64274,-3.421671 7.13902,-4.270727 5.71646,-1.944227 11.87615,-1.853453 18.85907,0.275359 4.21755,1.285643 4.92057,2.349099 6.05942,9.157879 0.83368,4.984383 1.22677,8.963798 4.60762,46.534952 1.87434,20.82799 3.48054,34.57202 4.11614,35.21218 0.57351,0.57541 2.39278,-0.67225 5.98043,-4.10418 7.17149,-6.85891 11.34404,-8.83219 22.85595,-10.8087 9.32803,-1.60278 17.50088,-2.2608 30.97222,-2.49799 30.40573,-0.53552 59.31276,3.54549 68.19395,9.62675 2.55647,1.75091 5.84198,4.86532 5.32811,5.05123 -0.24118,0.095 -2.70498,0.71594 -5.47434,1.39579 -9.07736,2.23003 -23.08688,3.65183 -51.11199,5.1872 -20.65062,1.13182 -36.85048,2.26231 -37.41677,2.6123 -0.52793,0.32663 -0.25447,1.31793 0.53553,1.93834 1.06156,0.83557 4.79011,1.99227 9.37095,2.90912 8.35896,1.67305 15.5295,2.30581 28.42676,2.50957 20.22409,0.31904 32.56551,-0.95331 58.74059,-6.05638 4.62831,-0.90204 5.51573,-0.9818 10.98531,-0.97421 7.00096,0.008 9.75152,0.42729 23.75192,3.62126 10.81573,2.46741 33.56971,7.93586 33.74139,8.10886 0.095,0.11394 0.90014,16.09275 1.14701,22.99421 l 0.057,1.81167 -7.15288,2.70308 c -25.73812,9.72683 -44.91319,12.52638 -114.56465,16.72665 -23.95796,1.44516 -42.20972,3.06256 -44.5385,3.94789 -4.93235,1.87624 12.02864,2.54603 64.52579,2.55173 54.48467,0.006 55.62769,-0.057 90.47583,-5.43844 33.27024,-5.13213 44.59453,-6.60217 54.39105,-7.06059 6.46544,-0.30195 10.07131,0.46716 10.95019,2.33884 0.1937,0.41019 0.62668,1.3654 0.9628,2.12065 1.32743,2.97711 3.50865,4.72022 12.42345,9.92774 16.37306,9.56389 34.43301,23.80301 45.58335,35.93971 10.20843,11.11141 15.47064,18.25898 19.58735,26.60502 3.90422,7.91478 6.87905,17.5007 9.59808,30.92817 l 0.20699,1.02357 -1.1755,-0.17091 c -0.64567,-0.095 -4.26522,-0.47096 -8.04125,-0.82608 -8.77807,-0.82797 -18.84388,-0.70264 -25.47984,0.31524 -10.46707,1.60468 -18.78045,3.80224 -35.70214,9.434 -26.99014,8.98279 -48.05493,14.0211 -72.1671,17.26123 -18.61808,2.50159 -33.0978,3.41369 -60.90833,3.83661 -22.81836,0.34752 -28.33978,0.69694 -28.13032,1.78508 0.30195,1.5629 9.88464,3.08118 25.72064,4.07304 8.22071,0.51464 42.14041,0.51084 52.18001,-0.007 35.12597,-1.81168 53.19599,-4.85183 90.32354,-15.20041 16.77298,-4.67522 26.48728,-6.79605 37.53298,-8.1945 3.6619,-0.46336 23.99272,-1.7452 24.21168,-1.52492 0.23168,0.23168 -1.52112,12.28672 -2.27846,15.68029 -1.69963,7.61644 -3.73425,13.13558 -8.05207,21.84928 -5.99163,12.09207 -13.2896,22.60434 -23.09068,33.26112 -13.03361,14.17151 -27.49491,24.58749 -45.56436,32.81827 -26.79739,12.20658 -57.95344,19.06682 -95.51092,21.03023 -5.20239,0.27156 -32.00871,0.35512 -36.61747,0.11394 z m -82.38958,-70.59243 c 8.61722,-2.90001 14.55416,-12.83706 16.49953,-27.61663 0.59629,-4.52805 0.20129,-14.42294 -0.75392,-18.95346 -2.44196,-11.56528 -7.12003,-18.83096 -15.20173,-23.61044 -4.02917,-2.3829 -4.9238,-2.65465 -7.11965,-2.16318 -2.20781,0.49374 -5.93864,2.38917 -8.15348,4.1414 -6.25711,4.95058 -11.11483,15.16641 -12.45023,26.18306 -0.51843,4.28345 -0.20509,13.58053 0.583,17.21736 1.82687,8.43833 5.93371,16.705 10.56962,21.27311 2.01601,1.98639 4.92039,3.65734 7.17871,4.12982 2.18142,0.45577 6.59799,0.15193 8.84929,-0.60199 z m -86.9273,-17.47468 c 4.24281,-2.19623 7.50724,-8.31015 9.32575,-17.46537 0.71214,-3.5905 0.79569,-17.70047 0.13293,-21.0553 -0.91533,-4.56489 -2.70991,-8.59672 -5.36286,-12.0444 -2.72966,-3.54739 -4.82752,-4.89627 -7.59649,-4.8845 -5.85243,0.019 -11.35183,8.16317 -13.5021,19.98045 -0.76721,4.2149 -0.77291,13.7917 -0.0127,17.69857 1.92657,9.88901 4.25763,14.38648 8.85119,17.07684 3.4118,1.99834 5.33741,2.16185 8.17342,0.69314 z m -62.641006,-8.91575 c 3.197206,-2.88843 4.932539,-9.33468 5.525796,-20.52604 0.638074,-12.0482 -1.147014,-24.32182 -3.97163,-27.29076 -1.01788,-1.07105 -1.241966,-1.16411 -2.757391,-1.16221 -2.820438,0.004 -5.989539,2.31606 -7.329494,5.3452 -1.331219,3.00768 -1.606579,6.28123 -1.619872,19.21628 -0.01443,13.49545 0.170913,15.59274 1.661651,18.65151 1.230571,2.52665 5.551812,6.6785 7.087747,6.80897 0.132932,0.01 0.757713,-0.45957 1.403383,-1.04257 z"
+     id="path196"
+     style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.305146;stroke-opacity:1" />
+</svg>
diff --git a/music_assistant/providers/subsonic_scrobble/manifest.json b/music_assistant/providers/subsonic_scrobble/manifest.json
new file mode 100644 (file)
index 0000000..702a919
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "type": "plugin",
+  "domain": "subsonic_scrobble",
+  "name": "Subsonic Scrobbler",
+  "description": "Report your music playback back to Subsonic server",
+  "codeowners": ["@clusters"],
+  "documentation": "",
+  "multi_instance": false,
+  "builtin": false,
+  "depends_on": "opensubsonic",
+  "requirements": []
+}