Convert from card based view to list based view with thumbnails

This commit is contained in:
csd4ni3l
2025-06-25 21:19:29 +02:00
parent 31e31d2da1
commit b192bdf424
2 changed files with 36 additions and 79 deletions

View File

@@ -3,7 +3,7 @@ import arcade, pyglet
from utils.preload import * from utils.preload import *
from utils.constants import button_style, slider_style, audio_extensions, discord_presence_id from utils.constants import button_style, slider_style, audio_extensions, discord_presence_id
from utils.utils import FakePyPresence, UIFocusTextureButton, Card, extract_metadata, get_audio_thumbnail_texture, truncate_end, get_wrapped_text, adjust_volume from utils.utils import FakePyPresence, UIFocusTextureButton, ListItem, extract_metadata, get_audio_thumbnail_texture, truncate_end, adjust_volume
from math import ceil from math import ceil
from thefuzz import process, fuzz from thefuzz import process, fuzz
@@ -56,9 +56,7 @@ class Main(arcade.gui.UIView):
self.tab_options = self.settings_dict.get("tab_options", [os.path.join("~", "Music"), os.path.join("~", "Downloads")]) self.tab_options = self.settings_dict.get("tab_options", [os.path.join("~", "Music"), os.path.join("~", "Downloads")])
self.tab_content = {} self.tab_content = {}
self.tab_wrapped_text = {}
self.playlist_content = {} self.playlist_content = {}
self.playlist_wrapped_text = {}
self.thumbnails = {} self.thumbnails = {}
self.tab_buttons = {} self.tab_buttons = {}
self.music_buttons = {} self.music_buttons = {}
@@ -107,8 +105,8 @@ class Main(arcade.gui.UIView):
self.scrollbar.size_hint = (0.02, 1) self.scrollbar.size_hint = (0.02, 1)
self.scroll_box.add(self.scrollbar) self.scroll_box.add(self.scrollbar)
self.music_grid = arcade.gui.UIGridLayout(horizontal_spacing=30, vertical_spacing=30, row_count=100, column_count=8) self.music_box = arcade.gui.UIBoxLayout(space_between=5)
self.scroll_area.add(self.music_grid) self.scroll_area.add(self.music_box)
# Utility # Utility
@@ -262,11 +260,11 @@ class Main(arcade.gui.UIView):
def show_content(self, tab): def show_content(self, tab):
for music_button in self.music_buttons.values(): for music_button in self.music_buttons.values():
music_button.remove(music_button.button) music_button.remove(music_button.button)
music_button.remove(music_button.label) music_button.remove(music_button.image)
self.music_grid.remove(music_button) self.music_box.remove(music_button)
del music_button del music_button
self.music_grid.clear() self.music_box.clear()
self.music_buttons.clear() self.music_buttons.clear()
if self.current_mode == "files": if self.current_mode == "files":
@@ -274,61 +272,46 @@ class Main(arcade.gui.UIView):
if not self.search_term == "": if not self.search_term == "":
matches = process.extract(self.search_term, self.tab_content[self.current_tab], limit=5, processor=lambda text: text.lower(), scorer=fuzz.partial_token_sort_ratio) matches = process.extract(self.search_term, self.tab_content[self.current_tab], limit=5, processor=lambda text: text.lower(), scorer=fuzz.partial_token_sort_ratio)
self.highest_score_file = f"{self.current_tab}/{matches[0][0]}" self.highest_score_file = f"{self.current_tab}/{matches[0][0]}"
for n, match in enumerate(matches): for match in matches:
music_filename = match[0] music_filename = match[0]
self.music_buttons[f"{tab}/{music_filename}"] = self.music_grid.add(Card(card_texture=self.thumbnails[f"{tab}/{music_filename}"], font_name="Roboto", font_size=13, text=music_filename, width=self.window.width / 11, height=self.window.height / 11), row=0, column=n) self.music_buttons[f"{tab}/{music_filename}"] = self.music_box.add(ListItem(texture=self.thumbnails[f"{tab}/{music_filename}"], font_name="Roboto", font_size=13, text=music_filename, width=self.window.width / 1.2, height=self.window.height / 11))
self.music_buttons[f"{tab}/{music_filename}"].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.music_button_click(event, f"{tab}/{music_filename}") self.music_buttons[f"{tab}/{music_filename}"].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.music_button_click(event, f"{tab}/{music_filename}")
else: else:
self.music_grid.row_count = ceil(len(self.tab_content[tab]) / 8)
self.highest_score_file = "" self.highest_score_file = ""
self.no_music_label.visible = not self.tab_content[tab] self.no_music_label.visible = not self.tab_content[tab]
for n, music_filename in enumerate(self.tab_content[tab]): for music_filename in self.tab_content[tab]:
row = n // 8 self.music_buttons[f"{tab}/{music_filename}"] = self.music_box.add(ListItem(texture=self.thumbnails[f"{tab}/{music_filename}"], font_name="Roboto", font_size=13, text=music_filename, width=self.window.width / 1.2, height=self.window.height / 11))
col = n % 8
self.music_buttons[f"{tab}/{music_filename}"] = self.music_grid.add(Card(card_texture=self.thumbnails[f"{tab}/{music_filename}"], font_name="Roboto", font_size=13, text=self.tab_wrapped_text[tab][n], width=self.window.width / 11, height=self.window.height / 11), row=row, column=col)
self.music_buttons[f"{tab}/{music_filename}"].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.music_button_click(event, f"{tab}/{music_filename}") self.music_buttons[f"{tab}/{music_filename}"].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.music_button_click(event, f"{tab}/{music_filename}")
self.music_grid._update_size_hints() self.music_box._update_size_hints()
elif self.current_mode == "playlist": elif self.current_mode == "playlist":
self.current_playlist = tab self.current_playlist = tab
n = 0
if self.current_playlist: if self.current_playlist:
if not self.search_term == "": if not self.search_term == "":
matches = process.extract(self.search_term, self.playlist_content[tab], limit=5, processor=lambda text: text.lower(), scorer=fuzz.partial_token_sort_ratio) matches = process.extract(self.search_term, self.playlist_content[tab], limit=5, processor=lambda text: text.lower(), scorer=fuzz.partial_token_sort_ratio)
self.highest_score_file = matches[0][0] self.highest_score_file = matches[0][0]
for n, match in enumerate(matches): for match in matches:
music_filename = match[0] music_filename = match[0]
self.music_buttons[music_filename] = self.music_grid.add(Card(card_texture=self.thumbnails[music_filename], font_name="Roboto", font_size=13, text=music_filename, width=self.window.width / 11, height=self.window.height / 11), row=0, column=n) self.music_buttons[music_filename] = self.music_box.add(ListItem(texture=self.thumbnails[music_filename], font_name="Roboto", font_size=13, text=music_filename, width=self.window.width / 1.2, height=self.window.height / 11))
self.music_buttons[music_filename].button.on_click = lambda event, music_filename=music_filename: self.music_button_click(event, music_filename) self.music_buttons[music_filename].button.on_click = lambda event, music_filename=music_filename: self.music_button_click(event, music_filename)
else: else:
self.music_grid.row_count = ceil((len(self.playlist_content[tab]) + 1) / 8)
self.highest_score_file = "" self.highest_score_file = ""
self.no_music_label.visible = not self.playlist_content[tab] self.no_music_label.visible = not self.playlist_content[tab]
for n, music_filename in enumerate(self.playlist_content[tab]): for music_filename in self.playlist_content[tab]:
row = n // 8 self.music_buttons[music_filename] = self.music_box.add(ListItem(texture=self.thumbnails[music_filename], font_name="Roboto", font_size=13, text=music_filename, width=self.window.width / 1.2, height=self.window.height / 11))
col = n % 8
self.music_buttons[music_filename] = self.music_grid.add(Card(card_texture=self.thumbnails[music_filename], font_name="Roboto", font_size=13, text=self.playlist_wrapped_text[tab][n], width=self.window.width / 11, height=self.window.height / 11), row=row, column=col)
self.music_buttons[music_filename].button.on_click = lambda event, music_filename=music_filename: self.music_button_click(event, music_filename) self.music_buttons[music_filename].button.on_click = lambda event, music_filename=music_filename: self.music_button_click(event, music_filename)
self.music_grid._update_size_hints() self.music_box._update_size_hints()
row = (n + 1) // 8 self.music_buttons["add_music"] = self.music_box.add(ListItem(texture=plus_icon, font_name="Roboto", font_size=13, text="Add Music", width=self.window.width / 1.2, height=self.window.height / 11))
col = (n + 1) % 8
self.music_buttons["add_music"] = self.music_grid.add(Card(card_texture=music_icon, font_name="Roboto", font_size=13, text="Add Music", width=self.window.width / 11, height=self.window.height / 11), row=row, column=col)
self.music_buttons["add_music"].button.on_click = lambda event: self.add_music() self.music_buttons["add_music"].button.on_click = lambda event: self.add_music()
self.anchor.detect_focusable_widgets() self.anchor.detect_focusable_widgets()
@@ -350,9 +333,7 @@ class Main(arcade.gui.UIView):
def load_content(self): def load_content(self):
self.tab_content.clear() self.tab_content.clear()
self.tab_wrapped_text.clear()
self.playlist_content.clear() self.playlist_content.clear()
self.playlist_wrapped_text.clear()
for tab in self.tab_options: for tab in self.tab_options:
expanded_tab = os.path.expanduser(tab) expanded_tab = os.path.expanduser(tab)
@@ -368,8 +349,6 @@ class Main(arcade.gui.UIView):
if f"{expanded_tab}/{filename}" not in self.thumbnails: if f"{expanded_tab}/{filename}" not in self.thumbnails:
self.thumbnails[f"{expanded_tab}/{filename}"] = get_audio_thumbnail_texture(f"{expanded_tab}/{filename}", self.window.size) self.thumbnails[f"{expanded_tab}/{filename}"] = get_audio_thumbnail_texture(f"{expanded_tab}/{filename}", self.window.size)
self.tab_content[expanded_tab].append(filename) self.tab_content[expanded_tab].append(filename)
self.tab_wrapped_text[expanded_tab] = get_wrapped_text(self.tab_content[expanded_tab], self.window.width // 11, 14)
for playlist, content in self.settings_dict.get("playlists", {}).items(): for playlist, content in self.settings_dict.get("playlists", {}).items():
for file in content: for file in content:
@@ -381,7 +360,6 @@ class Main(arcade.gui.UIView):
self.thumbnails[file] = get_audio_thumbnail_texture(file, self.window.size) self.thumbnails[file] = get_audio_thumbnail_texture(file, self.window.size)
self.playlist_content[playlist] = content self.playlist_content[playlist] = content
self.playlist_wrapped_text[playlist] = get_wrapped_text(content, self.window.width // 11, 14)
def load_tabs(self): def load_tabs(self):
self.tab_box.clear() self.tab_box.clear()

View File

@@ -1,4 +1,4 @@
import logging, sys, traceback, os, re, platform, urllib.request, textwrap, io, base64, tempfile import logging, sys, traceback, os, re, platform, urllib.request, io, base64, tempfile
from mutagen.easyid3 import EasyID3 from mutagen.easyid3 import EasyID3
from mutagen.id3 import ID3 from mutagen.id3 import ID3
@@ -8,7 +8,8 @@ from pydub import AudioSegment
from PIL import Image from PIL import Image
from utils.constants import menu_background_color from utils.constants import menu_background_color, button_style
from utils.preload import button_texture, button_hovered_texture
import pyglet, arcade, arcade.gui import pyglet, arcade, arcade.gui
@@ -86,50 +87,28 @@ class UIFocusTextureButton(arcade.gui.UITextureButton):
else: else:
self.resize(width=self.width / 1.1, height=self.height / 1.1) self.resize(width=self.width / 1.1, height=self.height / 1.1)
class Card(arcade.gui.UIBoxLayout): class ListItem(arcade.gui.UIBoxLayout):
def __init__(self, width: int, height: int, font_name: str, font_size: int, text: str, card_texture: arcade.Texture, padding=10): def __init__(self, width: int, height: int, font_name: str, font_size: int, text: str, texture: arcade.Texture, padding=10):
super().__init__(width=width, height=height, space_between=padding, align="top") super().__init__(width=width, height=height, space_between=padding, align="top", vertical=False)
self.image = self.add(arcade.gui.UIImage(
texture=texture,
width=width * 0.1,
height=height
))
self.button = self.add(arcade.gui.UITextureButton( self.button = self.add(arcade.gui.UITextureButton(
texture=card_texture, text=text,
texture_hovered=card_texture, texture=button_texture,
texture_pressed=card_texture, texture_hovered=button_hovered_texture,
texture_disabled=card_texture, texture_pressed=button_texture,
width=width, texture_disabled=button_texture,
style=button_style,
width=width * 0.9,
height=height, height=height,
interaction_buttons=[arcade.MOUSE_BUTTON_LEFT, arcade.MOUSE_BUTTON_RIGHT] interaction_buttons=[arcade.MOUSE_BUTTON_LEFT, arcade.MOUSE_BUTTON_RIGHT]
)) ))
self.label = self.add(arcade.gui.UILabel(
text=text,
font_name=font_name,
font_size=font_size,
width=width,
height=height * 0.1,
multiline=True,
))
def get_wrapped_text(text_list: list[str], width: int, font_size: int):
max_lines = 0
wrapped_line_list = []
wrapped_text_list = []
for text in text_list: # get max lines and wrap text
wrapped_lines = textwrap.wrap(text, width=int(width / (font_size * 0.6)))
if len(wrapped_lines) > max_lines:
max_lines = len(wrapped_lines)
wrapped_line_list.append(wrapped_lines)
for wrapped_lines in wrapped_line_list:
if len(wrapped_lines) < max_lines: # adjust text to maximum lines
for i in range(max_lines - len(wrapped_lines)):
wrapped_lines.append("")
wrapped_text_list.append("\n".join(wrapped_lines))
return wrapped_text_list
def on_exception(*exc_info): def on_exception(*exc_info):
logging.error(f"Unhandled exception:\n{''.join(traceback.format_exception(exc_info[1], limit=None))}") logging.error(f"Unhandled exception:\n{''.join(traceback.format_exception(exc_info[1], limit=None))}")