388 lines
14 KiB
Python
388 lines
14 KiB
Python
import pyray as pr
|
|
import math
|
|
from ctypes import c_float
|
|
from gapless_player import GaplessPlayer, song_data_to_Song, server, client, Song
|
|
from scrolling_text import ScrollingText
|
|
|
|
# --- Configuration Constants ---
|
|
INITIAL_SCREEN_WIDTH = 240
|
|
INITIAL_SCREEN_HEIGHT = 240
|
|
TARGET_FPS = 60
|
|
|
|
# --- State Variables ---
|
|
state = {
|
|
"screen_width": INITIAL_SCREEN_WIDTH,
|
|
"screen_height": INITIAL_SCREEN_HEIGHT,
|
|
"current_time": 120.0,
|
|
"total_time": 300.0,
|
|
"is_playing": True,
|
|
# 3D Camera State
|
|
"camera": None,
|
|
"render_texture": None,
|
|
# Assets
|
|
"album_texture": None,
|
|
"album_model": None,
|
|
}
|
|
|
|
# --- Utility Functions ---
|
|
|
|
|
|
def format_time_mm_ss(seconds):
|
|
"""Converts a time in seconds to an 'MM:SS' string format."""
|
|
seconds = int(seconds)
|
|
minutes = seconds // 60
|
|
seconds_remainder = seconds % 60
|
|
return f"{minutes:02d}:{seconds_remainder:02d}"
|
|
|
|
|
|
def get_progress_bar_rect(screen_width, screen_height):
|
|
width = screen_width
|
|
height = 5
|
|
x = (screen_width - width) / 2
|
|
y = screen_height - height
|
|
return pr.Rectangle(x, y, width, height)
|
|
|
|
|
|
def draw_progress_bar(rect, current_time, total_time):
|
|
if total_time > 0:
|
|
progress_ratio = current_time / total_time
|
|
else:
|
|
progress_ratio = 0.0
|
|
|
|
pr.draw_rectangle_rec(rect, pr.Color(100, 100, 100, 255))
|
|
progress_width = rect.width * progress_ratio
|
|
pr.draw_rectangle(
|
|
int(rect.x),
|
|
int(rect.y),
|
|
int(progress_width),
|
|
int(rect.height),
|
|
pr.Color(200, 50, 50, 255),
|
|
)
|
|
# pr.draw_rectangle_lines_ex(rect, 2, pr.Color(50, 50, 50, 255))
|
|
|
|
time_text = f"{format_time_mm_ss(current_time)} / {format_time_mm_ss(total_time)}"
|
|
text_width = pr.measure_text(time_text, int(rect.height * 0.7))
|
|
# pr.draw_text(
|
|
# time_text,
|
|
# int(rect.x + rect.width / 2 - text_width / 2),
|
|
# int(rect.y + rect.height * 0.15),
|
|
# int(rect.height * 0.7),
|
|
# pr.WHITE,
|
|
# )
|
|
|
|
|
|
# Initialization
|
|
pr.set_config_flags(pr.ConfigFlags.FLAG_WINDOW_RESIZABLE)
|
|
# pr.set_config_flags(pr.FLAG_MSAA_4X_HINT)
|
|
pr.init_window(state["screen_width"], state["screen_height"], "UgPod")
|
|
pr.set_target_fps(TARGET_FPS)
|
|
|
|
player = GaplessPlayer()
|
|
|
|
print("add queue")
|
|
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], , server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("58822c0fc47ec63ba798ba4f04ea3cf3")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("6382005f9dbae8d187d80a5cdca3e7a6")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("a5d2453e07a4998ea20e957c44f90be6")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("398d481a7b85287ad200578b5ab997b0")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("f9f32ca67be7f83139cee3c66e1e4965")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("2f651e103b1fd22ea2f202d6f3398b36")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("164b95968ab1a725fff060fa8c351cc8")["Id"], server["AccessToken"], server["UserId"]))
|
|
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("38a6c21561f54d284a6acad89a3ea8b0")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("631aeddb0557fef65f49463abb20ad7f")["Id"], server["AccessToken"], server["UserId"]))
|
|
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("3d611c8664c5b2072edbf46da2a76c89")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("66559c40d5904944a3f97198d0297894")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("84b75eeb5c8e862d002bae05d2671b1b")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("7ef66992426093252696e1d8666a22e4")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("f37982227942d3df031381e653ec5790")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("0e8fc5fcf119de0439f5a15a4f255c5c")["Id"], server["AccessToken"], server["UserId"]))
|
|
|
|
print(client.jellyfin.get_item("dab6efb24bb2372794d2b4fb53a12376"))
|
|
# player.add_to_queue(
|
|
# Song(
|
|
# "music/pink floyd/dark side of the moon/01 Speak to Me.flac",
|
|
# "Speak to Me",
|
|
# "The Dark Side Of The Moon",
|
|
# "music/albumcover.png",
|
|
# "Pink Floyd",
|
|
# )
|
|
# )
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("99067e877d91be1a66eb5a7ff2f4128f"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("916eda422f48efd8705f29e0600a3e60"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("5e1067d59ed98979ad12a58548b27b83"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("8bcf8240d12aa5c3b14dc3b57f32fef7"), server
|
|
)
|
|
)
|
|
|
|
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("dab6efb24bb2372794d2b4fb53a12376"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("58822c0fc47ec63ba798ba4f04ea3cf3"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("6382005f9dbae8d187d80a5cdca3e7a6"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("a5d2453e07a4998ea20e957c44f90be6"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("398d481a7b85287ad200578b5ab997b0"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("f9f32ca67be7f83139cee3c66e1e4965"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("2f651e103b1fd22ea2f202d6f3398b36"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("164b95968ab1a725fff060fa8c351cc8"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("38a6c21561f54d284a6acad89a3ea8b0"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("631aeddb0557fef65f49463abb20ad7f"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("3d611c8664c5b2072edbf46da2a76c89"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("66559c40d5904944a3f97198d0297894"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("84b75eeb5c8e862d002bae05d2671b1b"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("7ef66992426093252696e1d8666a22e4"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("f37982227942d3df031381e653ec5790"), server
|
|
)
|
|
)
|
|
player.add_to_queue(
|
|
song_data_to_Song(
|
|
client.jellyfin.get_item("0e8fc5fcf119de0439f5a15a4f255c5c"), server
|
|
)
|
|
)
|
|
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/01 Speak to Me.flac')#(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("99067e877d91be1a66eb5a7ff2f4128f")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/02 Breathe (In the Air).flac')#(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("916eda422f48efd8705f29e0600a3e60")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/03 On the Run.flac')#(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("5e1067d59ed98979ad12a58548b27b83")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/04 Time.flac')#(build_jellyfin_audio_url(server["address"], client.jellyfin.get_item("8bcf8240d12aa5c3b14dc3b57f32fef7")["Id"], server["AccessToken"], server["UserId"]))
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/05 The Great Gig in the Sky.flac')
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/06 Money.flac')
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/07 Us and Them.flac')
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/08 Any Colour You Like.flac')
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/09 Brain Damage.flac')
|
|
# player.add_to_queue('music/pink floyd/dark side of the moon/10 Eclipse.flac')
|
|
print("add queue done")
|
|
|
|
current_path = None
|
|
texture = None
|
|
|
|
|
|
def load_texture(path):
|
|
global texture, current_path
|
|
|
|
if not path:
|
|
return
|
|
|
|
if path == current_path:
|
|
return
|
|
|
|
if texture is not None:
|
|
pr.unload_texture(texture)
|
|
|
|
texture = pr.load_texture(path)
|
|
current_path = path
|
|
|
|
|
|
def draw_play_pause_button(pos: pr.Vector2, size: pr.Vector2, is_playing: bool) -> bool:
|
|
clicked = False
|
|
|
|
rect = pr.Rectangle(pos.x, pos.y, size.x, size.y)
|
|
|
|
# Optional hover background
|
|
if pr.check_collision_point_rec(pr.get_mouse_position(), rect):
|
|
pr.draw_rectangle_rec(rect, pr.fade(pr.BLACK, 0.4))
|
|
if pr.is_mouse_button_pressed(pr.MOUSE_LEFT_BUTTON):
|
|
clicked = True
|
|
|
|
cx = pos.x + size.x / 2
|
|
cy = pos.y + size.y / 2
|
|
|
|
icon_padding = size.x * 0.25
|
|
icon_size = size.x - icon_padding * 2
|
|
|
|
if is_playing:
|
|
# PAUSE (two bars centered, same visual weight as play)
|
|
bar_width = icon_size * 0.25
|
|
bar_height = icon_size
|
|
|
|
left_x = cx - bar_width - bar_width * 0.4
|
|
right_x = cx + bar_width * 0.4
|
|
top_y = cy - bar_height / 2
|
|
|
|
pr.draw_rectangle(
|
|
int(left_x),
|
|
int(top_y),
|
|
int(bar_width),
|
|
int(bar_height),
|
|
pr.WHITE,
|
|
)
|
|
pr.draw_rectangle(
|
|
int(right_x),
|
|
int(top_y),
|
|
int(bar_width),
|
|
int(bar_height),
|
|
pr.WHITE,
|
|
)
|
|
else:
|
|
# PLAY (centered triangle)
|
|
p1 = pr.Vector2(cx - icon_size / 2, cy - icon_size / 2)
|
|
p2 = pr.Vector2(cx - icon_size / 2, cy + icon_size / 2)
|
|
p3 = pr.Vector2(cx + icon_size / 2, cy)
|
|
|
|
pr.draw_triangle(p1, p2, p3, pr.WHITE)
|
|
|
|
return clicked
|
|
|
|
|
|
title = ScrollingText(
|
|
text="Aphex Twin - Xtal",
|
|
font_size=15,
|
|
speed=35,
|
|
pause_time=1.2,
|
|
)
|
|
|
|
# --- Main Game Loop ---
|
|
while not pr.window_should_close():
|
|
# 1. Update
|
|
current_width = pr.get_screen_width()
|
|
current_height = pr.get_screen_height()
|
|
|
|
if pr.is_key_pressed(pr.KeyboardKey.KEY_SPACE):
|
|
if player.playing:
|
|
player.pause()
|
|
else:
|
|
player.play()
|
|
if pr.is_key_pressed(pr.KeyboardKey.KEY_LEFT):
|
|
player.seek(player.position - 5)
|
|
if pr.is_key_pressed(pr.KeyboardKey.KEY_RIGHT):
|
|
player.seek(player.position + 5)
|
|
|
|
pr.begin_drawing()
|
|
pr.clear_background(pr.Color(40, 40, 40, 255))
|
|
dt = pr.get_frame_time()
|
|
|
|
progress_rect = get_progress_bar_rect(current_width, current_height)
|
|
|
|
pr.draw_text(
|
|
"UgPod",
|
|
int(current_width * 0.05),
|
|
int(current_height * 0.05),
|
|
int(current_height * 0.05),
|
|
pr.SKYBLUE,
|
|
)
|
|
|
|
current_song = player.get_current_song()
|
|
|
|
max_size = int(current_height * 0.075)
|
|
|
|
if current_song:
|
|
load_texture(current_song.album_cover_path)
|
|
title_size = pr.Vector2(current_width-int(current_width * 0.05 + max_size * 1.1), 30)
|
|
title.update(dt,title_size)
|
|
title.set_text(f"{current_song.name} - {current_song.artist_name}")
|
|
title.draw(pr.Vector2(int(current_width * 0.05 + max_size * 1.1),int(current_height * 0.8)),title_size)
|
|
# pr.draw_text(
|
|
# ,
|
|
# ,
|
|
# int(current_height * 0.03),
|
|
# pr.WHITE,
|
|
# )
|
|
draw_progress_bar(
|
|
progress_rect,
|
|
player.position,
|
|
current_song.duration,
|
|
)
|
|
if texture is not None:
|
|
scale = min(max_size / texture.width, max_size / texture.height)
|
|
|
|
dest_rect = pr.Rectangle(
|
|
int(current_width * 0.05),
|
|
int(current_height * 0.8),
|
|
texture.width * scale,
|
|
texture.height * scale,
|
|
)
|
|
|
|
src_rect = pr.Rectangle(0, 0, texture.width, texture.height)
|
|
|
|
pr.draw_texture_pro(
|
|
texture, src_rect, dest_rect, pr.Vector2(0, 0), 0.0, pr.WHITE
|
|
)
|
|
|
|
pos = pr.Vector2(current_width * 0.5 - current_height * 0.05, current_height * 0.9-progress_rect.height)
|
|
size = pr.Vector2(current_height * 0.1, current_height * 0.1)
|
|
|
|
if draw_play_pause_button(pos, size, player.playing):
|
|
if player.playing:
|
|
player.pause()
|
|
else:
|
|
player.play()
|
|
pr.end_drawing()
|
|
|
|
# Cleanup
|
|
if texture is not None:
|
|
pr.unload_texture(texture)
|
|
|
|
pr.close_window()
|