1
0
mirror of https://github.com/natekspencer/hacs-oasis_mini.git synced 2025-11-15 16:43:52 -05:00

1 Commits

Author SHA1 Message Date
Nathan Spencer
82a6a3cc1d Add temp fix for firmware 2.02 led issue 2025-03-13 04:32:44 +00:00
15 changed files with 2698 additions and 12876 deletions

View File

@@ -1,7 +1,7 @@
name: Update tracks name: Update tracks
on: on:
schedule: schedule:
- cron: "0 19 * * 1" - cron: "0 19 * * *"
permissions: permissions:
contents: write contents: write
pull-requests: write pull-requests: write

View File

@@ -3,14 +3,12 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
import homeassistant.helpers.device_registry as dr import homeassistant.helpers.device_registry as dr
import homeassistant.helpers.entity_registry as er
from .const import DOMAIN from .const import DOMAIN
from .coordinator import OasisMiniCoordinator from .coordinator import OasisMiniCoordinator
@@ -91,33 +89,3 @@ async def async_remove_entry(hass: HomeAssistant, entry: OasisMiniConfigEntry) -
async def update_listener(hass: HomeAssistant, entry: OasisMiniConfigEntry) -> None: async def update_listener(hass: HomeAssistant, entry: OasisMiniConfigEntry) -> None:
"""Handle options update.""" """Handle options update."""
await hass.config_entries.async_reload(entry.entry_id) await hass.config_entries.async_reload(entry.entry_id)
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Migrate old entry."""
_LOGGER.debug(
"Migrating configuration from version %s.%s", entry.version, entry.minor_version
)
if entry.version == 1 and entry.minor_version == 1:
# Need to update previous playlist select entity to queue
@callback
def migrate_unique_id(entity_entry: er.RegistryEntry) -> dict[str, Any] | None:
"""Migrate the playlist unique ID to queue."""
if entity_entry.domain == "select" and entity_entry.unique_id.endswith(
"-playlist"
):
unique_id = entity_entry.unique_id.replace("-playlist", "-queue")
return {"new_unique_id": unique_id}
return None
await er.async_migrate_entries(hass, entry.entry_id, migrate_unique_id)
hass.config_entries.async_update_entry(entry, minor_version=2, version=1)
_LOGGER.debug(
"Migration to configuration version %s.%s successful",
entry.version,
entry.minor_version,
)
return True

View File

@@ -61,11 +61,6 @@ DESCRIPTORS = (
translation_key="random_track", translation_key="random_track",
press_fn=play_random_track, press_fn=play_random_track,
), ),
OasisMiniButtonEntityDescription(
key="sleep",
translation_key="sleep",
press_fn=lambda device: device.async_sleep(),
),
) )

View File

@@ -62,7 +62,6 @@ class OasisMiniConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Oasis Mini.""" """Handle a config flow for Oasis Mini."""
VERSION = 1 VERSION = 1
MINOR_VERSION = 2
@staticmethod @staticmethod
@callback @callback

View File

