Compare commits
4 Commits
4ce6c2e9b9
...
34fa0c92b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34fa0c92b2 | ||
|
|
87f9e1e81b | ||
|
|
a31718d2b9 | ||
|
|
cf4e42358e |
38
app/dbconfig.py
Normal file
38
app/dbconfig.py
Normal file
@ -0,0 +1,38 @@
|
||||
import os
|
||||
import sqlalchemy
|
||||
|
||||
from config import Config
|
||||
from sqlalchemy.orm import (sessionmaker, scoped_session)
|
||||
|
||||
MM_ENV = os.environ.get('MM_ENV', 'PRODUCTION')
|
||||
testing = False
|
||||
|
||||
if MM_ENV == 'PRODUCTION':
|
||||
dbname = os.environ.get('MM_PRODUCTION_DBNAME', 'musicmuster_prod')
|
||||
dbuser = os.environ.get('MM_PRODUCTION_DBUSER', 'musicmuster')
|
||||
dbpw = os.environ.get('MM_PRODUCTION_DBPW', 'musicmuster')
|
||||
dbhost = os.environ.get('MM_PRODUCTION_DBHOST', 'localhost')
|
||||
elif MM_ENV == 'TESTING':
|
||||
dbname = os.environ.get('MM_TESTING_DBNAME', 'musicmuster_testing')
|
||||
dbuser = os.environ.get('MM_TESTING_DBUSER', 'musicmuster_testing')
|
||||
dbpw = os.environ.get('MM_TESTING_DBPW', 'musicmuster_testing')
|
||||
dbhost = os.environ.get('MM_TESTING_DBHOST', 'localhost')
|
||||
testing = True
|
||||
elif MM_ENV == 'DEVELOPMENT':
|
||||
dbname = os.environ.get('MM_DEVELOPMENT_DBNAME', 'musicmuster_dev')
|
||||
dbuser = os.environ.get('MM_DEVELOPMENT_DBUSER', 'musicmuster')
|
||||
dbpw = os.environ.get('MM_DEVELOPMENT_DBPW', 'musicmuster')
|
||||
dbhost = os.environ.get('MM_DEVELOPMENT_DBHOST', 'localhost')
|
||||
else:
|
||||
raise ValueError(f"Unknown MusicMuster environment: {MM_ENV=}")
|
||||
|
||||
MYSQL_CONNECT = f"mysql+mysqldb://{dbuser}:{dbpw}@{dbhost}/{dbname}"
|
||||
|
||||
engine = sqlalchemy.create_engine(
|
||||
MYSQL_CONNECT,
|
||||
encoding='utf-8',
|
||||
echo=Config.DISPLAY_SQL,
|
||||
pool_pre_ping=True
|
||||
)
|
||||
|
||||
session = scoped_session(sessionmaker(bind=engine))
|
||||
156
app/models.py
156
app/models.py
@ -3,7 +3,7 @@
|
||||
import os.path
|
||||
import re
|
||||
|
||||
import sqlalchemy
|
||||
from dbconfig import Session
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
@ -25,8 +25,7 @@ from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import (
|
||||
backref,
|
||||
relationship,
|
||||
sessionmaker,
|
||||
scoped_session, RelationshipProperty
|
||||
RelationshipProperty
|
||||
)
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
|
||||
@ -40,28 +39,8 @@ from helpers import (
|
||||
)
|
||||
from log import DEBUG, ERROR
|
||||
|
||||
# Create session at the global level as per
|
||||
# https://docs.sqlalchemy.org/en/13/orm/session_basics.html
|
||||
# and make objects persistent
|
||||
# https://docs.sqlalchemy.org/en/14/orm/session_state_management.html
|
||||
|
||||
engine = sqlalchemy.create_engine(
|
||||
f"{Config.MYSQL_CONNECT}?charset=utf8",
|
||||
encoding='utf-8',
|
||||
echo=Config.DISPLAY_SQL,
|
||||
pool_pre_ping=True)
|
||||
|
||||
# Create a Session factory
|
||||
Session = scoped_session(sessionmaker(bind=engine))
|
||||
# sm: sessionmaker = sessionmaker(bind=engine) # , expire_on_commit=False)
|
||||
# Session = scoped_session(sm)
|
||||
|
||||
Base: DeclarativeMeta = declarative_base()
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
|
||||
def db_init():
|
||||
return
|
||||
|
||||
|
||||
# Database classes
|
||||
@ -88,7 +67,7 @@ class NoteColours(Base):
|
||||
self.order = order
|
||||
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
@ -160,7 +139,7 @@ class Notes(Base):
|
||||
self.row = row
|
||||
self.note = text
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
@ -173,7 +152,7 @@ class Notes(Base):
|
||||
DEBUG(f"delete_note({self.id=}")
|
||||
|
||||
session.query(Notes).filter_by(id=self.id).delete()
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
@classmethod
|
||||
def get_by_id(cls, session: Session, note_id: int) -> Optional["Notes"]:
|
||||
@ -199,7 +178,7 @@ class Notes(Base):
|
||||
self.row = row
|
||||
if text:
|
||||
self.note = text
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
|
||||
class Playdates(Base):
|
||||
@ -220,7 +199,7 @@ class Playdates(Base):
|
||||
self.track_id = track.id
|
||||
track.update_lastplayed(session)
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
@staticmethod
|
||||
def last_played(session: Session, track_id: int) -> Optional[datetime]:
|
||||
@ -242,9 +221,8 @@ class Playdates(Base):
|
||||
"""
|
||||
|
||||
session.query(Playdates).filter(
|
||||
Playdates.track_id == track_id,
|
||||
).delete()
|
||||
session.commit()
|
||||
Playdates.track_id == track_id).delete()
|
||||
session.flush()
|
||||
|
||||
|
||||
class Playlists(Base):
|
||||
@ -269,7 +247,7 @@ class Playlists(Base):
|
||||
def __init__(self, session: Session, name: str) -> None:
|
||||
self.name = name
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Playlists(id={self.id}, name={self.name}>"
|
||||
@ -297,7 +275,7 @@ class Playlists(Base):
|
||||
|
||||
self.loaded = False
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, session: Session) -> List["Playlists"]:
|
||||
@ -338,25 +316,38 @@ class Playlists(Base):
|
||||
|
||||
self.loaded = True
|
||||
self.last_used = datetime.now()
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
def move_track(self, session: Session, rows: List[int],
|
||||
to_playlist: "Playlists") -> None:
|
||||
"""Move tracks to another playlist"""
|
||||
|
||||
for row in rows:
|
||||
track = self.tracks[row]
|
||||
to_playlist.add_track(session, track.id)
|
||||
del self.tracks[row]
|
||||
|
||||
session.flush()
|
||||
|
||||
def remove_all_tracks(self, session: Session) -> None:
|
||||
"""
|
||||
Remove all tracks from this playlist
|
||||
"""
|
||||
|
||||
session.query(PlaylistTracks).filter(
|
||||
PlaylistTracks.playlist_id == self.id,
|
||||
).delete()
|
||||
session.commit()
|
||||
self.tracks = {}
|
||||
session.flush()
|
||||
|
||||
def remove_track(self, session: Session, row: int) -> None:
|
||||
DEBUG(f"Playlist.remove_track({self.id=}, {row=})")
|
||||
|
||||
session.query(PlaylistTracks).filter(
|
||||
PlaylistTracks.playlist_id == self.id,
|
||||
PlaylistTracks.row == row
|
||||
).delete()
|
||||
# Get tracks collection for this playlist
|
||||
tracks_collections = self.tracks
|
||||
# Tracks are a dictionary of tracks keyed on row
|
||||
# number. Remove the relevant row.
|
||||
del tracks_collections[row]
|
||||
# Save the new tracks collection
|
||||
self.tracks = tracks_collections
|
||||
session.flush()
|
||||
|
||||
|
||||
class PlaylistTracks(Base):
|
||||
@ -373,7 +364,8 @@ class PlaylistTracks(Base):
|
||||
backref=backref(
|
||||
"playlist_tracks",
|
||||
collection_class=attribute_mapped_collection("row"),
|
||||
lazy="joined"
|
||||
lazy="joined",
|
||||
cascade="all, delete-orphan"
|
||||
)
|
||||
)
|
||||
|
||||
@ -386,45 +378,7 @@ class PlaylistTracks(Base):
|
||||
self.track_id = track_id
|
||||
self.row = row
|
||||
session.add(self)
|
||||
session.commit()
|
||||
|
||||
@staticmethod
|
||||
def move_track(
|
||||
session: Session, from_playlist_id: int, row: int,
|
||||
to_playlist_id: int) -> None:
|
||||
"""
|
||||
Move track between playlists. This would be more efficient with
|
||||
an ORM-enabled UPDATE statement, but this works just fine.
|
||||
"""
|
||||
DEBUG(
|
||||
"PlaylistTracks.move_tracks("
|
||||
f"{from_playlist_id=}, {row=}, {to_playlist_id=})"
|
||||
)
|
||||
|
||||
new_row: int
|
||||
max_row: Optional[int] = session.query(
|
||||
func.max(PlaylistTracks.row)).filter(
|
||||
PlaylistTracks.playlist_id == to_playlist_id).scalar()
|
||||
if max_row is None:
|
||||
# Destination playlist is empty; use row 0
|
||||
new_row = 0
|
||||
else:
|
||||
# Destination playlist has tracks; add to end
|
||||
new_row = max_row + 1
|
||||
try:
|
||||
record: PlaylistTracks = session.query(PlaylistTracks).filter(
|
||||
PlaylistTracks.playlist_id == from_playlist_id,
|
||||
PlaylistTracks.row == row).one()
|
||||
except NoResultFound:
|
||||
ERROR(
|
||||
f"No rows matched in query: "
|
||||
f"PlaylistTracks.playlist_id == {from_playlist_id}, "
|
||||
f"PlaylistTracks.row == {row}"
|
||||
)
|
||||
return
|
||||
record.playlist_id = to_playlist_id
|
||||
record.row = new_row
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
@staticmethod
|
||||
def next_free_row(session: Session, playlist: Playlists) -> int:
|
||||
@ -467,14 +421,14 @@ class Settings(Base):
|
||||
int_setting.name = name
|
||||
int_setting.f_int = None
|
||||
session.add(int_setting)
|
||||
session.commit()
|
||||
session.flush()
|
||||
return int_setting
|
||||
|
||||
def update(self, session: Session, data):
|
||||
for key, value in data.items():
|
||||
assert hasattr(self, key)
|
||||
setattr(self, key, value)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
|
||||
class Tracks(Base):
|
||||
@ -497,10 +451,30 @@ class Tracks(Base):
|
||||
back_populates="tracks",
|
||||
lazy="joined")
|
||||
|
||||
def __init__(self, session: Session, path: str) -> None:
|
||||
def __init__(self,
|
||||
session: Session,
|
||||
path: str,
|
||||
title: Optional[str] = None,
|
||||
artist: Optional[str] = None,
|
||||
duration: Optional[int] = None,
|
||||
start_gap: Optional[int] = None,
|
||||
fade_at: Optional[int] = None,
|
||||
silence_at: Optional[int] = None,
|
||||
mtime: Optional[float] = None,
|
||||
lastplayed: Optional[datetime] = None,
|
||||
) -> None:
|
||||
self.path = path
|
||||
self.title = title
|
||||
self.artist = artist
|
||||
self.duration = duration
|
||||
self.start_gap = start_gap
|
||||
self.fade_at = fade_at
|
||||
self.silence_at = silence_at
|
||||
self.mtime = mtime
|
||||
self.lastplayed = lastplayed
|
||||
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
@ -589,7 +563,7 @@ class Tracks(Base):
|
||||
Config.MILLISECOND_SIGFIGS) * 1000
|
||||
self.start_gap = leading_silence(audio)
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
@staticmethod
|
||||
def remove_by_path(session: Session, path: str) -> None:
|
||||
@ -599,7 +573,7 @@ class Tracks(Base):
|
||||
|
||||
try:
|
||||
session.query(Tracks).filter(Tracks.path == path).delete()
|
||||
session.commit()
|
||||
session.flush()
|
||||
except IntegrityError as exception:
|
||||
ERROR(f"Can't remove track with {path=} ({exception=})")
|
||||
|
||||
@ -623,17 +597,17 @@ class Tracks(Base):
|
||||
def update_lastplayed(self, session: Session) -> None:
|
||||
self.lastplayed = datetime.now()
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
def update_artist(self, session: Session, artist: str) -> None:
|
||||
self.artist = artist
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
def update_title(self, session: Session, title: str) -> None:
|
||||
self.title = title
|
||||
session.add(self)
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
def update_path(self, newpath: str) -> None:
|
||||
self.path = newpath
|
||||
|
||||
@ -23,11 +23,12 @@ from PyQt5.QtWidgets import (
|
||||
QMainWindow,
|
||||
)
|
||||
|
||||
import dbconfig
|
||||
import helpers
|
||||
import music
|
||||
|
||||
from config import Config
|
||||
from models import (db_init, Playdates, Playlists, PlaylistTracks,
|
||||
from models import (Playdates, Playlists, PlaylistTracks,
|
||||
Session, Settings, Tracks)
|
||||
from playlists import PlaylistTab
|
||||
from utilities import create_track_from_file
|
||||
@ -101,6 +102,12 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
height = record.f_int or 981
|
||||
self.setGeometry(x, y, width, height)
|
||||
|
||||
@staticmethod
|
||||
def kae():
|
||||
with Session() as session:
|
||||
db = session.bind.engine.url.database
|
||||
print(f"kae(): {db=}")
|
||||
|
||||
@staticmethod
|
||||
def check_audacity() -> None:
|
||||
"""Offer to run Audacity if not running"""
|
||||
@ -973,16 +980,13 @@ class SelectPlaylistDialog(QDialog):
|
||||
self.accept()
|
||||
|
||||
|
||||
def main():
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
Base.metadata.create_all(dbconfig.engine)
|
||||
Session = dbconfig.Session
|
||||
app = QApplication(sys.argv)
|
||||
db_init()
|
||||
win = Window()
|
||||
win.show()
|
||||
sys.exit(app.exec())
|
||||
except Exception:
|
||||
EXCEPTION("Unhandled Exception caught by musicmuster.main()")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@ -27,11 +27,11 @@ from models import (
|
||||
Notes,
|
||||
Playdates,
|
||||
Playlists,
|
||||
Session,
|
||||
Settings,
|
||||
Tracks,
|
||||
NoteColours
|
||||
)
|
||||
from dbconfig import Session
|
||||
|
||||
|
||||
class RowMeta:
|
||||
@ -101,7 +101,7 @@ class PlaylistTab(QTableWidget):
|
||||
self.setHorizontalHeaderItem(7, item)
|
||||
self.horizontalHeader().setMinimumSectionSize(0)
|
||||
|
||||
self._set_column_widths()
|
||||
self._set_column_widths(session)
|
||||
self.setHorizontalHeaderLabels([
|
||||
Config.COLUMN_NAME_AUTOPLAY,
|
||||
Config.COLUMN_NAME_LEADING_SILENCE,
|
||||
@ -1368,7 +1368,7 @@ class PlaylistTab(QTableWidget):
|
||||
ms: int = 0
|
||||
with Session() as session:
|
||||
for row in (sel_rows - notes_rows):
|
||||
ms += self._get_row_track_object(row, session).duration
|
||||
ms += self._get_row_track_object(row, session).duration or 0
|
||||
|
||||
# Only paint message if there are selected track rows
|
||||
if ms > 0:
|
||||
@ -1398,17 +1398,16 @@ class PlaylistTab(QTableWidget):
|
||||
# Reset extended selection
|
||||
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
|
||||
def _set_column_widths(self) -> None:
|
||||
def _set_column_widths(self, session: Session) -> None:
|
||||
"""Column widths from settings"""
|
||||
|
||||
with Session() as session:
|
||||
for column in range(self.columnCount()):
|
||||
name: str = f"playlist_col_{str(column)}_width"
|
||||
record: Settings = Settings.get_int_settings(session, name)
|
||||
if record and record.f_int is not None:
|
||||
self.setColumnWidth(column, record.f_int)
|
||||
else:
|
||||
self.setColumnWidth(column, Config.DEFAULT_COLUMN_WIDTH)
|
||||
for column in range(self.columnCount()):
|
||||
name: str = f"playlist_col_{str(column)}_width"
|
||||
record: Settings = Settings.get_int_settings(session, name)
|
||||
if record and record.f_int is not None:
|
||||
self.setColumnWidth(column, record.f_int)
|
||||
else:
|
||||
self.setColumnWidth(column, Config.DEFAULT_COLUMN_WIDTH)
|
||||
|
||||
def _set_next(self, row: int, session: Session) -> None:
|
||||
"""
|
||||
|
||||
@ -53,7 +53,7 @@ def main():
|
||||
DEBUG("Finished")
|
||||
|
||||
|
||||
def create_track_from_file(session, path, interactive=False):
|
||||
def create_track_from_file(session, path, normalise=None, interactive=False):
|
||||
"""
|
||||
Create track in database from passed path, or update database entry
|
||||
if path already in database.
|
||||
@ -100,7 +100,7 @@ def create_track_from_file(session, path, interactive=False):
|
||||
track.mtime = os.path.getmtime(path)
|
||||
session.commit()
|
||||
|
||||
if Config.NORMALISE_ON_IMPORT:
|
||||
if normalise or normalise is None and Config.NORMALISE_ON_IMPORT:
|
||||
if interactive:
|
||||
INFO("Normalise...")
|
||||
# Check type
|
||||
|
||||
22
conftest.py
22
conftest.py
@ -2,12 +2,12 @@
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
sys.path.append("app")
|
||||
import models
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
|
||||
sys.path.append("app")
|
||||
from app.models import Base # noqa E402
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@ -19,25 +19,9 @@ def connection():
|
||||
return engine.connect()
|
||||
|
||||
|
||||
def seed_database():
|
||||
pass
|
||||
|
||||
# users = [
|
||||
# {
|
||||
# "id": 1,
|
||||
# "name": "John Doe",
|
||||
# },
|
||||
# # ...
|
||||
# ]
|
||||
|
||||
# for user in users:
|
||||
# db_user = User(**user)
|
||||
# db_session.add(db_user)
|
||||
# db_session.commit()
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def setup_database(connection):
|
||||
from app.models import Base # noqa E402
|
||||
Base.metadata.bind = connection
|
||||
Base.metadata.create_all()
|
||||
# seed_database()
|
||||
|
||||
20
poetry.lock
generated
20
poetry.lock
generated
@ -1,6 +1,6 @@
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.7.6"
|
||||
version = "1.7.7"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -439,11 +439,11 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.0.1"
|
||||
version = "7.1.0"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
@ -551,14 +551,14 @@ tests = ["pytest", "typeguard", "pygments", "littleutils", "cython"]
|
||||
|
||||
[[package]]
|
||||
name = "tinytag"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
description = "Read music meta data and length of MP3, OGG, OPUS, MP4, M4A, FLAC, WMA and Wave files"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7"
|
||||
|
||||
[package.extras]
|
||||
tests = ["pytest", "pytest-cov", "coveralls", "flake8"]
|
||||
tests = ["pytest", "pytest-cov", "flake8"]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
@ -610,8 +610,8 @@ content-hash = "08353ac7c54559da365ff26807f5179fefe388b62bc52884ee475a4a644e924a
|
||||
|
||||
[metadata.files]
|
||||
alembic = [
|
||||
{file = "alembic-1.7.6-py3-none-any.whl", hash = "sha256:ad842f2c3ab5c5d4861232730779c05e33db4ba880a08b85eb505e87c01095bc"},
|
||||
{file = "alembic-1.7.6.tar.gz", hash = "sha256:6c0c05e9768a896d804387e20b299880fe01bc56484246b0dffe8075d6d3d847"},
|
||||
{file = "alembic-1.7.7-py3-none-any.whl", hash = "sha256:29be0856ec7591c39f4e1cb10f198045d890e6e2274cf8da80cb5e721a09642b"},
|
||||
{file = "alembic-1.7.7.tar.gz", hash = "sha256:4961248173ead7ce8a21efb3de378f13b8398e6630fab0eb258dc74a8af24c58"},
|
||||
]
|
||||
appnope = [
|
||||
{file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"},
|
||||
@ -940,8 +940,8 @@ pyqtwebengine-qt5 = [
|
||||
{file = "PyQtWebEngine_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:24231f19e1595018779977de6722b5c69f3d03f34a5f7574ff21cd1e764ef76d"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"},
|
||||
{file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"},
|
||||
{file = "pytest-7.1.0-py3-none-any.whl", hash = "sha256:b555252a95bbb2a37a97b5ac2eb050c436f7989993565f5e0c9128fcaacadd0e"},
|
||||
{file = "pytest-7.1.0.tar.gz", hash = "sha256:f1089d218cfcc63a212c42896f1b7fbf096874d045e1988186861a1a87d27b47"},
|
||||
]
|
||||
pytest-qt = [
|
||||
{file = "pytest-qt-4.0.2.tar.gz", hash = "sha256:dfc5240dec7eb43b76bcb5f9a87eecae6ef83592af49f3af5f1d5d093acaa93e"},
|
||||
@ -1001,7 +1001,7 @@ stack-data = [
|
||||
{file = "stack_data-0.2.0.tar.gz", hash = "sha256:45692d41bd633a9503a5195552df22b583caf16f0b27c4e58c98d88c8b648e12"},
|
||||
]
|
||||
tinytag = [
|
||||
{file = "tinytag-1.8.0.tar.gz", hash = "sha256:e222d2fed7e2a26708963d907355ae242cf44908c1175d9d0d21c2ce51d47beb"},
|
||||
{file = "tinytag-1.8.1.tar.gz", hash = "sha256:363ab3107831a5598b68aaa061aba915fb1c7b4254d770232e65d5db8487636d"},
|
||||
]
|
||||
toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
|
||||
@ -126,7 +126,7 @@ def test_playdates_add_playdate(session):
|
||||
assert playdate
|
||||
|
||||
last_played = Playdates.last_played(session, track.id)
|
||||
assert playdate.lastplayed == last_played
|
||||
assert abs((playdate.lastplayed - last_played).total_seconds()) < 2
|
||||
|
||||
|
||||
def test_playdates_remove_track(session):
|
||||
@ -274,6 +274,9 @@ def test_playlist_remove_tracks(session):
|
||||
|
||||
playlist1.remove_track(session, 1)
|
||||
assert len(playlist1.tracks) == 2
|
||||
# Check the track itself still exists
|
||||
original_track = Tracks.get_by_id(session, track1.id)
|
||||
assert original_track
|
||||
|
||||
playlist1.remove_all_tracks(session)
|
||||
assert len(playlist1.tracks) == 0
|
||||
@ -330,8 +333,7 @@ def test_playlisttracks_move_track(session):
|
||||
assert tracks[track2_row] == track2
|
||||
|
||||
# Move track2 to playlist2 and check
|
||||
PlaylistTracks.move_track(
|
||||
session, playlist1.id, track2_row, playlist2.id)
|
||||
playlist1.move_track(session, [track2_row], playlist2)
|
||||
|
||||
tracks1 = playlist1.tracks
|
||||
tracks2 = playlist2.tracks
|
||||
|
||||
@ -1,15 +1,47 @@
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
from app.playlists import Notes, PlaylistTab, Tracks
|
||||
from app.models import Playlists
|
||||
from musicmuster import Window
|
||||
from app import playlists
|
||||
from app import models
|
||||
from app import musicmuster
|
||||
from app import dbconfig
|
||||
|
||||
|
||||
def seed2tracks(session):
|
||||
tracks = [
|
||||
{
|
||||
"path": "testdata/isa.mp3",
|
||||
"title": "I'm so afraid",
|
||||
"artist": "Fleetwood Mac",
|
||||
"duration": 263000,
|
||||
"start_gap": 60,
|
||||
"fade_at": 236263,
|
||||
"silence_at": 260343,
|
||||
"mtime": 371900000,
|
||||
},
|
||||
{
|
||||
"path": "testdata/mom.mp3",
|
||||
"title": "Man of Mystery",
|
||||
"artist": "The Shadows",
|
||||
"duration": 120000,
|
||||
"start_gap": 70,
|
||||
"fade_at": 115000,
|
||||
"silence_at": 118000,
|
||||
"mtime": 1642760000,
|
||||
},
|
||||
]
|
||||
|
||||
for track in tracks:
|
||||
db_track = models.Tracks(session=session, **track)
|
||||
session.add(db_track)
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
def test_init(qtbot, session):
|
||||
"""Just check we can create a playlist_tab"""
|
||||
|
||||
playlist = Playlists(session, "my playlist")
|
||||
playlist_tab = PlaylistTab(None, session, playlist.id)
|
||||
playlist = models.Playlists(session, "my playlist")
|
||||
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
||||
assert playlist_tab
|
||||
|
||||
|
||||
@ -17,46 +49,51 @@ def test_save_and_restore(qtbot, session):
|
||||
"""Playlist with one track, one note, save and restore"""
|
||||
|
||||
# Create playlist
|
||||
playlist = Playlists(session, "my playlist")
|
||||
playlist_tab = PlaylistTab(None, session, playlist.id)
|
||||
playlist = models.Playlists(session, "my playlist")
|
||||
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
||||
|
||||
# Insert a note
|
||||
note_text = "my note"
|
||||
note_row = 7
|
||||
note = Notes(session, playlist.id, note_row, note_text)
|
||||
note = models.Notes(session, playlist.id, note_row, note_text)
|
||||
playlist_tab._insert_note(session, note)
|
||||
|
||||
# Add a track
|
||||
track_path = "/a/b/c"
|
||||
track = Tracks(session, track_path)
|
||||
track = models.Tracks(session, track_path)
|
||||
playlist_tab.insert_track(session, track)
|
||||
|
||||
# Save playlist
|
||||
playlist_tab.save_playlist(session)
|
||||
|
||||
# We need to commit the session before re-querying
|
||||
session.commit()
|
||||
|
||||
# Retrieve playlist
|
||||
playlists = Playlists.get_open(session)
|
||||
assert len(playlists) == 1
|
||||
retrieved_playlist = playlists[0]
|
||||
assert track_path in [a.path for a in retrieved_playlist.tracks.values()]
|
||||
assert note_text in [a.note for a in retrieved_playlist.notes]
|
||||
all_playlists = playlists.Playlists.get_open(session)
|
||||
assert len(all_playlists) == 1
|
||||
retrieved_playlist = all_playlists[0]
|
||||
paths = [a.path for a in retrieved_playlist.tracks.values()]
|
||||
assert track_path in paths
|
||||
notes = [a.note for a in retrieved_playlist.notes]
|
||||
assert note_text in notes
|
||||
|
||||
|
||||
def test_meta_all_clear(qtbot, session):
|
||||
|
||||
# Create playlist
|
||||
playlist = Playlists(session, "my playlist")
|
||||
playlist_tab = PlaylistTab(None, session, playlist.id)
|
||||
playlist = models.Playlists(session, "my playlist")
|
||||
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
||||
|
||||
# Add some tracks
|
||||
track1_path = "/a/b/c"
|
||||
track1 = Tracks(session, track1_path)
|
||||
track1 = models.Tracks(session, track1_path)
|
||||
playlist_tab.insert_track(session, track1)
|
||||
track2_path = "/d/e/f"
|
||||
track2 = Tracks(session, track2_path)
|
||||
track2 = models.Tracks(session, track2_path)
|
||||
playlist_tab.insert_track(session, track2)
|
||||
track3_path = "/h/i/j"
|
||||
track3 = Tracks(session, track3_path)
|
||||
track3 = models.Tracks(session, track3_path)
|
||||
playlist_tab.insert_track(session, track3)
|
||||
|
||||
assert playlist_tab._get_current_track_row() is None
|
||||
@ -69,18 +106,18 @@ def test_meta_all_clear(qtbot, session):
|
||||
def test_meta(qtbot, session):
|
||||
|
||||
# Create playlist
|
||||
playlist = Playlists(session, "my playlist")
|
||||
playlist_tab = PlaylistTab(None, session, playlist.id)
|
||||
playlist = playlists.Playlists(session, "my playlist")
|
||||
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
||||
|
||||
# Add some tracks
|
||||
track1_path = "/a/b/c"
|
||||
track1 = Tracks(session, track1_path)
|
||||
track1 = models.Tracks(session, track1_path)
|
||||
playlist_tab.insert_track(session, track1)
|
||||
track2_path = "/d/e/f"
|
||||
track2 = Tracks(session, track2_path)
|
||||
track2 = models.Tracks(session, track2_path)
|
||||
playlist_tab.insert_track(session, track2)
|
||||
track3_path = "/h/i/j"
|
||||
track3 = Tracks(session, track3_path)
|
||||
track3 = models.Tracks(session, track3_path)
|
||||
playlist_tab.insert_track(session, track3)
|
||||
|
||||
assert len(playlist_tab._get_unreadable_track_rows()) == 3
|
||||
@ -99,7 +136,7 @@ def test_meta(qtbot, session):
|
||||
# Add a note
|
||||
note_text = "my note"
|
||||
note_row = 7 # will be added as row 3
|
||||
note = Notes(session, playlist.id, note_row, note_text)
|
||||
note = models.Notes(session, playlist.id, note_row, note_text)
|
||||
playlist_tab._insert_note(session, note)
|
||||
|
||||
assert playlist_tab._get_played_track_rows() == [0]
|
||||
@ -147,15 +184,15 @@ def test_meta(qtbot, session):
|
||||
|
||||
def test_clear_next(qtbot, session):
|
||||
# Create playlist
|
||||
playlist = Playlists(session, "my playlist")
|
||||
playlist_tab = PlaylistTab(None, session, playlist.id)
|
||||
playlist = models.Playlists(session, "my playlist")
|
||||
playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
|
||||
|
||||
# Add some tracks
|
||||
track1_path = "/a/b/c"
|
||||
track1 = Tracks(session, track1_path)
|
||||
track1 = models.Tracks(session, track1_path)
|
||||
playlist_tab.insert_track(session, track1)
|
||||
track2_path = "/d/e/f"
|
||||
track2 = Tracks(session, track2_path)
|
||||
track2 = models.Tracks(session, track2_path)
|
||||
playlist_tab.insert_track(session, track2)
|
||||
|
||||
playlist_tab._set_next_track_row(1)
|
||||
@ -165,21 +202,24 @@ def test_clear_next(qtbot, session):
|
||||
assert playlist_tab._get_next_track_row() is None
|
||||
|
||||
|
||||
def test_get_selected_row(qtbot, session):
|
||||
def test_get_selected_row(qtbot, monkeypatch, session):
|
||||
|
||||
# Create playlist
|
||||
playlist = Playlists(session, "test playlist")
|
||||
playlist_tab = PlaylistTab(None, session, playlist.id)
|
||||
monkeypatch.setattr(musicmuster, "Session", session)
|
||||
monkeypatch.setattr(playlists, "Session", session)
|
||||
|
||||
# Create playlist and playlist_tab
|
||||
window = musicmuster.Window()
|
||||
playlist = models.Playlists(session, "test playlist")
|
||||
playlist_tab = playlists.PlaylistTab(window, session, playlist.id)
|
||||
|
||||
# Add some tracks
|
||||
track1_path = "/a/b/c"
|
||||
track1 = Tracks(session, track1_path)
|
||||
track1 = models.Tracks(session, track1_path)
|
||||
playlist_tab.insert_track(session, track1)
|
||||
track2_path = "/d/e/f"
|
||||
track2 = Tracks(session, track2_path)
|
||||
track2 = models.Tracks(session, track2_path)
|
||||
playlist_tab.insert_track(session, track2)
|
||||
|
||||
window = Window()
|
||||
qtbot.addWidget(playlist_tab)
|
||||
with qtbot.waitExposed(window):
|
||||
window.show()
|
||||
@ -191,32 +231,50 @@ def test_get_selected_row(qtbot, session):
|
||||
)
|
||||
|
||||
|
||||
def test_set_next(qtbot, session):
|
||||
def test_set_next(qtbot, monkeypatch, session):
|
||||
|
||||
monkeypatch.setattr(musicmuster, "Session", session)
|
||||
monkeypatch.setattr(playlists, "Session", session)
|
||||
seed2tracks(session)
|
||||
|
||||
playlist_name = "test playlist"
|
||||
# Create testing playlist
|
||||
playlist = Playlists(session, "test playlist")
|
||||
playlist_tab = PlaylistTab(None, session, playlist.id)
|
||||
window = musicmuster.Window()
|
||||
playlist = models.Playlists(session, playlist_name)
|
||||
playlist_tab = playlists.PlaylistTab(window, session, playlist.id)
|
||||
idx = window.tabPlaylist.addTab(playlist_tab, playlist_name)
|
||||
window.tabPlaylist.setCurrentIndex(idx)
|
||||
qtbot.addWidget(playlist_tab)
|
||||
|
||||
# Add some tracks
|
||||
track1_path = "testdata/isa.mp3"
|
||||
track1 = Tracks(session, track1_path)
|
||||
track1 = models.Tracks.get_from_filename(session, "isa.mp3")
|
||||
playlist_tab.insert_track(session, track1)
|
||||
track2_path = "mom.mp3"
|
||||
track2 = Tracks(session, track2_path)
|
||||
track2 = models.Tracks.get_from_filename(session, "mom.mp3")
|
||||
playlist_tab.insert_track(session, track2)
|
||||
|
||||
window = Window()
|
||||
qtbot.addWidget(playlist_tab)
|
||||
with qtbot.waitExposed(window):
|
||||
window.show()
|
||||
|
||||
row0_item2 = playlist_tab.item(0, 2)
|
||||
assert row0_item2 is not None
|
||||
rect = playlist_tab.visualItemRect(row0_item2)
|
||||
qtbot.mouseClick(
|
||||
playlist_tab.viewport(), Qt.LeftButton, pos=rect.center()
|
||||
)
|
||||
qtbot.wait(10000)
|
||||
# qtbot.wait(10000)
|
||||
qtbot.keyPress(playlist_tab.viewport(), "N",
|
||||
modifier=Qt.ControlModifier)
|
||||
qtbot.wait(2000)
|
||||
pass
|
||||
|
||||
|
||||
def test_kae(monkeypatch, session):
|
||||
# monkeypatch.setattr(dbconfig, "Session", session)
|
||||
monkeypatch.setattr(musicmuster, "Session", session)
|
||||
|
||||
musicmuster.Window.kae()
|
||||
# monkeypatch.setattr(musicmuster, "Session", session)
|
||||
# monkeypatch.setattr(dbconfig, "Session", session)
|
||||
# monkeypatch.setattr(models, "Session", session)
|
||||
# monkeypatch.setattr(playlists, "Session", session)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user