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
from dataclasses import dataclass, field
from enum import auto, Enum
from typing import Any, Optional
from typing import Any, Optional, NamedTuple
# PyQt imports
from PyQt6.QtCore import pyqtSignal, QObject
@ -65,3 +65,8 @@ class TrackFileData:
obsolete_path: Optional[str] = None
tags: dict[str, Any] = 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 (
MusicMusterSignals,
TrackFileData,
TrackInfo,
)
from config import Config
from dialogs import TrackSelectDialog, ReplaceFilesDialog
from helpers import file_is_unreadable
from log import log
from models import db, Playdates, PlaylistRows, Playlists, Settings, Tracks
from playlistmodel import PlaylistModel, PlaylistProxyModel
@ -150,7 +152,10 @@ class PreviewManager:
def __init__(self) -> None:
mixer.init()
self.intro: Optional[int] = None
self.path: str = ""
self.row_number: Optional[int] = None
self.track_id: int = 0
self.start_time: Optional[dt.datetime] = None
def back(self, ms: int) -> None:
@ -187,17 +192,64 @@ class PreviewManager:
def is_playing(self) -> bool:
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:
mixer.music.play()
self.start_time = dt.datetime.now()
def set_path(self, path) -> None:
self.path = path
mixer.music.load(path)
def restart(self) -> None:
"""
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:
mixer.music.stop()
self.path = ""
self.row_number = None
self.track_id = 0
self.start_time = None
@ -1114,14 +1166,18 @@ class Window(QMainWindow, Ui_MainWindow):
if self.btnPreview.isChecked():
# 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:
track_info = self.active_tab().get_selected_row_track_info()
if not track_info:
# Otherwise get track_id to next track to play
if track_sequence.next:
track_path = track_sequence.next.path
if track_path:
self.preview_manager.set_path(track_path)
self.preview_manager.play()
if track_sequence.next.path:
track_info = TrackInfo(track_sequence.next.track_id,
track_sequence.next.row_number
)
else:
return
self.preview_manager.set_track_info(track_info)
self.preview_manager.play()
else:
self.preview_manager.stop()
@ -1138,8 +1194,8 @@ class Window(QMainWindow, Ui_MainWindow):
def preview_end(self) -> None:
"""Advance preview file to Config.PREVIEW_END_BUFFER_MS before end of intro"""
# TODO
pass
if self.preview_manager:
self.preview_manager.move_to_intro_end()
def preview_fwd(self) -> None:
"""Advance preview file"""
@ -1149,29 +1205,26 @@ class Window(QMainWindow, Ui_MainWindow):
def preview_mark(self) -> None:
"""Set intro time"""
# 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)
if self.preview_manager.is_playing():
track_id = self.preview_manager.track_id
row_number = self.preview_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_manager.get_playtime() / 100) * 100
track.intro = intro
session.commit()
self.preview_manager.set_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"""
# TODO
pass
self.preview_manager.restart()
def rename_playlist(self) -> None:
"""

View File

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