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/
|
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).
|
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.
|
Huge Thanks to Python for being the programming language used in this application.
|
||||||
https://www.python.org/
|
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
|
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.preload import button_texture, button_hovered_texture
|
||||||
from utils.utils import UIFocusTextureButton
|
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.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.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_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=dropdown_style, dropdown_style=dropdown_style, active_style=dropdown_style))
|
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="Protest Strike", font_size=32))
|
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="Protest Strike", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
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 = 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()
|
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 arcade.gui.experimental.focus import UIFocusGroup
|
||||||
|
|
||||||
from utils.utils import UIFocusTextureButton, ensure_yt_dlp
|
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
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
class Downloader(arcade.gui.UIView):
|
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.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_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="Protest Strike", width=self.window.width / 2, height=self.window.height / 15, 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.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_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=dropdown_style, dropdown_style=dropdown_style, active_style=dropdown_style))
|
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))
|
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.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, 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 thefuzz import process, fuzz
|
||||||
from pydub import AudioSegment
|
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_options = self.settings_dict.get("tab_options", ["~/Music", "~/Downloads"])
|
||||||
self.tab_content = {}
|
self.tab_content = {}
|
||||||
self.playlist_content = {}
|
self.playlist_content = {}
|
||||||
|
self.thumbnails = {}
|
||||||
self.tab_buttons = {}
|
self.tab_buttons = {}
|
||||||
self.music_buttons = {}
|
self.music_buttons = {}
|
||||||
|
|
||||||
@@ -76,21 +78,26 @@ class Main(arcade.gui.UIView):
|
|||||||
def on_show_view(self):
|
def on_show_view(self):
|
||||||
super().on_show_view()
|
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.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))
|
self.ui_box = self.anchor.add(arcade.gui.UIBoxLayout(size_hint=(1, 1), space_between=10))
|
||||||
|
|
||||||
# Tabs
|
# 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:
|
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.current_playlist = list(self.playlist_content.keys())[0] if self.playlist_content else None
|
||||||
self.load_tabs()
|
|
||||||
|
|
||||||
# Scrollable Sounds
|
# 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_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_area.scroll_speed = -50
|
||||||
self.scroll_box.add(self.scroll_area)
|
self.scroll_box.add(self.scroll_area)
|
||||||
|
|
||||||
@@ -98,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_box = arcade.gui.UIBoxLayout(space_between=2)
|
self.music_grid = arcade.gui.UIGridLayout(horizontal_spacing=30, vertical_spacing=30, row_count=100, column_count=5)
|
||||||
self.scroll_area.add(self.music_box)
|
self.scroll_area.add(self.music_grid)
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
|
|
||||||
@@ -125,8 +132,8 @@ class Main(arcade.gui.UIView):
|
|||||||
# Controls
|
# Controls
|
||||||
|
|
||||||
self.control_box = self.ui_box.add(arcade.gui.UIBoxLayout(size_hint=(0.95, 0.1), space_between=10, vertical=False))
|
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.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="Protest Strike", 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 = 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
|
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.progressbar.max_value = self.current_length
|
||||||
self.volume = int(self.current_music_player.volume * 100)
|
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 = 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
|
self.volume_slider.on_change = self.on_volume_slider_change
|
||||||
|
|
||||||
@@ -200,6 +207,8 @@ class Main(arcade.gui.UIView):
|
|||||||
self.highest_score_file = ""
|
self.highest_score_file = ""
|
||||||
self.search_term = ""
|
self.search_term = ""
|
||||||
|
|
||||||
|
self.load_tabs()
|
||||||
|
|
||||||
self.reload()
|
self.reload()
|
||||||
|
|
||||||
def skip_sound(self):
|
def skip_sound(self):
|
||||||
@@ -237,7 +246,7 @@ class Main(arcade.gui.UIView):
|
|||||||
self.update_buttons()
|
self.update_buttons()
|
||||||
|
|
||||||
def show_content(self, tab):
|
def show_content(self, tab):
|
||||||
self.music_box.clear()
|
self.music_grid.clear()
|
||||||
self.music_buttons.clear()
|
self.music_buttons.clear()
|
||||||
|
|
||||||
if self.current_mode == "files":
|
if self.current_mode == "files":
|
||||||
@@ -245,16 +254,21 @@ 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 match in matches:
|
for n, match in enumerate(matches):
|
||||||
music_filename = match[0]
|
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] = 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].on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(f"{tab}/{music_filename}")
|
self.music_buttons[music_filename].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(f"{tab}/{music_filename}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
self.music_grid.row_count = ceil(len(self.tab_content[tab]) / 5)
|
||||||
|
self.music_grid._update_size_hints()
|
||||||
|
|
||||||
self.highest_score_file = ""
|
self.highest_score_file = ""
|
||||||
for music_filename in self.tab_content[tab]:
|
for n, music_filename in enumerate(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))
|
row = n // 5
|
||||||
self.music_buttons[music_filename].on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(f"{tab}/{music_filename}")
|
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":
|
elif self.current_mode == "playlist":
|
||||||
self.current_playlist = tab
|
self.current_playlist = tab
|
||||||
@@ -263,45 +277,57 @@ class Main(arcade.gui.UIView):
|
|||||||
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 match in matches:
|
for n, match in enumerate(matches):
|
||||||
music_filename = match[0]
|
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] = 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].on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(music_filename)
|
self.music_buttons[music_filename].button.on_click = lambda event, tab=tab, music_filename=music_filename: self.queue.append(music_filename)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.highest_score_file = ""
|
self.music_grid.row_count = ceil((len(self.playlist_content[tab]) + 1) / 5)
|
||||||
for music_filename in self.playlist_content[tab]:
|
self.music_grid._update_size_hints()
|
||||||
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["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.highest_score_file = ""
|
||||||
self.music_buttons["add_music"].on_click = lambda event: self.add_music()
|
|
||||||
|
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()
|
self.update_buttons()
|
||||||
|
|
||||||
def load_content(self):
|
def load_content(self):
|
||||||
self.tab_content.clear()
|
|
||||||
self.playlist_content.clear()
|
|
||||||
|
|
||||||
for tab in self.tab_options:
|
for tab in self.tab_options:
|
||||||
self.tab_content[os.path.expanduser(tab)] = []
|
self.tab_content[os.path.expanduser(tab)] = []
|
||||||
for filename in os.listdir(os.path.expanduser(tab)):
|
for filename in os.listdir(os.path.expanduser(tab)):
|
||||||
if filename.split(".")[-1] in audio_extensions:
|
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)
|
self.tab_content[os.path.expanduser(tab)].append(filename)
|
||||||
|
|
||||||
for playlist, content in self.settings_dict.get("playlists", {}).items():
|
for content in self.settings_dict.get("playlists", {}).values():
|
||||||
self.playlist_content[playlist] = content
|
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):
|
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":
|
if self.current_mode == "files":
|
||||||
for tab in self.tab_options:
|
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)
|
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":
|
elif self.current_mode == "playlist":
|
||||||
for playlist in self.playlist_content:
|
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)
|
self.tab_buttons[playlist].on_click = lambda event, playlist=playlist: self.show_content(playlist)
|
||||||
|
|
||||||
def on_progress_change(self, event):
|
def on_progress_change(self, event):
|
||||||
@@ -388,7 +414,7 @@ class Main(arcade.gui.UIView):
|
|||||||
elif symbol == arcade.key.BACKSPACE:
|
elif symbol == arcade.key.BACKSPACE:
|
||||||
self.search_term = self.search_term[:-1]
|
self.search_term = self.search_term[:-1]
|
||||||
if self.current_mode == "files":
|
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":
|
elif self.current_mode == "playlist":
|
||||||
self.show_content(self.current_playlist)
|
self.show_content(self.current_playlist)
|
||||||
elif symbol == arcade.key.ENTER and self.highest_score_file:
|
elif symbol == arcade.key.ENTER and self.highest_score_file:
|
||||||
@@ -396,14 +422,14 @@ class Main(arcade.gui.UIView):
|
|||||||
self.highest_score_file = ""
|
self.highest_score_file = ""
|
||||||
self.search_term = ""
|
self.search_term = ""
|
||||||
if self.current_mode == "files":
|
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":
|
elif self.current_mode == "playlist":
|
||||||
self.show_content(self.current_playlist)
|
self.show_content(self.current_playlist)
|
||||||
elif symbol == arcade.key.ESCAPE:
|
elif symbol == arcade.key.ESCAPE:
|
||||||
self.highest_score_file = ""
|
self.highest_score_file = ""
|
||||||
self.search_term = ""
|
self.search_term = ""
|
||||||
if self.current_mode == "files":
|
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":
|
elif self.current_mode == "playlist":
|
||||||
self.show_content(self.current_playlist)
|
self.show_content(self.current_playlist)
|
||||||
|
|
||||||
@@ -414,7 +440,7 @@ class Main(arcade.gui.UIView):
|
|||||||
self.search_term += text
|
self.search_term += text
|
||||||
|
|
||||||
if self.current_mode == "files":
|
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":
|
elif self.current_mode == "playlist":
|
||||||
self.show_content(self.current_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))
|
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):
|
def reload(self):
|
||||||
self.ui.clear()
|
self.load_content()
|
||||||
self.on_show_view()
|
|
||||||
|
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()
|
self.update_buttons()
|
||||||
|
|
||||||
def update_presence(self, _):
|
def update_presence(self, _):
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import arcade, arcade.gui, os, json
|
|||||||
|
|
||||||
from utils.constants import button_style
|
from utils.constants import button_style
|
||||||
from utils.preload import button_texture, button_hovered_texture
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
from utils.utils import UIFocusTextureButton
|
|
||||||
|
|
||||||
from arcade.gui.experimental.focus import UIFocusGroup
|
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")
|
self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x="center", anchor_y="center")
|
||||||
|
|
||||||
if self.current_mode == "files":
|
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_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="Protest Strike", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
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(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_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()
|
self.new_tab_button.on_click = lambda event: self.add_tab()
|
||||||
elif self.current_mode == "playlist":
|
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_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="Protest Strike", font_size=32, width=self.window.width / 2, height=self.window.height / 10))
|
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(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_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.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.back_button.on_click = lambda event: self.main_exit()
|
||||||
|
|
||||||
self.anchor.detect_focusable_widgets()
|
self.anchor.detect_focusable_widgets()
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
from arcade.gui.widgets.buttons import UITextureButton
|
||||||
import copy, pypresence, json
|
import copy, pypresence, json
|
||||||
|
|
||||||
import arcade, arcade.gui
|
import arcade, arcade.gui
|
||||||
|
|
||||||
from utils.constants import button_style, dropdown_style, slider_style, settings, discord_presence_id, settings_start_category
|
from utils.constants import button_style, slider_style, settings, discord_presence_id, settings_start_category
|
||||||
from utils.utils import FakePyPresence, UIFocusTextureButton
|
from utils.utils import FakePyPresence
|
||||||
from utils.preload import button_texture, button_hovered_texture
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
from arcade.gui.experimental.focus import UIFocusGroup
|
from arcade.gui.experimental.focus import UIFocusGroup
|
||||||
@@ -55,7 +56,7 @@ class Settings(arcade.gui.UIView):
|
|||||||
|
|
||||||
self.ui.push_handlers(self)
|
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.back_button.on_click = lambda event: self.main_exit()
|
||||||
self.top_box.add(self.back_button)
|
self.top_box.add(self.back_button)
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ class Settings(arcade.gui.UIView):
|
|||||||
|
|
||||||
def display_categories(self):
|
def display_categories(self):
|
||||||
for category in settings:
|
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":
|
if not category == "Credits":
|
||||||
category_button.on_click = lambda event, category=category: self.display_category(category)
|
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()
|
self.value_layout.clear()
|
||||||
|
|
||||||
for setting in settings[category]:
|
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)
|
self.key_layout.add(label)
|
||||||
|
|
||||||
setting_dict = settings[category][setting]
|
setting_dict = settings[category][setting]
|
||||||
|
|
||||||
if setting_dict['type'] == "option":
|
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")
|
dropdown.on_change = lambda _, setting=setting, dropdown=dropdown: self.update(setting, dropdown.value, "option")
|
||||||
self.value_layout.add(dropdown)
|
self.value_layout.add(dropdown)
|
||||||
|
|
||||||
elif setting_dict['type'] == "bool":
|
elif setting_dict['type'] == "bool":
|
||||||
button_layout = self.value_layout.add(arcade.gui.UIBoxLayout(space_between=50, vertical=False))
|
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
|
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)
|
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
|
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)
|
button_layout.add(off_radiobutton)
|
||||||
|
|
||||||
if self.settings_dict.get(setting_dict["config_key"], setting_dict["default"]):
|
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.sliders[setting] = slider
|
||||||
self.value_layout.add(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.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)
|
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.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.back_button.on_click = lambda event: self.main_exit()
|
||||||
self.top_box.add(self.back_button)
|
self.top_box.add(self.back_button)
|
||||||
|
|
||||||
@@ -275,7 +276,7 @@ class Settings(arcade.gui.UIView):
|
|||||||
else:
|
else:
|
||||||
font_size = 12
|
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)
|
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
|
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.utils import get_closest_resolution, print_debug_info, on_exception, ErrorView
|
||||||
from utils.constants import log_dir, menu_background_color
|
from utils.constants import log_dir, menu_background_color
|
||||||
from menus.main import Main
|
from menus.main import Main
|
||||||
|
|||||||
@@ -7,47 +7,51 @@ menu_background_color = (17, 17, 17)
|
|||||||
log_dir = 'logs'
|
log_dir = 'logs'
|
||||||
discord_presence_id = 1368277020332523530
|
discord_presence_id = 1368277020332523530
|
||||||
|
|
||||||
audio_extensions = [
|
audio_extensions = ["mp3", "m4a", "mp4", "aac", "flac", "ogg", "opus", "wav"]
|
||||||
"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"
|
|
||||||
]
|
|
||||||
|
|
||||||
yt_dlp_parameters = {
|
DARK_GRAY = Color(45, 45, 45)
|
||||||
"final_ext": "mp3",
|
GRAY = Color(70, 70, 70)
|
||||||
"format": "bestaudio/best",
|
LIGHT_GRAY = Color(150, 150, 150)
|
||||||
"outtmpl": {"pl_thumbnail": "", "default": "downloaded_music.mp3"},
|
PRIMARY = Color(0, 189, 126)
|
||||||
"postprocessors": [
|
PRIMARY_DARK = Color(0, 145, 96)
|
||||||
{
|
DISABLED = Color(90, 90, 90)
|
||||||
"key": "FFmpegExtractAudio",
|
FONT_COLOR = arcade.color.BLACK
|
||||||
"nopostoverwrites": False,
|
FONT = "Roboto"
|
||||||
"preferredcodec": "mp3",
|
FONT_SIZE = 14
|
||||||
"preferredquality": "5"
|
BIG_FONT_SIZE = 22
|
||||||
},
|
|
||||||
{
|
button_style = {
|
||||||
"add_chapters": True,
|
"normal": UIFlatButtonStyle(font_name=FONT, font_size=FONT_SIZE, font_color=FONT_COLOR, bg=GRAY),
|
||||||
"add_infojson": "if_exists",
|
"hover": UIFlatButtonStyle(font_name=FONT, font_size=FONT_SIZE, font_color=FONT_COLOR, bg=PRIMARY),
|
||||||
"add_metadata": True,
|
"press": UIFlatButtonStyle(font_name=FONT, font_size=FONT_SIZE, font_color=FONT_COLOR, bg=PRIMARY_DARK),
|
||||||
"key": "FFmpegMetadata"
|
"disabled": UIFlatButtonStyle(font_name=FONT, font_size=FONT_SIZE, font_color=LIGHT_GRAY, bg=DISABLED),
|
||||||
},
|
|
||||||
{ "already_have_thumbnail": False, "key": "EmbedThumbnail" }
|
|
||||||
],
|
|
||||||
"writethumbnail": True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button_style = {'normal': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK), 'hover': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK),
|
big_button_style = {
|
||||||
'press': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK), 'disabled': UITextureButtonStyle(font_name="Protest Strike", font_color=arcade.color.BLACK)}
|
"normal": UIFlatButtonStyle(font_name=FONT, font_size=BIG_FONT_SIZE, font_color=FONT_COLOR, bg=GRAY),
|
||||||
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),
|
"hover": UIFlatButtonStyle(font_name=FONT, font_size=BIG_FONT_SIZE, font_color=FONT_COLOR, bg=PRIMARY),
|
||||||
'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)}
|
"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)),
|
slider_default_style = UISliderStyle(
|
||||||
'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))}
|
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(
|
||||||
slider_hover_style = UISliderStyle(bg=Color(49, 154, 54), unfilled_track=Color(128, 128, 128), filled_track=Color(49, 154, 54))
|
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 = {
|
settings = {
|
||||||
"Music": {
|
"Music": {
|
||||||
|
|||||||
@@ -19,3 +19,5 @@ download_icon = arcade.load_texture("assets/graphics/download.png")
|
|||||||
plus_icon = arcade.load_texture("assets/graphics/plus.png")
|
plus_icon = arcade.load_texture("assets/graphics/plus.png")
|
||||||
playlist_icon = arcade.load_texture("assets/graphics/playlist.png")
|
playlist_icon = arcade.load_texture("assets/graphics/playlist.png")
|
||||||
files_icon = arcade.load_texture("assets/graphics/files.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.easyid3 import EasyID3
|
||||||
|
from mutagen import File
|
||||||
|
from PIL import Image
|
||||||
from utils.constants import menu_background_color
|
from utils.constants import menu_background_color
|
||||||
import pyglet
|
import pyglet, arcade, arcade.gui
|
||||||
|
|
||||||
def dump_platform():
|
def dump_platform():
|
||||||
import platform
|
import platform
|
||||||
@@ -58,25 +59,6 @@ class ErrorView(arcade.gui.UIView):
|
|||||||
msgbox.on_action = lambda event: self.exit()
|
msgbox.on_action = lambda event: self.exit()
|
||||||
self.add_widget(msgbox)
|
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():
|
class FakePyPresence():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -97,16 +79,60 @@ 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):
|
||||||
|
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():
|
def get_yt_dlp_binary_path():
|
||||||
binary = "yt-dlp"
|
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
|
|
||||||
if system == "Windows":
|
if system == "Windows":
|
||||||
binary += ".exe"
|
binary = "yt-dlp.exe"
|
||||||
elif system == "Darwin":
|
elif system == "Darwin":
|
||||||
binary += "_macos"
|
binary = "yt-dlp_macos"
|
||||||
elif system == "Linux":
|
elif system == "Linux":
|
||||||
binary += "_linux"
|
binary = "yt-dlp_linux"
|
||||||
|
|
||||||
return os.path.join("bin", binary)
|
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)
|
name_only = re.sub(r'\s*\[[a-zA-Z0-9\-_]{5,}\]$', '', name_only)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
audio = EasyID3(filename)
|
thumb_audio = EasyID3(filename)
|
||||||
|
|
||||||
artist = str(audio["artist"][0])
|
artist = str(thumb_audio["artist"][0])
|
||||||
title = str(audio["title"][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
|
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
|
title = name_only
|
||||||
|
|
||||||
return artist, title
|
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