mirror of
https://github.com/csd4ni3l/music-player.git
synced 2026-01-01 04:03:42 +01:00
Move from list to card grid view, only support popular file extensions,
Use Roboto Black as a font, dont recreate ui on refresh, update styles
This commit is contained in:
2
CREDITS
2
CREDITS
@@ -2,6 +2,8 @@ Some icons used in this project are from Font Awesome Free by Fonticons, Inc.
|
||||
Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0): https://creativecommons.org/licenses/by/4.0/
|
||||
Icons were modified (repainted to white and exported to PNG).
|
||||
|
||||
The Roboto Black font used in this project is licensed under the Open Font License. Read assets/fonts/OFL.txt for more information.
|
||||
|
||||
Huge Thanks to Python for being the programming language used in this application.
|
||||
https://www.python.org/
|
||||
|
||||
|
||||
93
assets/fonts/OFL.txt
Normal file
93
assets/fonts/OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
BIN
assets/fonts/Roboto-Black.ttf
Normal file
BIN
assets/fonts/Roboto-Black.ttf
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -1,6 +1,6 @@
|
||||
import arcade, arcade.gui, os, json
|
||||
|
||||
from utils.constants import button_style, dropdown_style
|
||||
from utils.constants import button_style
|
||||
from utils.preload import button_texture, button_hovered_texture
|
||||
from utils.utils import UIFocusTextureButton
|
||||
|
||||
@@ -32,10 +32,10 @@ class AddMusic(arcade.gui.UIView):
|
||||
self.anchor = self.add_widget(UIFocusGroup(size_hint=(1, 1)))
|
||||
self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x="center", anchor_y="center")
|
||||
|
||||
self.playlist_label = self.box.add(arcade.gui.UILabel(text="Playlist", font_name="Protest Strike", font_size=32))
|
||||
self.playlist_option = self.box.add(arcade.gui.UIDropdown(default=list(self.playlists.keys())[0], options=list(self.playlists.keys()), width=self.window.width / 2, height=self.window.height / 15, primary_style=dropdown_style, dropdown_style=dropdown_style, active_style=dropdown_style))
|
||||
self.music_label = self.box.add(arcade.gui.UILabel(text="Music File Path", font_name="Protest Strike", font_size=32))
|
||||
self.add_music_input = self.box.add(arcade.gui.UIInputText(font_name="Protest Strike", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.playlist_label = self.box.add(arcade.gui.UILabel(text="Playlist", font_name="Roboto", font_size=32))
|
||||
self.playlist_option = self.box.add(arcade.gui.UIDropdown(default=list(self.playlists.keys())[0], options=list(self.playlists.keys()), width=self.window.width / 2, height=self.window.height / 15, primary_style=button_style, dropdown_style=button_style, active_style=button_style))
|
||||
self.music_label = self.box.add(arcade.gui.UILabel(text="Music File Path", font_name="Roboto", font_size=32))
|
||||
self.add_music_input = self.box.add(arcade.gui.UIInputText(font_name="Roboto", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.add_music_button = self.box.add(UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Add Music', style=button_style, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.add_music_button.on_click = lambda event: self.add_music()
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import arcade, arcade.gui, os, json, threading, subprocess
|
||||
from arcade.gui.experimental.focus import UIFocusGroup
|
||||
|
||||
from utils.utils import UIFocusTextureButton, ensure_yt_dlp
|
||||
from utils.constants import button_style, dropdown_style, yt_dlp_parameters
|
||||
from utils.constants import button_style
|
||||
from utils.preload import button_texture, button_hovered_texture
|
||||
|
||||
class Downloader(arcade.gui.UIView):
|
||||
@@ -37,12 +37,12 @@ class Downloader(arcade.gui.UIView):
|
||||
|
||||
self.download_box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x="center", anchor_y="center")
|
||||
|
||||
self.url_name_label = self.download_box.add(arcade.gui.UILabel(text="URL or Name:", font_name="Protest Strike", font_size=36))
|
||||
self.url_name_input = self.download_box.add(arcade.gui.UIInputText(font_name="Protest Strike", width=self.window.width / 2, height=self.window.height / 15, font_size=36))
|
||||
self.url_name_label = self.download_box.add(arcade.gui.UILabel(text="URL or Name:", font_name="Roboto", font_size=36))
|
||||
self.url_name_input = self.download_box.add(arcade.gui.UIInputText(font_name="Roboto", width=self.window.width / 2, height=self.window.height / 15, font_size=36))
|
||||
self.url_name_input.activate()
|
||||
|
||||
self.tab_label = self.download_box.add(arcade.gui.UILabel(text="Path:", font_name="Protest Strike", font_size=36))
|
||||
self.tab_selector = self.download_box.add(arcade.gui.UIDropdown(default=self.tab_options[0], options=self.tab_options, width=self.window.width / 2, height=self.window.height / 15, primary_style=dropdown_style, dropdown_style=dropdown_style, active_style=dropdown_style))
|
||||
self.tab_label = self.download_box.add(arcade.gui.UILabel(text="Path:", font_name="Roboto", font_size=36))
|
||||
self.tab_selector = self.download_box.add(arcade.gui.UIDropdown(default=self.tab_options[0], options=self.tab_options, width=self.window.width / 2, height=self.window.height / 15, primary_style=button_style, dropdown_style=button_style, active_style=button_style))
|
||||
|
||||
self.status_label = self.download_box.add(arcade.gui.UILabel(text="No errors.", font_size=16, text_color=arcade.color.LIGHT_GREEN))
|
||||
|
||||
|
||||
111
menus/main.py
111
menus/main.py
@@ -3,8 +3,9 @@ import arcade, arcade.gui, pyglet
|
||||
|
||||
from utils.preload import *
|
||||
from utils.constants import button_style, slider_style, audio_extensions, discord_presence_id
|
||||
from utils.utils import FakePyPresence, UIFocusTextureButton, extract_metadata, truncate_end
|
||||
from utils.utils import FakePyPresence, UIFocusTextureButton, Card, extract_metadata, get_audio_thumbnail_texture, truncate_end
|
||||
|
||||
from math import ceil
|
||||
from thefuzz import process, fuzz
|
||||
from pydub import AudioSegment
|
||||
|
||||
@@ -56,6 +57,7 @@ class Main(arcade.gui.UIView):
|
||||
self.tab_options = self.settings_dict.get("tab_options", ["~/Music", "~/Downloads"])
|
||||
self.tab_content = {}
|
||||
self.playlist_content = {}
|
||||
self.thumbnails = {}
|
||||
self.tab_buttons = {}
|
||||
self.music_buttons = {}
|
||||
|
||||
@@ -76,21 +78,26 @@ class Main(arcade.gui.UIView):
|
||||
def on_show_view(self):
|
||||
super().on_show_view()
|
||||
|
||||
self.load_content()
|
||||
|
||||
self.create_ui()
|
||||
|
||||
def create_ui(self):
|
||||
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
|
||||
|
||||
self.ui_box = self.anchor.add(arcade.gui.UIBoxLayout(size_hint=(1, 1), space_between=10))
|
||||
|
||||
# Tabs
|
||||
self.tab_box = self.ui_box.add(arcade.gui.UIBoxLayout(size_hint=(0.95, 0.1), space_between=10, vertical=False))
|
||||
self.load_tabs()
|
||||
|
||||
self.load_content()
|
||||
if self.current_mode == "playlist" and not self.current_playlist:
|
||||
self.current_playlist = list(self.playlist_content.keys())[0] if self.playlist_content else None
|
||||
self.load_tabs()
|
||||
|
||||
# Scrollable Sounds
|
||||
self.scroll_box = self.ui_box.add(arcade.gui.UIBoxLayout(size_hint=(0.95, 0.8), space_between=15, vertical=False))
|
||||
|
||||
self.scroll_area = UIScrollArea(size_hint=(0.9, 1)) # center on screen
|
||||
self.scroll_area = UIScrollArea(size_hint=(0.95, 1)) # center on screen
|
||||
self.scroll_area.scroll_speed = -50
|
||||
self.scroll_box.add(self.scroll_area)
|
||||
|
||||
@@ -98,8 +105,8 @@ class Main(arcade.gui.UIView):
|
||||
self.scrollbar.size_hint = (0.02, 1)
|
||||
self.scroll_box.add(self.scrollbar)
|
||||
|
||||
self.music_box = arcade.gui.UIBoxLayout(space_between=2)
|
||||
self.scroll_area.add(self.music_box)
|
||||
self.music_grid = arcade.gui.UIGridLayout(horizontal_spacing=30, vertical_spacing=30, row_count=100, column_count=5)
|
||||
self.scroll_area.add(self.music_grid)
|
||||
|
||||
# Utility
|
||||
|
||||
@@ -125,8 +132,8 @@ class Main(arcade.gui.UIView):
|
||||
# Controls
|
||||
|
||||
self.control_box = self.ui_box.add(arcade.gui.UIBoxLayout(size_hint=(0.95, 0.1), space_between=10, vertical=False))
|
||||
self.current_music_label = self.control_box.add(arcade.gui.UILabel(text=truncate_end(self.current_music_name, int(self.window.width / 30)) if self.current_music_name else "No songs playing", font_name="Protest Strike", font_size=16))
|
||||
self.time_label = self.control_box.add(arcade.gui.UILabel(text="00:00", font_name="Protest Strike", font_size=16))
|
||||
self.current_music_label = self.control_box.add(arcade.gui.UILabel(text=truncate_end(self.current_music_name, int(self.window.width / 30)) if self.current_music_name else "No songs playing", font_name="Roboto", font_size=16))
|
||||
self.time_label = self.control_box.add(arcade.gui.UILabel(text="00:00", font_name="Roboto", font_size=16))
|
||||
|
||||
self.progressbar = self.control_box.add(arcade.gui.UISlider(style=slider_style, width=self.window.width / 3, height=35))
|
||||
self.progressbar.on_change = self.on_progress_change
|
||||
@@ -147,7 +154,7 @@ class Main(arcade.gui.UIView):
|
||||
self.progressbar.max_value = self.current_length
|
||||
self.volume = int(self.current_music_player.volume * 100)
|
||||
|
||||
self.volume_label = self.control_box.add(arcade.gui.UILabel(text=f"{self.volume}%", font_name="Protest Strike", font_size=16))
|
||||
self.volume_label = self.control_box.add(arcade.gui.UILabel(text=f"{self.volume}%", font_name="Roboto", font_size=16))
|
||||
self.volume_slider = self.control_box.add(arcade.gui.UISlider(style=slider_style, width=self.window.width / 10, height=35, value=self.volume, max_value=100))
|
||||
self.volume_slider.on_change = self.on_volume_slider_change
|
||||
|
||||
@@ -200,6 +207,8 @@ class Main(arcade.gui.UIView):
|
||||
self.highest_score_file = ""
|
||||
self.search_term = ""
|
||||
|
||||
self.load_tabs()
|
||||
|
||||
self.reload()
|
||||
|
||||
def skip_sound(self):
|
||||
@@ -237,7 +246,7 @@ class Main(arcade.gui.UIView):
|
||||
self.update_buttons()
|
||||
|
||||
def show_content(self, tab):
|
||||
self.music_box.clear()
|
||||
self.music_grid.clear()
|
||||
self.music_buttons.clear()
|
||||
|
||||
if self.current_mode == "files":
|
||||
@@ -245,16 +254,21 @@ class Main(arcade.gui.UIView):
|
||||
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)
|
||||
self.highest_score_file = f"{self.current_tab}/{matches[0][0]}"
|
||||
for match in matches:
|
||||
for n, match in enumerate(matches):
|
||||
music_filename = match[0]
|
||||
self.music_buttons[music_filename] = self.music_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=music_filename, style=button_style, width=self.window.width * 0.85, height=self.window.height / 30))
|
||||
self.music_buttons[music_filename].on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(f"{tab}/{music_filename}")
|
||||
|
||||
self.music_buttons[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 / 6, height=self.window.height / 5), row=0, column=n)
|
||||
self.music_buttons[music_filename].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(f"{tab}/{music_filename}")
|
||||
else:
|
||||
self.music_grid.row_count = ceil(len(self.tab_content[tab]) / 5)
|
||||
self.music_grid._update_size_hints()
|
||||
|
||||
self.highest_score_file = ""
|
||||
for music_filename in self.tab_content[tab]:
|
||||
self.music_buttons[music_filename] = self.music_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=music_filename, style=button_style, width=self.window.width * 0.85, height=self.window.height / 30))
|
||||
self.music_buttons[music_filename].on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(f"{tab}/{music_filename}")
|
||||
for n, music_filename in enumerate(self.tab_content[tab]):
|
||||
row = n // 5
|
||||
col = n % 5
|
||||
|
||||
self.music_buttons[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 / 6, height=self.window.height / 5), row=row, column=col)
|
||||
self.music_buttons[music_filename].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(f"{tab}/{music_filename}")
|
||||
|
||||
elif self.current_mode == "playlist":
|
||||
self.current_playlist = tab
|
||||
@@ -263,45 +277,57 @@ class Main(arcade.gui.UIView):
|
||||
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)
|
||||
self.highest_score_file = matches[0][0]
|
||||
for match in matches:
|
||||
for n, match in enumerate(matches):
|
||||
music_filename = match[0]
|
||||
self.music_buttons[music_filename] = self.music_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=music_filename, style=button_style, width=self.window.width * 0.85, height=self.window.height / 30))
|
||||
self.music_buttons[music_filename].on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(music_filename)
|
||||
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 / 6, height=self.window.height / 5), row=0, column=n)
|
||||
self.music_buttons[music_filename].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(music_filename)
|
||||
|
||||
else:
|
||||
self.highest_score_file = ""
|
||||
for music_filename in self.playlist_content[tab]:
|
||||
self.music_buttons[music_filename] = self.music_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=music_filename, style=button_style, width=self.window.width * 0.85, height=self.window.height / 30))
|
||||
self.music_buttons[music_filename].on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(music_filename)
|
||||
self.music_grid.row_count = ceil((len(self.playlist_content[tab]) + 1) / 5)
|
||||
self.music_grid._update_size_hints()
|
||||
|
||||
self.music_buttons["add_music"] = self.music_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="Add Music", style=button_style, width=self.window.width * 0.85, height=self.window.height / 30))
|
||||
self.music_buttons["add_music"].on_click = lambda event: self.add_music()
|
||||
self.highest_score_file = ""
|
||||
|
||||
for n, music_filename in enumerate(self.playlist_content[tab]):
|
||||
row = n // 5
|
||||
col = n % 5
|
||||
|
||||
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 / 6, height=self.window.height / 5), row=row, column=col)
|
||||
self.music_buttons[music_filename].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(music_filename)
|
||||
row = (n + 1) // 5
|
||||
col = (n + 1) % 5
|
||||
|
||||
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 / 6, height=self.window.height / 5), row=row, column=col)
|
||||
self.music_buttons["add_music"].button.on_click = lambda event: self.add_music()
|
||||
|
||||
self.update_buttons()
|
||||
|
||||
def load_content(self):
|
||||
self.tab_content.clear()
|
||||
self.playlist_content.clear()
|
||||
|
||||
for tab in self.tab_options:
|
||||
self.tab_content[os.path.expanduser(tab)] = []
|
||||
for filename in os.listdir(os.path.expanduser(tab)):
|
||||
if filename.split(".")[-1] in audio_extensions:
|
||||
if f"{os.path.expanduser(tab)}/{filename}" not in self.thumbnails:
|
||||
self.thumbnails[f"{os.path.expanduser(tab)}/{filename}"] = get_audio_thumbnail_texture(f"{os.path.expanduser(tab)}/{filename}", self.window.size)
|
||||
self.tab_content[os.path.expanduser(tab)].append(filename)
|
||||
|
||||
for playlist, content in self.settings_dict.get("playlists", {}).items():
|
||||
self.playlist_content[playlist] = content
|
||||
for content in self.settings_dict.get("playlists", {}).values():
|
||||
for file in content:
|
||||
if file not in self.thumbnails:
|
||||
self.thumbnails[file] = get_audio_thumbnail_texture(file, self.window.size)
|
||||
|
||||
self.playlist_content = self.settings_dict.get("playlists", {})
|
||||
|
||||
def load_tabs(self):
|
||||
self.tab_box = self.ui_box.add(arcade.gui.UIBoxLayout(size_hint=(0.95, 0.1), space_between=10, vertical=False))
|
||||
self.tab_box.clear()
|
||||
|
||||
if self.current_mode == "files":
|
||||
for tab in self.tab_options:
|
||||
self.tab_buttons[os.path.expanduser(tab)] = self.tab_box.add(UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=os.path.basename(os.path.normpath(os.path.expanduser(tab))), style=button_style, width=self.window.width / 10, height=self.window.height / 15))
|
||||
self.tab_buttons[os.path.expanduser(tab)] = self.tab_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=os.path.basename(os.path.normpath(os.path.expanduser(tab))), style=button_style, width=self.window.width / 10, height=self.window.height / 15))
|
||||
self.tab_buttons[os.path.expanduser(tab)].on_click = lambda event, tab=os.path.expanduser(tab): self.show_content(tab)
|
||||
elif self.current_mode == "playlist":
|
||||
for playlist in self.playlist_content:
|
||||
self.tab_buttons[playlist] = self.tab_box.add(UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=playlist, style=button_style, width=self.window.width / 10, height=self.window.height / 15))
|
||||
self.tab_buttons[playlist] = self.tab_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=playlist, style=button_style, width=self.window.width / 10, height=self.window.height / 15))
|
||||
self.tab_buttons[playlist].on_click = lambda event, playlist=playlist: self.show_content(playlist)
|
||||
|
||||
def on_progress_change(self, event):
|
||||
@@ -388,7 +414,7 @@ class Main(arcade.gui.UIView):
|
||||
elif symbol == arcade.key.BACKSPACE:
|
||||
self.search_term = self.search_term[:-1]
|
||||
if self.current_mode == "files":
|
||||
self.show_content(self.current_tab)
|
||||
self.show_content(os.path.expanduser(self.current_tab))
|
||||
elif self.current_mode == "playlist":
|
||||
self.show_content(self.current_playlist)
|
||||
elif symbol == arcade.key.ENTER and self.highest_score_file:
|
||||
@@ -396,14 +422,14 @@ class Main(arcade.gui.UIView):
|
||||
self.highest_score_file = ""
|
||||
self.search_term = ""
|
||||
if self.current_mode == "files":
|
||||
self.show_content(self.current_tab)
|
||||
self.show_content(os.path.expanduser(self.current_tab))
|
||||
elif self.current_mode == "playlist":
|
||||
self.show_content(self.current_playlist)
|
||||
elif symbol == arcade.key.ESCAPE:
|
||||
self.highest_score_file = ""
|
||||
self.search_term = ""
|
||||
if self.current_mode == "files":
|
||||
self.show_content(self.current_tab)
|
||||
self.show_content(os.path.expanduser(self.current_tab))
|
||||
elif self.current_mode == "playlist":
|
||||
self.show_content(self.current_playlist)
|
||||
|
||||
@@ -414,7 +440,7 @@ class Main(arcade.gui.UIView):
|
||||
self.search_term += text
|
||||
|
||||
if self.current_mode == "files":
|
||||
self.show_content(self.current_tab)
|
||||
self.show_content(os.path.expanduser(self.current_tab))
|
||||
elif self.current_mode == "playlist":
|
||||
self.show_content(self.current_playlist)
|
||||
|
||||
@@ -443,8 +469,13 @@ class Main(arcade.gui.UIView):
|
||||
self.window.show_view(Downloader(self.pypresence_client, self.current_mode, self.current_music_name, self.current_length, self.current_music_player, self.queue, self.loaded_sounds, self.shuffle))
|
||||
|
||||
def reload(self):
|
||||
self.ui.clear()
|
||||
self.on_show_view()
|
||||
self.load_content()
|
||||
|
||||
if self.current_mode == "files":
|
||||
self.show_content(self.current_tab)
|
||||
elif self.current_mode == "playlist":
|
||||
self.show_content(self.current_playlist)
|
||||
|
||||
self.update_buttons()
|
||||
|
||||
def update_presence(self, _):
|
||||
|
||||
@@ -2,7 +2,6 @@ import arcade, arcade.gui, os, json
|
||||
|
||||
from utils.constants import button_style
|
||||
from utils.preload import button_texture, button_hovered_texture
|
||||
from utils.utils import UIFocusTextureButton
|
||||
|
||||
from arcade.gui.experimental.focus import UIFocusGroup
|
||||
|
||||
@@ -34,17 +33,17 @@ class NewTab(arcade.gui.UIView):
|
||||
self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x="center", anchor_y="center")
|
||||
|
||||
if self.current_mode == "files":
|
||||
self.new_tab_label = self.box.add(arcade.gui.UILabel(text="New Tab Path:", font_name="Protest Strike", font_size=32))
|
||||
self.new_tab_input = self.box.add(arcade.gui.UIInputText(font_name="Protest Strike", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.new_tab_button = self.box.add(UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Add new tab', style=button_style, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.new_tab_label = self.box.add(arcade.gui.UILabel(text="New Tab Path:", font_name="Roboto", font_size=32))
|
||||
self.new_tab_input = self.box.add(arcade.gui.UIInputText(font_name="Roboto", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.new_tab_button = self.box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Add new tab', style=button_style, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.new_tab_button.on_click = lambda event: self.add_tab()
|
||||
elif self.current_mode == "playlist":
|
||||
self.new_tab_label = self.box.add(arcade.gui.UILabel(text="New Playlist Name:", font_name="Protest Strike", font_size=32))
|
||||
self.new_tab_input = self.box.add(arcade.gui.UIInputText(font_name="Protest Strike", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.new_tab_button = self.box.add(UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Add new Playlist', style=button_style, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.new_tab_label = self.box.add(arcade.gui.UILabel(text="New Playlist Name:", font_name="Roboto", font_size=32))
|
||||
self.new_tab_input = self.box.add(arcade.gui.UIInputText(font_name="Roboto", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.new_tab_button = self.box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Add new Playlist', style=button_style, width=self.window.width / 2, height=self.window.height / 10))
|
||||
self.new_tab_button.on_click = lambda event: self.add_tab()
|
||||
|
||||
self.back_button = self.anchor.add(UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50), anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
|
||||
self.back_button = self.anchor.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50), anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
|
||||
self.back_button.on_click = lambda event: self.main_exit()
|
||||
|
||||
self.anchor.detect_focusable_widgets()
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from arcade.gui.widgets.buttons import UITextureButton
|
||||
import copy, pypresence, json
|
||||
|
||||
import arcade, arcade.gui
|
||||
|
||||
from utils.constants import button_style, dropdown_style, slider_style, settings, discord_presence_id, settings_start_category
|
||||
from utils.utils import FakePyPresence, UIFocusTextureButton
|
||||
from utils.constants import button_style, slider_style, settings, discord_presence_id, settings_start_category
|
||||
from utils.utils import FakePyPresence
|
||||
from utils.preload import button_texture, button_hovered_texture
|
||||
|
||||
from arcade.gui.experimental.focus import UIFocusGroup
|
||||
@@ -55,7 +56,7 @@ class Settings(arcade.gui.UIView):
|
||||
|
||||
self.ui.push_handlers(self)
|
||||
|
||||
self.back_button = UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
|
||||
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
|
||||
self.back_button.on_click = lambda event: self.main_exit()
|
||||
self.top_box.add(self.back_button)
|
||||
|
||||
@@ -67,7 +68,7 @@ class Settings(arcade.gui.UIView):
|
||||
|
||||
def display_categories(self):
|
||||
for category in settings:
|
||||
category_button = UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=category, style=button_style, width=self.window.width / 10, height=50)
|
||||
category_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=category, style=button_style, width=self.window.width / 10, height=50)
|
||||
|
||||
if not category == "Credits":
|
||||
category_button.on_click = lambda event, category=category: self.display_category(category)
|
||||
@@ -93,27 +94,27 @@ class Settings(arcade.gui.UIView):
|
||||
self.value_layout.clear()
|
||||
|
||||
for setting in settings[category]:
|
||||
label = arcade.gui.UILabel(text=setting, font_name="Protest Strike", font_size=28, text_color=arcade.color.WHITE )
|
||||
label = arcade.gui.UILabel(text=setting, font_name="Roboto", font_size=28, text_color=arcade.color.WHITE )
|
||||
self.key_layout.add(label)
|
||||
|
||||
setting_dict = settings[category][setting]
|
||||
|
||||
if setting_dict['type'] == "option":
|
||||
dropdown = arcade.gui.UIDropdown(options=setting_dict['options'], width=200, height=50, default=self.settings_dict.get(setting_dict["config_key"], setting_dict["options"][0]), active_style=dropdown_style, dropdown_style=dropdown_style, primary_style=dropdown_style)
|
||||
dropdown = arcade.gui.UIDropdown(options=setting_dict['options'], width=200, height=50, default=self.settings_dict.get(setting_dict["config_key"], setting_dict["options"][0]), active_style=button_style, dropdown_style=button_style, primary_style=button_style)
|
||||
dropdown.on_change = lambda _, setting=setting, dropdown=dropdown: self.update(setting, dropdown.value, "option")
|
||||
self.value_layout.add(dropdown)
|
||||
|
||||
elif setting_dict['type'] == "bool":
|
||||
button_layout = self.value_layout.add(arcade.gui.UIBoxLayout(space_between=50, vertical=False))
|
||||
|
||||
on_radiobutton = UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="ON", style=button_style, width=150, height=50)
|
||||
on_radiobutton = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="ON", style=button_style, width=150, height=50)
|
||||
self.on_radiobuttons[setting] = on_radiobutton
|
||||
on_radiobutton.on_click = lambda _, setting=setting: self.update(setting, True, "bool")
|
||||
on_radiobutton.on_click = lambda event, setting=setting: self.update(setting, True, "bool")
|
||||
button_layout.add(on_radiobutton)
|
||||
|
||||
off_radiobutton = UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="OFF", style=button_style, width=150, height=50)
|
||||
off_radiobutton = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="OFF", style=button_style, width=150, height=50)
|
||||
self.off_radiobuttons[setting] = off_radiobutton
|
||||
off_radiobutton.on_click = lambda _, setting=setting: self.update(setting, False, "bool")
|
||||
off_radiobutton.on_click = lambda event, setting=setting: self.update(setting, False, "bool")
|
||||
button_layout.add(off_radiobutton)
|
||||
|
||||
if self.settings_dict.get(setting_dict["config_key"], setting_dict["default"]):
|
||||
@@ -142,7 +143,7 @@ class Settings(arcade.gui.UIView):
|
||||
self.sliders[setting] = slider
|
||||
self.value_layout.add(slider)
|
||||
|
||||
self.apply_button = UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Apply', style=button_style, width=200, height=100)
|
||||
self.apply_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Apply', style=button_style, width=200, height=100)
|
||||
self.apply_button.on_click = lambda event: self.apply_settings()
|
||||
self.anchor.add(self.apply_button, anchor_x="right", anchor_y="bottom", align_x=-10, align_y=10)
|
||||
|
||||
@@ -205,7 +206,7 @@ class Settings(arcade.gui.UIView):
|
||||
|
||||
self.create_layouts()
|
||||
|
||||
self.back_button = UIFocusTextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
|
||||
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
|
||||
self.back_button.on_click = lambda event: self.main_exit()
|
||||
self.top_box.add(self.back_button)
|
||||
|
||||
@@ -275,7 +276,7 @@ class Settings(arcade.gui.UIView):
|
||||
else:
|
||||
font_size = 12
|
||||
|
||||
self.credits_label = arcade.gui.UILabel(text=text, text_color=arcade.color.WHITE, font_name="Protest Strike", font_size=font_size, align="center", multiline=True)
|
||||
self.credits_label = arcade.gui.UILabel(text=text, text_color=arcade.color.WHITE, font_name="Roboto", font_size=font_size, align="center", multiline=True)
|
||||
|
||||
self.key_layout.add(self.credits_label)
|
||||
|
||||
|
||||
2
run.py
2
run.py
@@ -9,6 +9,8 @@ pyglet.options.debug_gl = False
|
||||
|
||||
import logging, datetime, json, sys, arcade
|
||||
|
||||
arcade.ArcadeContext.atlas_size = (16384, 16384)
|
||||
|
||||
from utils.utils import get_closest_resolution, print_debug_info, on_exception, ErrorView
|
||||
from utils.constants import log_dir, menu_background_color
|
||||
from menus.main import Main
|
||||
|
||||
@@ -7,47 +7,51 @@ menu_background_color = (17, 17, 17)
|
||||
log_dir = 'logs'
|
||||
discord_presence_id = 1368277020332523530
|
||||
|
||||
audio_extensions = [
|
||||
"3g2", "3gp", "aac", "ac3", "aiff", "alac", "amr", "ape", "au", "caf",
|
||||
"dts", "flac", "gsm", "m4a", "mka", "mlp", "mmf", "mp2", "mp3",
|
||||
"oga", "ogg", "opus", "ra", "rm", "sln", "tta", "vorbis", "voc", "vox",
|
||||
"wav", "webm", "wma", "wv"
|
||||
]
|
||||
audio_extensions = ["mp3", "m4a", "mp4", "aac", "flac", "ogg", "opus", "wav"]
|
||||
|
||||
yt_dlp_parameters = {
|
||||
"final_ext": "mp3",
|
||||
"format": "bestaudio/best",
|
||||
"outtmpl": {"pl_thumbnail": "", "default": "downloaded_music.mp3"},
|
||||
"postprocessors": [
|
||||
{
|
||||
"key": "FFmpegExtractAudio",
|
||||
"nopostoverwrites": False,
|
||||
"preferredcodec": "mp3",
|
||||
"preferredquality": "5"
|
||||
},
|
||||
{
|
||||
"add_chapters": True,
|
||||
"add_infojson": "if_exists",
|
||||
"add_metadata": True,
|
||||
"key": "FFmpegMetadata"
|
||||
},
|
||||
{ "already_have_thumbnail": False, "key": "EmbedThumbnail" }
|
||||
],
|
||||
"writethumbnail": True
|
||||
DARK_GRAY = Color(45, 45, 45)
|
||||
GRAY = Color(70, 70, 70)
|
||||
LIGHT_GRAY = Color(150, 150, 150)
|
||||
PRIMARY = Color(0, 189, 126)
|
||||
PRIMARY_DARK = Color(0, 145, 96)
|
||||
DISABLED = Color(90, 90, 90)
|
||||
FONT_COLOR = arcade.color.BLACK
|
||||
FONT = "Roboto"
|
||||
FONT_SIZE = 14
|
||||
BIG_FONT_SIZE = 22
|
||||
|
||||
button_style = {
|
||||
"normal": UIFlatButtonStyle(font_name=FONT, font_size=FONT_SIZE, font_color=FONT_COLOR, bg=GRAY),
|
||||
"hover": UIFlatButtonStyle(font_name=FONT, font_size=FONT_SIZE, font_color=FONT_COLOR, bg=PRIMARY),
|
||||
"press": UIFlatButtonStyle(font_name=FONT, font_size=FONT_SIZE, font_color=FONT_COLOR, bg=PRIMARY_DARK),
|
||||
"disabled": UIFlatButtonStyle(font_name=FONT, font_size=FONT_SIZE, font_color=LIGHT_GRAY, bg=DISABLED),
|
||||
}
|
||||
|
||||
button_style = {'normal': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK), 'hover': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK),
|
||||
'press': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK), 'disabled': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK)}
|
||||
big_button_style = {'normal': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK, font_size=26), 'hover': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK, font_size=26),
|
||||
'press': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK, font_size=26), 'disabled': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK, font_size=26)}
|
||||
big_button_style = {
|
||||
"normal": UIFlatButtonStyle(font_name=FONT, font_size=BIG_FONT_SIZE, font_color=FONT_COLOR, bg=GRAY),
|
||||
"hover": UIFlatButtonStyle(font_name=FONT, font_size=BIG_FONT_SIZE, font_color=FONT_COLOR, bg=PRIMARY),
|
||||
"press": UIFlatButtonStyle(font_name=FONT, font_size=BIG_FONT_SIZE, font_color=FONT_COLOR, bg=PRIMARY_DARK),
|
||||
"disabled": UIFlatButtonStyle(font_name=FONT, font_size=BIG_FONT_SIZE, font_color=LIGHT_GRAY, bg=DISABLED),
|
||||
}
|
||||
|
||||
dropdown_style = {'normal': UIFlatButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK, bg=Color(128, 128, 128)), 'hover': UIFlatButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK, bg=Color(49, 154, 54)),
|
||||
'press': UIFlatButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK, bg=Color(128, 128, 128)), 'disabled': UIFlatButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK, bg=Color(128, 128, 128))}
|
||||
slider_default_style = UISliderStyle(
|
||||
bg=GRAY,
|
||||
unfilled_track=DARK_GRAY,
|
||||
filled_track=PRIMARY
|
||||
)
|
||||
|
||||
slider_default_style = UISliderStyle(bg=Color(128, 128, 128), unfilled_track=Color(128, 128, 128), filled_track=Color(49, 154, 54))
|
||||
slider_hover_style = UISliderStyle(bg=Color(49, 154, 54), unfilled_track=Color(128, 128, 128), filled_track=Color(49, 154, 54))
|
||||
slider_hover_style = UISliderStyle(
|
||||
bg=PRIMARY,
|
||||
unfilled_track=DARK_GRAY,
|
||||
filled_track=PRIMARY_DARK
|
||||
)
|
||||
|
||||
slider_style = {'normal': slider_default_style, 'hover': slider_hover_style, 'press': slider_hover_style, 'disabled': slider_default_style}
|
||||
slider_style = {
|
||||
"normal": slider_default_style,
|
||||
"hover": slider_hover_style,
|
||||
"press": slider_hover_style,
|
||||
"disabled": slider_default_style,
|
||||
}
|
||||
|
||||
settings = {
|
||||
"Music": {
|
||||
|
||||
@@ -19,3 +19,5 @@ download_icon = arcade.load_texture("assets/graphics/download.png")
|
||||
plus_icon = arcade.load_texture("assets/graphics/plus.png")
|
||||
playlist_icon = arcade.load_texture("assets/graphics/playlist.png")
|
||||
files_icon = arcade.load_texture("assets/graphics/files.png")
|
||||
|
||||
music_icon = arcade.load_texture("assets/graphics/music.png")
|
||||
|
||||
124
utils/utils.py
124
utils/utils.py
@@ -1,8 +1,9 @@
|
||||
import logging, arcade, arcade.gui, sys, traceback, os, re, platform, urllib.request, zipfile, subprocess
|
||||
import logging, sys, traceback, os, re, platform, urllib.request, zipfile, subprocess, textwrap, io, base64
|
||||
from mutagen.easyid3 import EasyID3
|
||||
|
||||
from mutagen import File
|
||||
from PIL import Image
|
||||
from utils.constants import menu_background_color
|
||||
import pyglet
|
||||
import pyglet, arcade, arcade.gui
|
||||
|
||||
def dump_platform():
|
||||
import platform
|
||||
@@ -58,25 +59,6 @@ class ErrorView(arcade.gui.UIView):
|
||||
msgbox.on_action = lambda event: self.exit()
|
||||
self.add_widget(msgbox)
|
||||
|
||||
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):
|
||||
@@ -97,16 +79,60 @@ class UIFocusTextureButton(arcade.gui.UITextureButton):
|
||||
else:
|
||||
self.resize(width=self.width / 1.1, height=self.height / 1.1)
|
||||
|
||||
class Card(arcade.gui.UIBoxLayout):
|
||||
def __init__(self, width: int, height: int, font_name: str, font_size: int, text: str, card_texture: arcade.Texture, padding=10):
|
||||
super().__init__(width=width, height=height, space_between=padding, align="bottom")
|
||||
|
||||
self.button = self.add(arcade.gui.UITextureButton(
|
||||
texture=card_texture,
|
||||
texture_hovered=card_texture,
|
||||
texture_pressed=card_texture,
|
||||
texture_disabled=card_texture,
|
||||
width=width / 2,
|
||||
height=height * 0.5,
|
||||
))
|
||||
|
||||
wrapped_lines = textwrap.wrap(text, width=int(width / (font_size * 0.6)))
|
||||
wrapped_text = "\n".join(wrapped_lines)
|
||||
|
||||
self.label = self.add(arcade.gui.UILabel(
|
||||
text=wrapped_text,
|
||||
font_name=font_name,
|
||||
font_size=font_size,
|
||||
width=width,
|
||||
height=height * 0.5,
|
||||
multiline=True
|
||||
))
|
||||
|
||||
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 get_yt_dlp_binary_path():
|
||||
binary = "yt-dlp"
|
||||
system = platform.system()
|
||||
|
||||
if system == "Windows":
|
||||
binary += ".exe"
|
||||
binary = "yt-dlp.exe"
|
||||
elif system == "Darwin":
|
||||
binary += "_macos"
|
||||
binary = "yt-dlp_macos"
|
||||
elif system == "Linux":
|
||||
binary += "_linux"
|
||||
binary = "yt-dlp_linux"
|
||||
|
||||
return os.path.join("bin", binary)
|
||||
|
||||
@@ -150,10 +176,10 @@ def extract_metadata(filename):
|
||||
name_only = re.sub(r'\s*\[[a-zA-Z0-9\-_]{5,}\]$', '', name_only)
|
||||
|
||||
try:
|
||||
audio = EasyID3(filename)
|
||||
thumb_audio = EasyID3(filename)
|
||||
|
||||
artist = str(audio["artist"][0])
|
||||
title = str(audio["title"][0])
|
||||
artist = str(thumb_audio["artist"][0])
|
||||
title = str(thumb_audio["title"][0])
|
||||
|
||||
artist_title_match = re.search(r'^.+\s*-\s*.+$', title) # check for Artist - Title titles, so Artist doesnt appear twice
|
||||
|
||||
@@ -182,3 +208,43 @@ def extract_metadata(filename):
|
||||
title = name_only
|
||||
|
||||
return artist, title
|
||||
|
||||
def get_audio_thumbnail_texture(audio_path: str, window_resolution: tuple) -> arcade.Texture:
|
||||
ext = os.path.splitext(audio_path)[1].lower().lstrip('.')
|
||||
thumb_audio = File(audio_path)
|
||||
|
||||
thumb_image_data = None
|
||||
|
||||
try:
|
||||
if ext == 'mp3':
|
||||
for tag in thumb_audio.values():
|
||||
if tag.FrameID == "APIC":
|
||||
thumb_image_data = tag.data
|
||||
break
|
||||
|
||||
elif ext in ('m4a', 'mp4', 'aac'):
|
||||
if 'covr' in thumb_audio:
|
||||
thumb_image_data = thumb_audio['covr'][0]
|
||||
|
||||
elif ext == 'flac':
|
||||
if thumb_audio.pictures:
|
||||
thumb_image_data = thumb_audio.pictures[0].data
|
||||
|
||||
elif ext in ('ogg', 'opus'):
|
||||
if "metadata_block_picture" in thumb_audio:
|
||||
pic_data = base64.b64decode(thumb_audio["metadata_block_picture"][0])
|
||||
import struct
|
||||
header_len = struct.unpack(">I", pic_data[0:4])[0]
|
||||
thumb_image_data = pic_data[4 + header_len:]
|
||||
|
||||
if thumb_image_data:
|
||||
pil_image = Image.open(io.BytesIO(thumb_image_data)).convert("RGBA")
|
||||
pil_image = pil_image.resize((int(window_resolution[0] / 5), int(window_resolution[1] / 8)))
|
||||
thumb_texture = arcade.Texture(pil_image)
|
||||
return thumb_texture
|
||||
|
||||
except Exception as e:
|
||||
logging.debug(f"[Thumbnail Error] {audio_path}: {e}")
|
||||
|
||||
from utils.preload import music_icon
|
||||
return music_icon
|
||||
|
||||
Reference in New Issue
Block a user