@@ -50,7 +50,6 @@ class OasisMiniCoordinator(DataUpdateCoordinator[str]):
self.attempt = 0 self.attempt = 0
await self.device.async_get_current_track_details() await self.device.async_get_current_track_details()
await self.device.async_get_playlist_details() await self.device.async_get_playlist_details()
await self.device.async_cloud_get_playlists()
except Exception as ex: # pylint:disable=broad-except except Exception as ex: # pylint:disable=broad-except
if self.attempt > 2 or not (data or self.data): if self.attempt > 2 or not (data or self.data):
raise UpdateFailed( raise UpdateFailed(

View File

@@ -24,14 +24,12 @@
"status": { "status": {
"state": { "state": {
"booting": "mdi:loading", "booting": "mdi:loading",
"busy": "mdi:progress-clock",
"centering": "mdi:record-circle-outline", "centering": "mdi:record-circle-outline",
"downloading": "mdi:progress-download", "downloading": "mdi:progress-download",
"error": "mdi:alert-circle-outline", "error": "mdi:alert-circle-outline",
"live": "mdi:pencil-circle-outline", "live": "mdi:pencil-circle-outline",
"paused": "mdi:motion-pause-outline", "paused": "mdi:motion-pause-outline",
"playing": "mdi:motion-play-outline", "playing": "mdi:motion-play-outline",
"sleeping": "mdi:power-sleep",
"stopped": "mdi:stop-circle-outline", "stopped": "mdi:stop-circle-outline",
"updating": "mdi:update" "updating": "mdi:update"
} }

View File

@@ -49,8 +49,8 @@ class OasisMiniMediaPlayerEntity(OasisMiniEntity, MediaPlayerEntity):
@property @property
def media_duration(self) -> int | None: def media_duration(self) -> int | None:
"""Duration of current playing media in seconds.""" """Duration of current playing media in seconds."""
if (track := self.device.track) and "reduced_svg_content_new" in track: if (track := self.device.track) and "reduced_svg_content" in track:
return track["reduced_svg_content_new"] return track["reduced_svg_content"].get("1")
return None return None
@property @property

View File

@@ -3,7 +3,6 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from datetime import datetime, timedelta
import logging import logging
from typing import Any, Awaitable, Final from typing import Any, Awaitable, Final
from urllib.parse import urljoin from urllib.parse import urljoin
@@ -11,53 +10,28 @@ from urllib.parse import urljoin
from aiohttp import ClientResponseError, ClientSession from aiohttp import ClientResponseError, ClientSession
from .const import TRACKS from .const import TRACKS
from .utils import _bit_to_bool, _parse_int, decrypt_svg_content, now from .utils import _bit_to_bool, _parse_int, decrypt_svg_content
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
STATUS_CODE_MAP = { STATUS_CODE_MAP = {
0: "booting", 0: "booting", # maybe?
2: "stopped", 2: "stopped",
3: "centering", 3: "centering",
4: "playing", 4: "playing",
5: "paused", 5: "paused",
6: "sleeping",
9: "error", 9: "error",
11: "updating", 11: "updating",
13: "downloading", 13: "downloading",
14: "busy",
15: "live", 15: "live",
} }
ERROR_CODE_MAP = {
0: "None",
1: "Error has occurred while reading the flash memory",
2: "Error while starting the Wifi",
3: "Error when starting DNS settings for your machine",
4: "Failed to open the file to write",
5: "Not enough memory to perform the upgrade",
6: "Error while trying to upgrade your system",
7: "Error while trying to download the new version of the software",
8: "Error while reading the upgrading file",
9: "Failed to start downloading the upgrade file",
10: "Error while starting downloading the job file",
11: "Error while opening the file folder",
12: "Failed to delete a file",
13: "Error while opening the job file",
14: "You have wrong power adapter",
15: "Failed to update the device IP on Oasis Server",
16: "Your device failed centering itself",
17: "There appears to be an issue with your Oasis Device",
18: "Error while downloading the job file",
}
AUTOPLAY_MAP = { AUTOPLAY_MAP = {
"0": "on", "0": "on",
"1": "off", "1": "off",
"2": "5 minutes", "2": "5 minutes",
"3": "10 minutes", "3": "10 minutes",
"4": "30 minutes", "4": "30 minutes",
"5": "24 hours",
} }
LED_EFFECTS: Final[dict[str, str]] = { LED_EFFECTS: Final[dict[str, str]] = {
@@ -76,33 +50,6 @@ LED_EFFECTS: Final[dict[str, str]] = {
"12": "Follow Rainbow", "12": "Follow Rainbow",
"13": "Chasing Comet", "13": "Chasing Comet",
"14": "Gradient Follow", "14": "Gradient Follow",
"15": "Cumulative Fill",
"16": "Multi Comets A",
"17": "Rainbow Chaser",
"18": "Twinkle Lights",
"19": "Tennis Game",
"20": "Breathing Exercise 4-7-8",
"21": "Cylon Scanner",
"22": "Palette Mode",
"23": "Aurora Flow",
"24": "Colorful Drops",
"25": "Color Snake",
"26": "Flickering Candles",
"27": "Digital Rain",
"28": "Center Explosion",
"29": "Rainbow Plasma",
"30": "Comet Race",
"31": "Color Waves",
"32": "Meteor Storm",
"33": "Firefly Flicker",
"34": "Ripple",
"35": "Jelly Bean",
"36": "Forest Rain",
"37": "Multi Comets",
"38": "Multi Comets with Background",
"39": "Rainbow Fill",
"40": "White Red Comet",
"41": "Color Comets",
} }
CLOUD_BASE_URL = "https://app.grounded.so" CLOUD_BASE_URL = "https://app.grounded.so"
@@ -112,8 +59,6 @@ BALL_SPEED_MIN: Final = 100
LED_SPEED_MAX: Final = 90 LED_SPEED_MAX: Final = 90
LED_SPEED_MIN: Final = -90 LED_SPEED_MIN: Final = -90
PLAYLISTS_REFRESH_LIMITER = timedelta(minutes=5)
class OasisMini: class OasisMini:
"""Oasis Mini API client class.""" """Oasis Mini API client class."""
@@ -141,9 +86,6 @@ class OasisMini:
repeat_playlist: bool repeat_playlist: bool
status_code: int status_code: int
playlists: list[dict[str, Any]] = []
_playlists_next_refresh: datetime = now()
def __init__( def __init__(
self, self,
host: str, host: str,
@@ -172,17 +114,10 @@ class OasisMini:
return None return None
svg_content = decrypt_svg_content(svg_content) svg_content = decrypt_svg_content(svg_content)
paths = svg_content.split("L") paths = svg_content.split("L")
total = self.track.get("reduced_svg_content_new", 0) or len(paths) total = self.track.get("reduced_svg_content", {}).get("1", len(paths))
percent = (100 * self.progress) / total percent = (100 * self.progress) / total
return percent return percent
@property
def error_message(self) -> str | None:
"""Return the error message, if any."""
if self.status_code == 9:
return ERROR_CODE_MAP.get(self.error, f"Unknown ({self.error})")
return None
@property @property
def serial_number(self) -> str | None: def serial_number(self) -> str | None:
"""Return the serial number.""" """Return the serial number."""
@@ -278,7 +213,7 @@ class OasisMini:
shift = len(values) - 18 if len(values) > 17 else 0 shift = len(values) - 18 if len(values) > 17 else 0
status = { status = {
"status_code": _parse_int(values[0]), # see status code map "status_code": _parse_int(values[0]), # see status code map
"error": _parse_int(values[1]), "error": _parse_int(values[1]), # noqa: E501; error, 0 = none, and 10 = ?, 18 = can't download?
"ball_speed": _parse_int(values[2]), # 200 - 1000 "ball_speed": _parse_int(values[2]), # 200 - 1000
"playlist": playlist, "playlist": playlist,
"playlist_index": min(_parse_int(values[4]), len(playlist)), # noqa: E501; index of above "playlist_index": min(_parse_int(values[4]), len(playlist)), # noqa: E501; index of above
@@ -293,7 +228,7 @@ class OasisMini:
"max_brightness": _parse_int(values[13 + shift]), "max_brightness": _parse_int(values[13 + shift]),
"wifi_connected": _bit_to_bool(values[14 + shift]), "wifi_connected": _bit_to_bool(values[14 + shift]),
"repeat_playlist": _bit_to_bool(values[15 + shift]), "repeat_playlist": _bit_to_bool(values[15 + shift]),
"autoplay": AUTOPLAY_MAP.get(value := values[16 + shift], value), "autoplay": AUTOPLAY_MAP.get(values[16 + shift]),
"autoclean": _bit_to_bool(values[17 + shift]) "autoclean": _bit_to_bool(values[17 + shift])
if len(values) > 17 if len(values) > 17
else False, else False,
@@ -395,10 +330,6 @@ class OasisMini:
"""Set repeat playlist.""" """Set repeat playlist."""
await self._async_command(params={"WRIREPEATJOB": 1 if repeat else 0}) await self._async_command(params={"WRIREPEATJOB": 1 if repeat else 0})
async def async_sleep(self) -> None:
"""Send sleep command."""
await self._async_command(params={"CMDSLEEP": ""})
async def async_stop(self) -> None: async def async_stop(self) -> None:
"""Send stop command.""" """Send stop command."""
await self._async_command(params={"CMDSTOP": ""}) await self._async_command(params={"CMDSTOP": ""})
@@ -420,18 +351,6 @@ class OasisMini:
"""Login via the cloud.""" """Login via the cloud."""
await self._async_cloud_request("GET", "api/auth/logout") await self._async_cloud_request("GET", "api/auth/logout")
async def async_cloud_get_playlists(
self, personal_only: bool = False
) -> list[dict[str, Any]]:
"""Get playlists from the cloud."""
if self._playlists_next_refresh <= now():
if playlists := await self._async_cloud_request(
"GET", "api/playlist", params={"my_playlists": str(personal_only)}
):
self.playlists = playlists
self._playlists_next_refresh = now() + PLAYLISTS_REFRESH_LIMITER
return self.playlists
async def async_cloud_get_track_info(self, track_id: int) -> dict[str, Any] | None: async def async_cloud_get_track_info(self, track_id: int) -> dict[str, Any] | None:
"""Get cloud track info.""" """Get cloud track info."""
try: try:
@@ -451,7 +370,7 @@ class OasisMini:
"GET", "api/track", params={"ids[]": tracks or []} "GET", "api/track", params={"ids[]": tracks or []}
) )
if not response: if not response:
return [] return None
track_details = response.get("data", []) track_details = response.get("data", [])
while next_page_url := response.get("next_page_url"): while next_page_url := response.get("next_page_url"):
response = await self._async_cloud_request("GET", next_page_url) response = await self._async_cloud_request("GET", next_page_url)

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@
from __future__ import annotations from __future__ import annotations
import base64 import base64
from datetime import UTC, datetime
import logging import logging
import math import math
from xml.etree.ElementTree import Element, SubElement, tostring from xml.etree.ElementTree import Element, SubElement, tostring
@@ -42,7 +41,7 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
if progress is not None: if progress is not None:
svg_content = decrypt_svg_content(svg_content) svg_content = decrypt_svg_content(svg_content)
paths = svg_content.split("L") paths = svg_content.split("L")
total = track.get("reduced_svg_content_new", 0) or len(paths) total = track.get("reduced_svg_content", {}).get(model_id, len(paths))
percent = min((100 * progress) / total, 100) percent = min((100 * progress) / total, 100)
progress = math.floor((percent / 100) * (len(paths) - 1)) progress = math.floor((percent / 100) * (len(paths) - 1))
@@ -178,7 +177,3 @@ def decrypt_svg_content(svg_content: dict[str, str]):
svg_content["decrypted"] = decrypted svg_content["decrypted"] = decrypted
return decrypted return decrypted
def now() -> datetime:
return datetime.now(UTC)

View File

@@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Awaitable, Callable from typing import Any, Awaitable, Callable
@@ -64,33 +63,12 @@ class OasisMiniSelectEntity(OasisMiniEntity, SelectEntity):
return super()._handle_coordinator_update() return super()._handle_coordinator_update()
def playlists_update_handler(entity: OasisMiniSelectEntity) -> None: def playlist_update_handler(entity: OasisMiniSelectEntity) -> None:
"""Handle playlists updates.""" """Handle playlist updates."""
# pylint: disable=protected-access # pylint: disable=protected-access
device = entity.device device = entity.device
counts = defaultdict(int) options = [
options = [] device._playlist.get(track, {}).get(
current_option: str | None = None
for playlist in device.playlists:
name = playlist["name"]
counts[name] += 1
if counts[name] > 1:
name = f"{name} ({counts[name]})"
options.append(name)
if device.playlist == [pattern["id"] for pattern in playlist["patterns"]]:
current_option = name
entity._attr_options = options
entity._attr_current_option = current_option
def queue_update_handler(entity: OasisMiniSelectEntity) -> None:
"""Handle queue updates."""
# pylint: disable=protected-access
device = entity.device
counts = defaultdict(int)
options = []
for track in device.playlist:
name = device._playlist.get(track, {}).get(
"name", "name",
TRACKS.get(track, {"id": track, "name": f"Unknown Title (#{track})"}).get( TRACKS.get(track, {"id": track, "name": f"Unknown Title (#{track})"}).get(
"name", "name",
@@ -99,10 +77,8 @@ def queue_update_handler(entity: OasisMiniSelectEntity) -> None:
else str(track), else str(track),
), ),
) )
counts[name] += 1 for track in device.playlist
if counts[name] > 1: ]
name = f"{name} ({counts[name]})"
options.append(name)
entity._attr_options = options entity._attr_options = options
index = min(device.playlist_index, len(options) - 1) index = min(device.playlist_index, len(options) - 1)
entity._attr_current_option = options[index] if options else None entity._attr_current_option = options[index] if options else None
@@ -117,22 +93,11 @@ DESCRIPTORS = (
select_fn=lambda device, option: device.async_set_autoplay(option), select_fn=lambda device, option: device.async_set_autoplay(option),
), ),
OasisMiniSelectEntityDescription( OasisMiniSelectEntityDescription(
key="queue", key="playlist",
translation_key="queue", translation_key="playlist",
current_value=lambda device: (device.playlist.copy(), device.playlist_index), current_value=lambda device: (device.playlist.copy(), device.playlist_index),
select_fn=lambda device, option: device.async_change_track(option), select_fn=lambda device, option: device.async_change_track(option),
update_handler=queue_update_handler, update_handler=playlist_update_handler,
),
)
CLOUD_DESCRIPTORS = (
OasisMiniSelectEntityDescription(
key="playlists",
translation_key="playlist",
current_value=lambda device: (device.playlists, device.playlist.copy()),
select_fn=lambda device, option: device.async_set_playlist(
[pattern["id"] for pattern in device.playlists[option]["patterns"]]
),
update_handler=playlists_update_handler,
), ),
) )
@@ -143,13 +108,9 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up Oasis Mini select using config entry.""" """Set up Oasis Mini select using config entry."""
coordinator: OasisMiniCoordinator = entry.runtime_data async_add_entities(
entities = [ [
OasisMiniSelectEntity(coordinator, descriptor) for descriptor in DESCRIPTORS OasisMiniSelectEntity(entry.runtime_data, descriptor)
] for descriptor in DESCRIPTORS
if coordinator.device.access_token: ]
entities.extend( )
OasisMiniSelectEntity(coordinator, descriptor)
for descriptor in CLOUD_DESCRIPTORS
)
async_add_entities(entities)

View File

@@ -28,8 +28,10 @@ async def async_setup_entry(
] ]
if coordinator.device.access_token: if coordinator.device.access_token:
entities.extend( entities.extend(
OasisMiniSensorEntity(coordinator, descriptor) [
for descriptor in CLOUD_DESCRIPTORS OasisMiniSensorEntity(coordinator, descriptor)
for descriptor in CLOUD_DESCRIPTORS
]
) )
async_add_entities(entities) async_add_entities(entities)

View File

@@ -41,9 +41,6 @@
"button": { "button": {
"random_track": { "random_track": {
"name": "Play random track" "name": "Play random track"
},
"sleep": {
"name": "Sleep"
} }
}, },
"binary_sensor": { "binary_sensor": {
@@ -73,9 +70,6 @@
}, },
"playlist": { "playlist": {
"name": "Playlist" "name": "Playlist"
},
"queue": {
"name": "Queue"
} }
}, },
"sensor": { "sensor": {
@@ -86,28 +80,7 @@
"name": "Drawing progress" "name": "Drawing progress"
}, },
"error": { "error": {
"name": "Error", "name": "Error"
"state": {
"0": "None",
"1": "Error has occurred while reading the flash memory",
"2": "Error while starting the Wifi",
"3": "Error when starting DNS settings for your machine",
"4": "Failed to open the file to write",
"5": "Not enough memory to perform the upgrade",
"6": "Error while trying to upgrade your system",
"7": "Error while trying to download the new version of the software",
"8": "Error while reading the upgrading file",
"9": "Failed to start downloading the upgrade file",
"10": "Error while starting downloading the job file",
"11": "Error while opening the file folder",
"12": "Failed to delete a file",
"13": "Error while opening the job file",
"14": "You have wrong power adapter",
"15": "Failed to update the device IP on Oasis Server",
"16": "Your device failed centering itself",
"17": "There appears to be an issue with your Oasis Device",
"18": "Error while downloading the job file"
}
}, },
"led_color_id": { "led_color_id": {
"name": "LED color ID" "name": "LED color ID"
@@ -120,11 +93,9 @@
"centering": "Centering", "centering": "Centering",
"playing": "Playing", "playing": "Playing",
"paused": "Paused", "paused": "Paused",
"sleeping": "Sleeping",
"error": "Error", "error": "Error",
"updating": "Updating", "updating": "Updating",
"downloading": "Downloading", "downloading": "Downloading",
"busy": "Busy",
"live": "Live drawing" "live": "Live drawing"
} }
} }

View File

@@ -41,9 +41,6 @@
"button": { "button": {
"random_track": { "random_track": {
"name": "Play random track" "name": "Play random track"
},
"sleep": {
"name": "Sleep"
} }
}, },
"binary_sensor": { "binary_sensor": {
@@ -73,9 +70,6 @@
}, },
"playlist": { "playlist": {
"name": "Playlist" "name": "Playlist"
},
"queue": {
"name": "Queue"
} }
}, },
"sensor": { "sensor": {
@@ -86,28 +80,7 @@
"name": "Drawing progress" "name": "Drawing progress"
}, },
"error": { "error": {
"name": "Error", "name": "Error"
"state": {
"0": "None",
"1": "Error has occurred while reading the flash memory",
"2": "Error while starting the Wifi",
"3": "Error when starting DNS settings for your machine",
"4": "Failed to open the file to write",
"5": "Not enough memory to perform the upgrade",
"6": "Error while trying to upgrade your system",
"7": "Error while trying to download the new version of the software",
"8": "Error while reading the upgrading file",
"9": "Failed to start downloading the upgrade file",
"10": "Error while starting downloading the job file",
"11": "Error while opening the file folder",
"12": "Failed to delete a file",
"13": "Error while opening the job file",
"14": "You have wrong power adapter",
"15": "Failed to update the device IP on Oasis Server",
"16": "Your device failed centering itself",
"17": "There appears to be an issue with your Oasis Device",
"18": "Error while downloading the job file"
}
}, },
"led_color_id": { "led_color_id": {
"name": "LED color ID" "name": "LED color ID"
@@ -120,11 +93,9 @@
"centering": "Centering", "centering": "Centering",
"playing": "Playing", "playing": "Playing",
"paused": "Paused", "paused": "Paused",
"sleeping": "Sleeping",
"error": "Error", "error": "Error",
"updating": "Updating", "updating": "Updating",
"downloading": "Downloading", "downloading": "Downloading",
"busy": "Busy",
"live": "Live drawing" "live": "Live drawing"
} }
} }

View File

@@ -13,12 +13,6 @@ from custom_components.oasis_mini.pyoasismini.const import TRACKS
ACCESS_TOKEN = os.getenv("GROUNDED_TOKEN") ACCESS_TOKEN = os.getenv("GROUNDED_TOKEN")
def get_author_name(data: dict) -> str:
"""Get author name from a dict."""
author = (data.get("author") or {}).get("user") or {}
return author.get("name") or author.get("nickname") or "Oasis Mini"
async def update_tracks() -> None: async def update_tracks() -> None:
"""Update tracks.""" """Update tracks."""
client = OasisMini("", ACCESS_TOKEN) client = OasisMini("", ACCESS_TOKEN)
@@ -38,22 +32,23 @@ async def update_tracks() -> None:
for result in filter(lambda d: d["public"], data): for result in filter(lambda d: d["public"], data):
if ( if (
(track_id := result["id"]) not in TRACKS (track_id := result["id"]) not in TRACKS
or any( or result["name"] != TRACKS[track_id].get("name")
result[field] != TRACKS[track_id].get(field) or result["image"] != TRACKS[track_id].get("image")
for field in ("name", "image", "png_image")
)
or TRACKS[track_id].get("author") != get_author_name(result)
): ):
print(f"Updating track {track_id}: {result['name']}") print(f"Updating track {track_id}: {result["name"]}")
track_info = await client.async_cloud_get_track_info(int(track_id)) track_info = await client.async_cloud_get_track_info(int(track_id))
if not track_info: if not track_info:
print("No track info") print("No track info")
break break
result["author"] = get_author_name(result) author = (result.get("author") or {}).get("user") or {}
result["reduced_svg_content_new"] = track_info.get( updated_tracks[track_id] = {
"reduced_svg_content_new" "id": track_id,
) "name": result["name"],
updated_tracks[track_id] = result "author": author.get("name") or author.get("nickname") or "Oasis Mini",
"image": result["image"],
"clean_pattern": track_info.get("cleanPattern", {}).get("id"),
"reduced_svg_content": track_info.get("reduced_svg_content"),
}
await client.session.close() await client.session.close()
if not updated_tracks: if not updated_tracks: