From 4954f6de5436541242341fd20842d9f023068f70 Mon Sep 17 00:00:00 2001 From: jojo141185 Date: Sat, 6 Sep 2025 10:10:03 +0000 Subject: [PATCH] fix: Resolve Mopidy v4 ModuleNotFoundError on startup The Mopidy v4.0 development versions have removed the `mopidy.models.serialize` module, which causes a `ModuleNotFoundError` in Iris on startup. This commit introduces a check to detect the Mopidy version. It conditionally uses the new Pydantic `.model_dump()` method for serialization on v4 while falling back to the legacy `ModelJSONEncoder` for older versions. This makes Iris compatible with both Mopidy v3 and the new v4 pre-releases. --- mopidy_iris/core.py | 49 ++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/mopidy_iris/core.py b/mopidy_iris/core.py index 9e39e256..83183a8f 100755 --- a/mopidy_iris/core.py +++ b/mopidy_iris/core.py @@ -13,11 +13,20 @@ import pickle from pkg_resources import parse_version from tornado.escape import json_encode from tornado.httpclient import AsyncHTTPClient, HTTPRequest -from mopidy.models.serialize import ModelJSONEncoder from . import Extension from .system import IrisSystemThread +# Check for Mopidy v4+ compatibility. +# We try to import the old serialization class. +# If it fails, we assume we're running on Mopidy v4+. +try: + from mopidy.models.serialize import ModelJSONEncoder + MOPIDY_V4 = False +except ImportError: + ModelJSONEncoder = None + MOPIDY_V4 = True + if sys.platform == "win32": import ctypes @@ -1165,19 +1174,31 @@ class IrisCore(pykka.ThreadingActor): meta = {} if track: - # Convert the Track to JSON, but to make it a response-ready JSON we need to load it. - # Required because ModelJSONEncoder produces single-quote JSON, and we need standard - # double-quoted json. - track = json.loads(json.dumps(track, cls=ModelJSONEncoder)) - images = self.core.library.get_images([track["uri"]]).get() - if images: - meta["images"] = json.loads( - json.dumps(images[track["uri"]], cls=ModelJSONEncoder) - ) - meta["name"] = track["name"] - meta["uri"] = track["uri"] - meta["artists"] = track["artists"] - meta["album"] = track["album"] + # Convert the Track to JSON, + # We use different serialization methods depending on the Mopidy version + if MOPIDY_V4: + # Mopidy v4+ uses Pydantic models with .model_dump() + track_data = track.model_dump() + images_result = self.core.library.get_images([track_data["uri"]]).get() + if images_result and track_data["uri"] in images_result: + images = images_result[track_data["uri"]] + # Images in Mopidy v4 are a list of Image objects + meta["images"] = [img.model_dump() for img in images] + else: + # Older versions use the legacy ModelJSONEncoder: + # Convert the Track to JSON, but to make it a response-ready JSON we need to load it. + # Required because ModelJSONEncoder produces single-quote JSON, and we need standard + # double-quoted json. + track_data = json.loads(json.dumps(track, cls=ModelJSONEncoder)) + images_result = self.core.library.get_images([track_data["uri"]]).get() + if images_result and track_data["uri"] in images_result: + images = images_result[track_data["uri"]] + meta["images"] = json.loads(json.dumps(images, cls=ModelJSONEncoder)) + + meta["name"] = track_data["name"] + meta["uri"] = track_data["uri"] + meta["artists"] = track_data["artists"] + meta["album"] = track_data["album"] url = "http" if self.config["iris"]["snapcast_ssl"]: