WIP: preview via pygame working
This commit is contained in:
parent
a971298982
commit
9656bac49f
2
.envrc
2
.envrc
@ -4,6 +4,8 @@ export MAIL_PORT=587
|
||||
export MAIL_SERVER="smtp.fastmail.com"
|
||||
export MAIL_USERNAME="kae@midnighthax.com"
|
||||
export MAIL_USE_TLS=True
|
||||
export PYGAME_HIDE_SUPPORT_PROMPT=1
|
||||
|
||||
branch=$(git branch --show-current)
|
||||
|
||||
# Always treat running from /home/kae/mm as production
|
||||
|
||||
@ -39,6 +39,7 @@ from PyQt6.QtWidgets import (
|
||||
|
||||
# Third party imports
|
||||
import pipeclient
|
||||
from pygame import mixer
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm.session import Session
|
||||
import stackprinter # type: ignore
|
||||
@ -142,6 +143,46 @@ class ImportTrack(QObject):
|
||||
self.import_finished.emit()
|
||||
|
||||
|
||||
class PreviewManager:
|
||||
"""
|
||||
Manage track preview player
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
mixer.init()
|
||||
self.path: str = ""
|
||||
self.start_time: Optional[dt.datetime] = None
|
||||
|
||||
def get_playtime(self) -> int:
|
||||
"""
|
||||
Return time since track started in milliseconds, 0 if not playing
|
||||
"""
|
||||
|
||||
if not mixer.music.get_busy():
|
||||
return 0
|
||||
|
||||
if not self.start_time:
|
||||
return 0
|
||||
|
||||
return int((dt.datetime.now() - self.start_time).total_seconds() * 1000)
|
||||
|
||||
def is_playing(self) -> bool:
|
||||
return mixer.music.get_busy()
|
||||
|
||||
def play(self) -> None:
|
||||
mixer.music.play()
|
||||
self.start_time = dt.datetime.now()
|
||||
|
||||
def set_path(self, path) -> None:
|
||||
self.path = path
|
||||
mixer.music.load(path)
|
||||
|
||||
def stop(self) -> None:
|
||||
mixer.music.stop()
|
||||
self.path = ""
|
||||
self.start_time = None
|
||||
|
||||
|
||||
class Window(QMainWindow, Ui_MainWindow):
|
||||
def __init__(self, parent=None, *args, **kwargs) -> None:
|
||||
super().__init__(parent)
|
||||
@ -159,6 +200,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.txtSearch.setHidden(True)
|
||||
self.statusbar.addWidget(self.txtSearch)
|
||||
self.hide_played_tracks = False
|
||||
self.preview_manager = PreviewManager()
|
||||
|
||||
self.widgetFadeVolume.hideAxis("bottom")
|
||||
self.widgetFadeVolume.hideAxis("left")
|
||||
@ -288,7 +330,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
current_track_playlist_id = track_sequence.current.playlist_id
|
||||
if current_track_playlist_id:
|
||||
if closing_tab_playlist_id == current_track_playlist_id:
|
||||
helpers.show_OK(self, "Current track", "Can't close current track playlist")
|
||||
helpers.show_OK(
|
||||
self, "Current track", "Can't close current track playlist"
|
||||
)
|
||||
return False
|
||||
|
||||
# Don't close next track playlist
|
||||
@ -296,7 +340,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
next_track_playlist_id = track_sequence.next.playlist_id
|
||||
if next_track_playlist_id:
|
||||
if closing_tab_playlist_id == next_track_playlist_id:
|
||||
helpers.show_OK(self, "Next track", "Can't close next track playlist")
|
||||
helpers.show_OK(
|
||||
self, "Next track", "Can't close next track playlist"
|
||||
)
|
||||
return False
|
||||
|
||||
# Record playlist as closed and update remaining playlist tabs
|
||||
@ -1043,42 +1089,23 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def preview(self) -> None:
|
||||
"""
|
||||
Preview selected or next track.
|
||||
Preview selected or next track. We use a different mechanism to
|
||||
normal track playing so that the user can route the output audio
|
||||
differently (eg, to headphones).
|
||||
"""
|
||||
|
||||
if self.btnPreview.isChecked():
|
||||
# Get track_id for first selected track if there is one
|
||||
track_id = None
|
||||
row_number_and_track_id = self.active_tab().get_selected_row_and_track_id()
|
||||
if row_number_and_track_id:
|
||||
row_number, track_id = row_number_and_track_id
|
||||
else:
|
||||
# Get track path for first selected track if there is one
|
||||
track_path = self.active_tab().get_selected_row_track_path()
|
||||
if not track_path:
|
||||
# Otherwise get track_id to next track to play
|
||||
if track_sequence.next:
|
||||
track_id = track_sequence.next.track_id
|
||||
row_number = track_sequence.next.row_number
|
||||
if not track_id or row_number is None:
|
||||
self.btnPreview.setChecked(False)
|
||||
return
|
||||
|
||||
try:
|
||||
with db.Session() as session:
|
||||
self.preview_track_manager = PreviewTrackManager(
|
||||
session=session, track_id=track_id, row_number=row_number
|
||||
)
|
||||
self.preview_track_manager.play()
|
||||
except ValueError as e:
|
||||
log.error(f"Error creating PreviewTrackManager({str(e)})")
|
||||
return
|
||||
|
||||
track_path = track_sequence.next.path
|
||||
if track_path:
|
||||
self.preview_manager.set_path(track_path)
|
||||
self.preview_manager.play()
|
||||
else:
|
||||
if self.preview_track_manager:
|
||||
self.preview_track_manager.stop()
|
||||
self.preview_track_manager = None
|
||||
self.label_intro_timer.setText("0.0")
|
||||
self.label_intro_timer.setStyleSheet("")
|
||||
self.btnPreviewMark.setEnabled(False)
|
||||
self.btnPreviewArm.setChecked(False)
|
||||
self.preview_manager.stop()
|
||||
|
||||
def preview_arm(self):
|
||||
"""Manager arm button for setting intro length"""
|
||||
@ -1088,45 +1115,47 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def preview_back(self) -> None:
|
||||
"""Wind back preview file"""
|
||||
|
||||
if self.preview_track_manager:
|
||||
self.preview_track_manager.move_back()
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def preview_end(self) -> None:
|
||||
"""Advance preview file to Config.PREVIEW_END_BUFFER_MS before end of intro"""
|
||||
|
||||
if self.preview_track_manager:
|
||||
self.preview_track_manager.move_to_intro_end()
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def preview_fwd(self) -> None:
|
||||
"""Advance preview file"""
|
||||
|
||||
if self.preview_track_manager:
|
||||
self.preview_track_manager.move_forward()
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def preview_mark(self) -> None:
|
||||
"""Set intro time"""
|
||||
|
||||
if self.preview_track_manager:
|
||||
track_id = self.preview_track_manager.track_id
|
||||
row_number = self.preview_track_manager.row_number
|
||||
with db.Session() as session:
|
||||
track = session.get(Tracks, track_id)
|
||||
if track:
|
||||
# Save intro as millisends rounded to nearest 0.1
|
||||
# second because editor spinbox only resolves to 0.1
|
||||
# seconds
|
||||
intro = round(self.preview_track_manager.time_playing() / 100) * 100
|
||||
track.intro = intro
|
||||
session.commit()
|
||||
self.preview_track_manager.intro = intro
|
||||
self.active_tab().source_model.refresh_row(session, row_number)
|
||||
self.active_tab().source_model.invalidate_row(row_number)
|
||||
# TODO
|
||||
pass
|
||||
# if self.preview_track_manager:
|
||||
# track_id = self.preview_track_manager.track_id
|
||||
# row_number = self.preview_track_manager.row_number
|
||||
# with db.Session() as session:
|
||||
# track = session.get(Tracks, track_id)
|
||||
# if track:
|
||||
# # Save intro as millisends rounded to nearest 0.1
|
||||
# # second because editor spinbox only resolves to 0.1
|
||||
# # seconds
|
||||
# intro = round(self.preview_track_manager.time_playing() / 100) * 100
|
||||
# track.intro = intro
|
||||
# session.commit()
|
||||
# self.preview_track_manager.intro = intro
|
||||
# self.active_tab().source_model.refresh_row(session, row_number)
|
||||
# self.active_tab().source_model.invalidate_row(row_number)
|
||||
|
||||
def preview_start(self) -> None:
|
||||
"""Restart preview"""
|
||||
|
||||
if self.preview_track_manager:
|
||||
self.preview_track_manager.restart()
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def rename_playlist(self) -> None:
|
||||
"""
|
||||
@ -1477,23 +1506,23 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
pass
|
||||
|
||||
# Ensure preview button is reset if preview finishes playing
|
||||
if self.preview_track_manager:
|
||||
self.btnPreview.setChecked(self.preview_track_manager.is_playing())
|
||||
|
||||
# Update preview timer
|
||||
if self.preview_track_manager.is_playing():
|
||||
playtime = self.preview_track_manager.time_playing()
|
||||
# Update preview timer
|
||||
if self.btnPreview.isChecked():
|
||||
if self.preview_manager.is_playing():
|
||||
self.btnPreview.setChecked(True)
|
||||
playtime = self.preview_manager.get_playtime()
|
||||
self.label_intro_timer.setText(f"{playtime / 1000:.1f}")
|
||||
if self.preview_track_manager.time_remaining_intro() <= 50:
|
||||
self.label_intro_timer.setStyleSheet(
|
||||
f"background: {Config.COLOUR_WARNING_TIMER}"
|
||||
)
|
||||
else:
|
||||
self.label_intro_timer.setStyleSheet("")
|
||||
else:
|
||||
self.label_intro_timer.setText("0.0")
|
||||
self.label_intro_timer.setStyleSheet("")
|
||||
self.btnPreview.setChecked(False)
|
||||
# if self.preview_track_manager.time_remaining_intro() <= 50:
|
||||
# self.label_intro_timer.setStyleSheet(
|
||||
# f"background: {Config.COLOUR_WARNING_TIMER}"
|
||||
# )
|
||||
# else:
|
||||
# self.label_intro_timer.setStyleSheet("")
|
||||
else:
|
||||
self.btnPreview.setChecked(False)
|
||||
self.label_intro_timer.setText("0.0")
|
||||
self.label_intro_timer.setStyleSheet("")
|
||||
self.btnPreview.setChecked(False)
|
||||
|
||||
def tick_1000ms(self) -> None:
|
||||
"""
|
||||
|
||||
@ -644,26 +644,6 @@ class PlaylistTab(QTableView):
|
||||
self.source_model.delete_rows(self.selected_model_row_numbers())
|
||||
self.clear_selection()
|
||||
|
||||
def get_selected_row_and_track_id(self) -> Optional[tuple[int, int]]:
|
||||
"""
|
||||
Return the (row_number, track_id) of the selected row. If no row selected or selected
|
||||
row does not have a track, return None.
|
||||
"""
|
||||
|
||||
row_number = self.source_model_selected_row_number()
|
||||
if row_number is None:
|
||||
result = None
|
||||
else:
|
||||
track_id = self.source_model.get_row_track_id(row_number)
|
||||
if not track_id:
|
||||
result = None
|
||||
else:
|
||||
result = (row_number, track_id)
|
||||
|
||||
log.debug(f"get_selected_row_and_track_id() returned: {result=}")
|
||||
|
||||
return result
|
||||
|
||||
def get_selected_row_track_path(self) -> str:
|
||||
"""
|
||||
Return the path of the selected row. If no row selected or selected
|
||||
|
||||
68
poetry.lock
generated
68
poetry.lock
generated
@ -1253,6 +1253,72 @@ files = [
|
||||
{file = "pyfzf-0.3.1.tar.gz", hash = "sha256:dd902e34cffeca9c3082f96131593dd20b4b3a9bba5b9dde1b0688e424b46bd2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygame"
|
||||
version = "2.6.0"
|
||||
description = "Python Game Development"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "pygame-2.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e5707aa9d029752495b3eddc1edff62e0e390a02f699b0f1ce77fe0b8c70ea4f"},
|
||||
{file = "pygame-2.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3ed0547368733b854c0d9981c982a3cdfabfa01b477d095c57bf47f2199da44"},
|
||||
{file = "pygame-2.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6050f3e95f1f16602153d616b52619c6a2041cee7040eb529f65689e9633fc3e"},
|
||||
{file = "pygame-2.6.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89be55b7e9e22e0eea08af9d6cfb97aed5da780f0b3a035803437d481a16d972"},
|
||||
{file = "pygame-2.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d65fb222eea1294cfc8206d9e5754d476a1673eb2783c03c4f70e0455320274"},
|
||||
{file = "pygame-2.6.0-cp310-cp310-win32.whl", hash = "sha256:71eebb9803cb350298de188fb7cdd3ebf13299f78d59a71c7e81efc649aae348"},
|
||||
{file = "pygame-2.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:1551852a2cd5b4139a752888f6cbeeb4a96fc0fe6e6f3f8b9d9784eb8fceab13"},
|
||||
{file = "pygame-2.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f6e5e6c010b1bf429388acf4d41d7ab2f7ad8fbf241d0db822102d35c9a2eb84"},
|
||||
{file = "pygame-2.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:99902f4a2f6a338057200d99b5120a600c27a9f629ca012a9b0087c045508d08"},
|
||||
{file = "pygame-2.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a284664978a1989c1e31a0888b2f70cfbcbafdfa3bb310e750b0d3366416225"},
|
||||
{file = "pygame-2.6.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:829623cee298b3dbaa1dd9f52c3051ae82f04cad7708c8c67cb9a1a4b8fd3c0b"},
|
||||
{file = "pygame-2.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6acf7949ed764487d51123f4f3606e8f76b0df167fef12ef73ef423c35fdea39"},
|
||||
{file = "pygame-2.6.0-cp311-cp311-win32.whl", hash = "sha256:3f809560c99bd1fb4716610eca0cd36412528f03da1a63841a347b71d0c604ee"},
|
||||
{file = "pygame-2.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6897ab87f9193510a774a3483e00debfe166f340ca159f544ef99807e2a44ec4"},
|
||||
{file = "pygame-2.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b834711ebc8b9d0c2a5f9bfae4403dd277b2c61bcb689e1aa630d01a1ebcf40a"},
|
||||
{file = "pygame-2.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b5ac288655e8a31a303cc286e79cc57979ed2ba19c3a14042d4b6391c1d3bed2"},
|
||||
{file = "pygame-2.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d666667b7826b0a7921b8ce0a282ba5281dfa106976c1a3b24e32a0af65ad3b1"},
|
||||
{file = "pygame-2.6.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd8848a37a7cee37854c7efb8d451334477c9f8ce7ac339c079e724dc1334a76"},
|
||||
{file = "pygame-2.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:315e7b3c1c573984f549ac5da9778ac4709b3b4e3a4061050d94eab63fa4fe31"},
|
||||
{file = "pygame-2.6.0-cp312-cp312-win32.whl", hash = "sha256:e44bde0840cc21a91c9d368846ac538d106cf0668be1a6030f48df139609d1e8"},
|
||||
{file = "pygame-2.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:1c429824b1f881a7a5ce3b5c2014d3d182aa45a22cea33c8347a3971a5446907"},
|
||||
{file = "pygame-2.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b832200bd8b6fc485e087bf3ef7ec1a21437258536413a5386088f5dcd3a9870"},
|
||||
{file = "pygame-2.6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:098029d01a46ea4e30620dfb7c28a577070b456c8fc96350dde05f85c0bf51b5"},
|
||||
{file = "pygame-2.6.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a858bbdeac5ec473ec9e726c55fb8fbdc2f4aad7c55110e899883738071c7c9b"},
|
||||
{file = "pygame-2.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f908762941fd99e1f66d1211d26383184f6045c45673443138b214bf48a89aa"},
|
||||
{file = "pygame-2.6.0-cp36-cp36m-win32.whl", hash = "sha256:4a63daee99d050f47d6ec7fa7dbd1c6597b8f082cdd58b6918d382d2bc31262d"},
|
||||
{file = "pygame-2.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:ace471b3849d68968e5427fc01166ef5afaf552a5c442fc2c28d3b7226786f55"},
|
||||
{file = "pygame-2.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fea019713d0c89dfd5909225aa933010100035d1cd30e6c936e8b6f00529fb80"},
|
||||
{file = "pygame-2.6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:249dbf2d51d9f0266009a380ccf0532e1a57614a1528bb2f89a802b01d61f93e"},
|
||||
{file = "pygame-2.6.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb51533ee3204e8160600b0de34eaad70eb913a182c94a7777b6051e8fc52f1"},
|
||||
{file = "pygame-2.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f637636a44712e94e5601ec69160a080214626471983dfb0b5b68aa0c61563d"},
|
||||
{file = "pygame-2.6.0-cp37-cp37m-win32.whl", hash = "sha256:e432156b6f346f4cc6cab03ce9657600093390f4c9b10bf458716b25beebfe33"},
|
||||
{file = "pygame-2.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a0194652db7874bdde7dfc69d659ca954544c012e04ae527151325bfb970f423"},
|
||||
{file = "pygame-2.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eae3ee62cc172e268121d5bd9dc406a67094d33517de3a91de3323d6ae23eb02"},
|
||||
{file = "pygame-2.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f6a58b0a5a8740a3c2cf6fc5366888bd4514561253437f093c12a9ab4fb3ecae"},
|
||||
{file = "pygame-2.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c71da36997dc7b9b4ee973fa3a5d4a6cfb2149161b5b1c08b712d2f13a63ccfe"},
|
||||
{file = "pygame-2.6.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b86771801a7fc10d9a62218f27f1d5c13341c3a27394aa25578443a9cd199830"},
|
||||
{file = "pygame-2.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4928f3acf5a9ce5fbab384c21f1245304535ffd5fb167ae92a6b4d3cdb55a3b6"},
|
||||
{file = "pygame-2.6.0-cp38-cp38-win32.whl", hash = "sha256:4faab2df9926c4d31215986536b112f0d76f711cf02f395805f1ff5df8fd55fc"},
|
||||
{file = "pygame-2.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:afbb8d97aed93dfb116fe105603dacb68f8dab05b978a40a9e4ab1b6c1f683fd"},
|
||||
{file = "pygame-2.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d11f3646b53819892f4a731e80b8589a9140343d0d4b86b826802191b241228c"},
|
||||
{file = "pygame-2.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5ef92ed93c354eabff4b85e457d4d6980115004ec7ff52a19fd38b929c3b80fb"},
|
||||
{file = "pygame-2.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bc1795f2e36302882546faacd5a0191463c4f4ae2b90e7c334a7733aa4190d2"},
|
||||
{file = "pygame-2.6.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e92294fcc85c4955fe5bc6a0404e4cc870808005dc8f359e881544e3cc214108"},
|
||||
{file = "pygame-2.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0cb7bdf3ee0233a3ac02ef777c01dfe315e6d4670f1312c83b91c1ef124359a"},
|
||||
{file = "pygame-2.6.0-cp39-cp39-win32.whl", hash = "sha256:ac906478ae489bb837bf6d2ae1eb9261d658aa2c34fa5b283027a04149bda81a"},
|
||||
{file = "pygame-2.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:92cf12a9722f6f0bdc5520d8925a8f085cff9c054a2ea462fc409cba3781be27"},
|
||||
{file = "pygame-2.6.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:a6636f452fdaddf604a060849feb84c056930b6a3c036214f607741f16aac942"},
|
||||
{file = "pygame-2.6.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dc242dc15d067d10f25c5b12a1da48ca9436d8e2d72353eaf757e83612fba2f"},
|
||||
{file = "pygame-2.6.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f82df23598a281c8c342d3c90be213c8fe762a26c15815511f60d0aac6e03a70"},
|
||||
{file = "pygame-2.6.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ed2539bb6bd211fc570b1169dc4a64a74ec5cd95741e62a0ab46bd18fe08e0d"},
|
||||
{file = "pygame-2.6.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:904aaf29710c6b03a7e1a65b198f5467ed6525e8e60bdcc5e90ff8584c1d54ea"},
|
||||
{file = "pygame-2.6.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcd28f96f0fffd28e71a98773843074597e10d7f55a098e2e5bcb2bef1bdcbf5"},
|
||||
{file = "pygame-2.6.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4fad1ab33443ecd4f958dbbb67fc09fcdc7a37e26c34054e3296fb7e26ad641e"},
|
||||
{file = "pygame-2.6.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e909186d4d512add39b662904f0f79b73028fbfc4fbfdaf6f9412aed4e500e9c"},
|
||||
{file = "pygame-2.6.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79abcbf6d12fce51a955a0652ccd50b6d0a355baa27799535eaf21efb43433dd"},
|
||||
{file = "pygame-2.6.0.tar.gz", hash = "sha256:722d33ae676aa8533c1f955eded966411298831346b8d51a77dad22e46ba3e35"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.17.2"
|
||||
@ -1970,4 +2036,4 @@ test = ["websockets"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "3b2e747f93972b78a9a35454810c99c4ec81e14fc9780e65a6a4434a97d1a713"
|
||||
content-hash = "4400e265162fa56d70d4ef5501896dec6f22b743414364ef99bdfef0be979785"
|
||||
|
||||
@ -25,6 +25,7 @@ pyqtgraph = "^0.13.3"
|
||||
colorlog = "^6.8.2"
|
||||
alchemical = "^1.0.2"
|
||||
obs-websocket-py = "^1.0"
|
||||
pygame = "^2.6.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
ipdb = "^0.13.9"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user