Preview with pygame working

This commit is contained in:
Keith Edmunds 2024-07-03 17:55:09 +01:00
parent e3d7ae8e0f
commit 553376a99e
3 changed files with 103 additions and 41 deletions

View File

@ -1,7 +1,7 @@
# Standard library imports # Standard library imports
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import auto, Enum from enum import auto, Enum
from typing import Any, Optional from typing import Any, Optional, NamedTuple
# PyQt imports # PyQt imports
from PyQt6.QtCore import pyqtSignal, QObject from PyQt6.QtCore import pyqtSignal, QObject
@ -65,3 +65,8 @@ class TrackFileData:
obsolete_path: Optional[str] = None obsolete_path: Optional[str] = None
tags: dict[str, Any] = field(default_factory=dict) tags: dict[str, Any] = field(default_factory=dict)
audio_metadata: dict[str, str | int | float] = field(default_factory=dict) audio_metadata: dict[str, str | int | float] = field(default_factory=dict)
class TrackInfo(NamedTuple):
track_id: int
row_number: int

View File

@ -48,9 +48,11 @@ import stackprinter # type: ignore
from classes import ( from classes import (
MusicMusterSignals, MusicMusterSignals,
TrackFileData, TrackFileData,
TrackInfo,
) )
from config import Config from config import Config
from dialogs import TrackSelectDialog, ReplaceFilesDialog from dialogs import TrackSelectDialog, ReplaceFilesDialog
from helpers import file_is_unreadable
from log import log from log import log
from models import db, Playdates, PlaylistRows, Playlists, Settings, Tracks from models import db, Playdates, PlaylistRows, Playlists, Settings, Tracks
from playlistmodel import PlaylistModel, PlaylistProxyModel from playlistmodel import PlaylistModel, PlaylistProxyModel
@ -150,7 +152,10 @@ class PreviewManager:
def __init__(self) -> None: def __init__(self) -> None:
mixer.init() mixer.init()
self.intro: Optional[int] = None
self.path: str = "" self.path: str = ""
self.row_number: Optional[int] = None
self.track_id: int = 0
self.start_time: Optional[dt.datetime] = None self.start_time: Optional[dt.datetime] = None
def back(self, ms: int) -> None: def back(self, ms: int) -> None:
@ -187,17 +192,64 @@ class PreviewManager:
def is_playing(self) -> bool: def is_playing(self) -> bool:
return mixer.music.get_busy() return mixer.music.get_busy()
def move_to_intro_end(self) -> None:
"""
Move play position to 'buffer' milliseconds before end of intro.
If no intro defined, do nothing.
"""
if self.intro is None:
return
position = max(0, self.intro - Config.PREVIEW_END_BUFFER_MS) / 1000
mixer.music.set_pos(position)
self.start_time = dt.datetime.now() - dt.timedelta(seconds=position)
def play(self) -> None: def play(self) -> None:
mixer.music.play() mixer.music.play()
self.start_time = dt.datetime.now() self.start_time = dt.datetime.now()
def set_path(self, path) -> None: def restart(self) -> None:
self.path = path """
mixer.music.load(path) Restart player from beginning
"""
if not mixer.music.get_busy():
return
mixer.music.set_pos(0)
self.start_time = dt.datetime.now()
def set_intro(self, ms: int) -> None:
"""
Set intro time
"""
self.intro = ms
def set_track_info(self, info: TrackInfo) -> None:
self.track_id = info.track_id
self.row_number = info.row_number
with db.Session() as session:
track = session.get(Tracks, self.track_id)
if not track:
raise ValueError(f"PreviewManager: unable to retreive track {self.track_id=}")
self.intro = track.intro
self.path = track.path
# Check file readable
if file_is_unreadable(self.path):
raise ValueError(f"PreviewManager.__init__: {track.path=} unreadable")
mixer.music.load(self.path)
def stop(self) -> None: def stop(self) -> None:
mixer.music.stop() mixer.music.stop()
self.path = "" self.path = ""
self.row_number = None
self.track_id = 0
self.start_time = None self.start_time = None
@ -1114,14 +1166,18 @@ class Window(QMainWindow, Ui_MainWindow):
if self.btnPreview.isChecked(): if self.btnPreview.isChecked():
# Get track path for first selected track if there is one # Get track path for first selected track if there is one
track_path = self.active_tab().get_selected_row_track_path() track_info = self.active_tab().get_selected_row_track_info()
if not track_path: if not track_info:
# Otherwise get track_id to next track to play # Otherwise get track_id to next track to play
if track_sequence.next: if track_sequence.next:
track_path = track_sequence.next.path if track_sequence.next.path:
if track_path: track_info = TrackInfo(track_sequence.next.track_id,
self.preview_manager.set_path(track_path) track_sequence.next.row_number
self.preview_manager.play() )
else:
return
self.preview_manager.set_track_info(track_info)
self.preview_manager.play()
else: else:
self.preview_manager.stop() self.preview_manager.stop()
@ -1138,8 +1194,8 @@ class Window(QMainWindow, Ui_MainWindow):
def preview_end(self) -> None: def preview_end(self) -> None:
"""Advance preview file to Config.PREVIEW_END_BUFFER_MS before end of intro""" """Advance preview file to Config.PREVIEW_END_BUFFER_MS before end of intro"""
# TODO if self.preview_manager:
pass self.preview_manager.move_to_intro_end()
def preview_fwd(self) -> None: def preview_fwd(self) -> None:
"""Advance preview file""" """Advance preview file"""
@ -1149,29 +1205,26 @@ class Window(QMainWindow, Ui_MainWindow):
def preview_mark(self) -> None: def preview_mark(self) -> None:
"""Set intro time""" """Set intro time"""
# TODO if self.preview_manager.is_playing():
pass track_id = self.preview_manager.track_id
# if self.preview_track_manager: row_number = self.preview_manager.row_number
# track_id = self.preview_track_manager.track_id with db.Session() as session:
# row_number = self.preview_track_manager.row_number track = session.get(Tracks, track_id)
# with db.Session() as session: if track:
# track = session.get(Tracks, track_id) # Save intro as millisends rounded to nearest 0.1
# if track: # second because editor spinbox only resolves to 0.1
# # Save intro as millisends rounded to nearest 0.1 # seconds
# # second because editor spinbox only resolves to 0.1 intro = round(self.preview_manager.get_playtime() / 100) * 100
# # seconds track.intro = intro
# intro = round(self.preview_track_manager.time_playing() / 100) * 100 session.commit()
# track.intro = intro self.preview_manager.set_intro(intro)
# session.commit() self.active_tab().source_model.refresh_row(session, row_number)
# self.preview_track_manager.intro = intro self.active_tab().source_model.invalidate_row(row_number)
# self.active_tab().source_model.refresh_row(session, row_number)
# self.active_tab().source_model.invalidate_row(row_number)
def preview_start(self) -> None: def preview_start(self) -> None:
"""Restart preview""" """Restart preview"""
# TODO self.preview_manager.restart()
pass
def rename_playlist(self) -> None: def rename_playlist(self) -> None:
""" """

