from arcade.gui.experimental.scroll_area import UIScrollArea import logging, arcade, traceback, pyglet.display, arcade.gui, textwrap, os, json def dump_platform(): import platform logging.debug(f'Platform: {platform.platform()}') logging.debug(f'Release: {platform.release()}') logging.debug(f'Machine: {platform.machine()}') logging.debug(f'Architecture: {platform.architecture()}') def dump_gl(context=None): if context is not None: info = context.get_info() else: from pyglet.gl import gl_info as info logging.debug(f'gl_info.get_version(): {info.get_version()}') logging.debug(f'gl_info.get_vendor(): {info.get_vendor()}') logging.debug(f'gl_info.get_renderer(): {info.get_renderer()}') def print_debug_info(): logging.debug('########################## DEBUG INFO ##########################') logging.debug('') dump_platform() dump_gl() logging.debug('') logging.debug(f'Number of screens: {len(pyglet.display.get_display().get_screens())}') logging.debug('') for n, screen in enumerate(pyglet.display.get_display().get_screens()): logging.debug(f"Screen #{n+1}:") logging.debug(f'DPI: {screen.get_dpi()}') logging.debug(f'Scale: {screen.get_scale()}') logging.debug(f'Size: {screen.width}, {screen.height}') logging.debug(f'Position: {screen.x}, {screen.y}') logging.debug('') logging.debug('########################## DEBUG INFO ##########################') logging.debug('') def on_exception(*exc_info): logging.error(f"Unhandled exception:\n{''.join(traceback.format_exception(exc_info[1], limit=None))}") def get_closest_resolution(): allowed_resolutions = [(1366, 768), (1440, 900), (1600,900), (1920,1080), (2560,1440), (3840,2160)] screen_width, screen_height = arcade.get_screens()[0].width, arcade.get_screens()[0].height if (screen_width, screen_height) in allowed_resolutions: if not allowed_resolutions.index((screen_width, screen_height)) == 0: closest_resolution = allowed_resolutions[allowed_resolutions.index((screen_width, screen_height))-1] else: closest_resolution = (screen_width, screen_height) else: target_width, target_height = screen_width // 2, screen_height // 2 closest_resolution = min( allowed_resolutions, key=lambda res: abs(res[0] - target_width) + abs(res[1] - target_height) ) return closest_resolution class FakePyPresence(): def __init__(self): ... def update(self, *args, **kwargs): ... def close(self, *args, **kwargs): ... class UIFocusTextureButton(arcade.gui.UITextureButton): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) arcade.gui.bind(self, "hovered", self.on_hover) def on_hover(self): if self.hovered: self.resize(width=self.width * 1.1, height=self.height * 1.1) else: self.resize(width=self.width / 1.1, height=self.height / 1.1) # Thanks to Eruvanos for the MouseAwareScrollArea and the UIMouseOutOfAreaEvent class UIMouseOutOfAreaEvent(arcade.gui.UIEvent): """Indicates that the mouse is outside a specific area.""" pass class MouseAwareScrollArea(UIScrollArea): """Keep track of mouse position, None if outside of area.""" mouse_inside = False def on_event(self, event: arcade.gui.UIEvent): if isinstance(event, arcade.gui.UIMouseMovementEvent): if self.rect.point_in_rect(event.pos): if not self.mouse_inside: self.mouse_inside = True else: if self.mouse_inside: self.mouse_inside = False self.dispatch_ui_event(UIMouseOutOfAreaEvent(self)) return super().on_event(event) class Card(arcade.gui.UIBoxLayout): def __init__(self, thumbnail, line_1: str, line_2: str, width: int, height: int, padding=10): super().__init__(width=width, height=height, space_between=padding, align="top") self.button = self.add(arcade.gui.UITextureButton( texture=thumbnail, texture_hovered=thumbnail, width=width / 2.5, height=height / 2.5, interaction_buttons=[arcade.MOUSE_BUTTON_LEFT, arcade.MOUSE_BUTTON_RIGHT] )) if line_1: self.line_1_label = self.add(arcade.gui.UILabel( text=line_1, font_name="Roboto", font_size=14, width=width, height=height * 0.5, multiline=True )) if line_2: self.line_2_label = self.add(arcade.gui.UILabel( text=line_2, font_name="Roboto", font_size=12, width=width, height=height * 0.5, multiline=True, text_color=arcade.color.GRAY )) def on_event(self, event: arcade.gui.UIEvent): if isinstance(event, UIMouseOutOfAreaEvent): # not hovering self.with_background(color=arcade.color.TRANSPARENT_BLACK) self.trigger_full_render() elif isinstance(event, arcade.gui.UIMouseMovementEvent): if self.rect.point_in_rect(event.pos): # hovering self.with_background(color=arcade.color.DARK_GRAY) self.trigger_full_render() else: # not hovering self.with_background(color=arcade.color.TRANSPARENT_BLACK) self.trigger_full_render() elif isinstance(event, arcade.gui.UIMousePressEvent) and self.rect.point_in_rect(event.pos): self.button.on_click(event) return super().on_event(event) def on_exception(*exc_info): logging.error(f"Unhandled exception:\n{''.join(traceback.format_exception(exc_info[1], limit=None))}") def get_closest_resolution(): allowed_resolutions = [(1366, 768), (1440, 900), (1600,900), (1920,1080), (2560,1440), (3840,2160)] screen_width, screen_height = arcade.get_screens()[0].width, arcade.get_screens()[0].height if (screen_width, screen_height) in allowed_resolutions: if not allowed_resolutions.index((screen_width, screen_height)) == 0: closest_resolution = allowed_resolutions[allowed_resolutions.index((screen_width, screen_height))-1] else: closest_resolution = (screen_width, screen_height) else: target_width, target_height = screen_width // 2, screen_height // 2 closest_resolution = min( allowed_resolutions, key=lambda res: abs(res[0] - target_width) + abs(res[1] - target_height) ) return closest_resolution def convert_seconds_to_date(seconds): days, remainder = divmod(seconds, 86400) hours, remainder = divmod(remainder, 3600) minutes, seconds = divmod(remainder, 60) result = "" if days > 0: result += "{} days ".format(int(days)) if hours > 0: result += "{} hours ".format(int(hours)) if minutes > 0: result += "{} minutes ".format(int(minutes)) if seconds > 0 or not any([days, hours, minutes]): result += "{} seconds".format(int(seconds)) return result.strip() def get_wordwrapped_text(text, width=18): if len(text) < width: output_text = text.center(width) elif len(text) == width: output_text = text else: output_text = '\n'.join(textwrap.wrap(text, width=width)) return output_text def ensure_metadata_file(): if os.path.exists("metadata_cache.json") and os.path.isfile("metadata_cache.json"): with open("metadata_cache.json", "r") as file: metadata_cache = json.load(file) else: metadata_cache = { "query_results": {}, "recording_by_id": {}, "artist_by_id": {}, "lyrics_by_artist_title": {}, "album_by_id": {} } return metadata_cache