View File

@ -35,7 +35,7 @@ from PyQt6.QtWidgets import (
# Third party imports # Third party imports
# App imports # App imports
from classes import Col, MusicMusterSignals from classes import Col, MusicMusterSignals, TrackInfo
from config import Config from config import Config
from dialogs import TrackSelectDialog from dialogs import TrackSelectDialog
from helpers import ( from helpers import (
@ -644,22 +644,26 @@ class PlaylistTab(QTableView):
self.source_model.delete_rows(self.selected_model_row_numbers()) self.source_model.delete_rows(self.selected_model_row_numbers())
self.clear_selection() self.clear_selection()
def get_selected_row_track_path(self) -> str: def get_selected_row_track_info(self) -> Optional[TrackInfo]:
""" """
Return the path of the selected row. If no row selected or selected Return the track_path, track_id and row number of the selected
row does not have a track, return empty string. row. If no row selected or selected row does not have a track,
return None.
""" """
log.debug("get_selected_row_track_path() called") selected_row = self.get_selected_row()
if selected_row is None:
return None
model_row_number = self.source_model_selected_row_number() model_row_number = self.source_model_selected_row_number()
if model_row_number is None: if model_row_number is None:
result = "" return None
else: else:
result = self.source_model.get_row_track_path(model_row_number) track_id = self.source_model.get_row_track_id(model_row_number)
if not track_id:
log.debug(f"get_selected_row_track_path() returned: {result=}") return None
return result else:
return TrackInfo(track_id, selected_row)
def get_selected_row(self) -> Optional[int]: def get_selected_row(self) -> Optional[int]:
""" """