V4 WIP: mostly Black formatting

This commit is contained in:
Keith Edmunds 2024-04-05 09:42:51 +01:00
parent df620cde86
commit 16e3c8235e
18 changed files with 496 additions and 296 deletions

View File

@ -53,7 +53,6 @@ class MyTableWidget(QTableView):
class MyModel(QAbstractTableModel):
def columnCount(self, index):
return 2
@ -71,7 +70,11 @@ class MyModel(QAbstractTableModel):
return QVariant()
def flags(self, index):
return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEditable
return (
Qt.ItemFlag.ItemIsEnabled
| Qt.ItemFlag.ItemIsSelectable
| Qt.ItemFlag.ItemIsEditable
)
class MainWindow(QMainWindow):

View File

@ -66,7 +66,9 @@ class PlaydatesTable(Model):
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
lastplayed: Mapped[dt.datetime] = mapped_column(index=True)
track_id: Mapped[int] = mapped_column(ForeignKey("tracks.id"))
track: Mapped["TracksTable"] = relationship("TracksTable", back_populates="playdates")
track: Mapped["TracksTable"] = relationship(
"TracksTable", back_populates="playdates"
)
def __repr__(self) -> str:
return (

View File

@ -111,7 +111,9 @@ class TrackSelectDialog(QDialog):
if self.add_to_header:
if move_existing and existing_prd: # "and existing_prd" for mypy's benefit
self.source_model.move_track_to_header(self.new_row_number, existing_prd, note)
self.source_model.move_track_to_header(
self.new_row_number, existing_prd, note
)
else:
self.source_model.add_track_to_header(self.new_row_number, track_id)
# Close dialog - we can only add one track to a header
@ -119,7 +121,9 @@ class TrackSelectDialog(QDialog):
else:
# Adding a new track row
if move_existing and existing_prd: # "and existing_prd" for mypy's benefit
self.source_model.move_track_add_note(self.new_row_number, existing_prd, note)
self.source_model.move_track_add_note(
self.new_row_number, existing_prd, note
)
else:
self.source_model.insert_row(self.new_row_number, track_id, note)

View File

@ -31,14 +31,13 @@ from log import log
ALCHEMICAL_DATABASE_URI = os.environ.get("ALCHEMICAL_DATABASE_URI")
if ALCHEMICAL_DATABASE_URI is None:
raise ValueError("ALCHEMICAL_DATABASE_URI is undefined")
if 'unittest' in sys.modules and 'sqlite' not in ALCHEMICAL_DATABASE_URI:
if "unittest" in sys.modules and "sqlite" not in ALCHEMICAL_DATABASE_URI:
raise ValueError("Unit tests running on non-Sqlite database")
db = Alchemical(ALCHEMICAL_DATABASE_URI)
# Database classes
class Carts(dbtables.CartsTable):
def __init__(
self,
session: Session,
@ -61,7 +60,6 @@ class Carts(dbtables.CartsTable):
class NoteColours(dbtables.NoteColoursTable):
def __init__(
self,
session: Session,
@ -123,7 +121,6 @@ class NoteColours(dbtables.NoteColoursTable):
class Playdates(dbtables.PlaydatesTable):
def __init__(self, session: Session, track_id: int) -> None:
"""Record that track was played"""
@ -162,7 +159,6 @@ class Playdates(dbtables.PlaydatesTable):
class Playlists(dbtables.PlaylistsTable):
def __init__(self, session: Session, name: str):
self.name = name
session.add(self)
@ -175,11 +171,7 @@ class Playlists(dbtables.PlaylistsTable):
"""
session.execute(
update(Playlists)
.where(
(Playlists.id.in_(playlist_ids))
)
.values(tab=None)
update(Playlists).where((Playlists.id.in_(playlist_ids))).values(tab=None)
)
def close(self) -> None:
@ -295,7 +287,6 @@ class Playlists(dbtables.PlaylistsTable):
class PlaylistRows(dbtables.PlaylistRowsTable):
def __init__(
self,
session: Session,
@ -361,9 +352,7 @@ class PlaylistRows(dbtables.PlaylistRowsTable):
return session.execute(stmt).unique().scalar_one()
@classmethod
def deep_rows(
cls, session: Session, playlist_id: int
) -> Sequence["PlaylistRows"]:
def deep_rows(cls, session: Session, playlist_id: int) -> Sequence["PlaylistRows"]:
"""
Return a list of playlist rows that include full track and lastplayed data for
given playlist_id., Sequence
@ -380,9 +369,7 @@ class PlaylistRows(dbtables.PlaylistRowsTable):
return session.scalars(stmt).unique().all()
@staticmethod
def delete_higher_rows(
session: Session, playlist_id: int, maxrow: int
) -> None:
def delete_higher_rows(session: Session, playlist_id: int, maxrow: int) -> None:
"""
Delete rows in given playlist that have a higher row number
than 'maxrow'
@ -527,10 +514,21 @@ class PlaylistRows(dbtables.PlaylistRowsTable):
@classmethod
def insert_row(
cls, session: Session, playlist_id: int, new_row_number: int
cls,
session: Session,
playlist_id: int,
new_row_number: int,
note: str = "",
track_id: Optional[int] = None,
) -> "PlaylistRows":
cls.move_rows_down(session, playlist_id, new_row_number, 1)
return cls(session, playlist_id, new_row_number)
return cls(
session,
playlist_id=playlist_id,
row_number=new_row_number,
note=note,
track_id=track_id,
)
@staticmethod
def move_rows_down(
@ -574,7 +572,6 @@ class PlaylistRows(dbtables.PlaylistRowsTable):
class Settings(dbtables.SettingsTable):
def __init__(self, session: Session, name: str):
self.name = name
session.add(self)
@ -612,7 +609,6 @@ class Settings(dbtables.SettingsTable):
class Tracks(dbtables.TracksTable):
def __repr__(self) -> str:
return (
f"<Track(id={self.id}, title={self.title}, "

View File

@ -81,22 +81,22 @@ import argparse
if sys.version_info[0] < 3:
raise RuntimeError('PipeClient Error: Python 3.x required')
raise RuntimeError("PipeClient Error: Python 3.x required")
# Platform specific constants
if sys.platform == 'win32':
WRITE_NAME: str = '\\\\.\\pipe\\ToSrvPipe'
READ_NAME: str = '\\\\.\\pipe\\FromSrvPipe'
EOL: str = '\r\n\0'
if sys.platform == "win32":
WRITE_NAME: str = "\\\\.\\pipe\\ToSrvPipe"
READ_NAME: str = "\\\\.\\pipe\\FromSrvPipe"
EOL: str = "\r\n\0"
else:
# Linux or Mac
PIPE_BASE: str = '/tmp/audacity_script_pipe.'
WRITE_NAME: str = PIPE_BASE + 'to.' + str(os.getuid())
READ_NAME: str = PIPE_BASE + 'from.' + str(os.getuid())
EOL: str = '\n'
PIPE_BASE: str = "/tmp/audacity_script_pipe."
WRITE_NAME: str = PIPE_BASE + "to." + str(os.getuid())
READ_NAME: str = PIPE_BASE + "from." + str(os.getuid())
EOL: str = "\n"
class PipeClient():
class PipeClient:
"""Write / read client access to Audacity via named pipes.
Normally there should be just one instance of this class. If
@ -141,7 +141,7 @@ class PipeClient():
self.timer: bool = False # type: ignore
self._start_time: float = 0 # type: ignore
self._write_pipe = None
self.reply: str = '' # type: ignore
self.reply: str = "" # type: ignore
if not self._write_pipe:
self._write_thread_start()
self._read_thread_start()
@ -156,7 +156,7 @@ class PipeClient():
# Allow a little time for connection to be made.
time.sleep(0.1)
if not self._write_pipe:
raise RuntimeError('PipeClientError: Write pipe cannot be opened.')
raise RuntimeError("PipeClientError: Write pipe cannot be opened.")
def _write_pipe_open(self) -> None:
"""Open _write_pipe."""
@ -187,16 +187,16 @@ class PipeClient():
self._write_pipe.write(command + EOL)
# Check that read pipe is alive
if PipeClient.reader_pipe_broken.is_set():
raise RuntimeError('PipeClient: Read-pipe error.')
raise RuntimeError("PipeClient: Read-pipe error.")
try:
self._write_pipe.flush()
if self.timer:
self._start_time = time.time()
self.reply = ''
self.reply = ""
PipeClient.reply_ready.clear()
except IOError as err:
if err.errno == errno.EPIPE:
raise RuntimeError('PipeClient: Write-pipe error.')
raise RuntimeError("PipeClient: Write-pipe error.")
else:
raise
@ -211,20 +211,20 @@ class PipeClient():
line = read_pipe.readline()
# Stop timer as soon as we get first line of response.
stop_time = time.time()
while pipe_ok and line != '\n':
while pipe_ok and line != "\n":
message += line
line = read_pipe.readline()
if line == '':
if line == "":
# No data in read_pipe indicates that the pipe
# is broken (Audacity may have crashed).
PipeClient.reader_pipe_broken.set()
pipe_ok = False
if self.timer:
xtime = (stop_time - self._start_time) * 1000
message += f'Execution time: {xtime:.2f}ms'
message += f"Execution time: {xtime:.2f}ms"
self.reply = message
PipeClient.reply_ready.set()
message = ''
message = ""
def read(self) -> str:
"""Read Audacity's reply from pipe.
@ -238,31 +238,45 @@ class PipeClient():
"""
if not PipeClient.reply_ready.is_set():
return ''
return ""
return self.reply
def bool_from_string(strval) -> bool:
"""Return boolean value from string"""
if strval.lower() in ('true', 't', '1', 'yes', 'y'):
if strval.lower() in ("true", "t", "1", "yes", "y"):
return True
if strval.lower() in ('false', 'f', '0', 'no', 'n'):
if strval.lower() in ("false", "f", "0", "no", "n"):
return False
raise argparse.ArgumentTypeError('Boolean value expected.')
raise argparse.ArgumentTypeError("Boolean value expected.")
def main() -> None:
"""Interactive command-line for PipeClient"""
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--timeout', type=float, metavar='', default=10,
help="timeout for reply in seconds (default: 10")
parser.add_argument('-s', '--show-time', metavar='True/False',
nargs='?', type=bool_from_string,
const='t', default='t', dest='show',
help='show command execution time (default: True)')
parser.add_argument('-d', '--docs', action='store_true',
help='show documentation and exit')
parser.add_argument(
"-t",
"--timeout",
type=float,
metavar="",
default=10,
help="timeout for reply in seconds (default: 10",
)
parser.add_argument(
"-s",
"--show-time",
metavar="True/False",
nargs="?",
type=bool_from_string,
const="t",
default="t",
dest="show",
help="show command execution time (default: True)",
)
parser.add_argument(
"-d", "--docs", action="store_true", help="show documentation and exit"
)
args = parser.parse_args()
if args.docs:
@ -271,23 +285,23 @@ def main() -> None:
client: PipeClient = PipeClient()
while True:
reply: str = ''
reply: str = ""
message: str = input("\nEnter command or 'Q' to quit: ")
start = time.time()
if message.upper() == 'Q':
if message.upper() == "Q":
sys.exit(0)
elif message == '':
elif message == "":
pass
else:
client.write(message, timer=args.show)
while reply == '':
while reply == "":
time.sleep(0.1) # allow time for reply
if time.time() - start > args.timeout:
reply = 'PipeClient: Reply timed-out.'
reply = "PipeClient: Reply timed-out."
else:
reply = client.read()
print(reply)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -27,6 +27,7 @@ from PyQt6.QtGui import (
# Third party imports
import obsws_python as obs # type: ignore
# import snoop # type: ignore
# App imports
@ -694,8 +695,9 @@ class PlaylistModel(QAbstractTableModel):
< prd.plr_rownum
)
):
section_end_time = track_sequence.now.end_time + dt.timedelta(
milliseconds=duration
section_end_time = (
track_sequence.now.end_time
+ dt.timedelta(milliseconds=duration)
)
end_time_str = (
", section end time "
@ -750,7 +752,7 @@ class PlaylistModel(QAbstractTableModel):
self,
proposed_row_number: Optional[int],
track_id: Optional[int] = None,
note: Optional[str] = None,
note: str = "",
) -> None:
"""
Insert a row.
@ -762,11 +764,13 @@ class PlaylistModel(QAbstractTableModel):
with db.Session() as session:
super().beginInsertRows(QModelIndex(), new_row_number, new_row_number)
plr = PlaylistRows.insert_row(session, self.playlist_id, new_row_number)
plr.track_id = track_id
if note:
plr.note = note
_ = PlaylistRows.insert_row(
session=session,
playlist_id=self.playlist_id,
new_row_number=new_row_number,
note=note,
track_id=track_id,
)
self.refresh_data(session)
super().endInsertRows()

View File

@ -47,6 +47,7 @@ from helpers import (
from log import log
from models import Settings
from playlistmodel import PlaylistModel, PlaylistProxyModel
if TYPE_CHECKING:
from musicmuster import Window

View File

@ -15,7 +15,11 @@ class Ui_MainWindow(object):
MainWindow.resize(1280, 857)
MainWindow.setMinimumSize(QtCore.QSize(1280, 0))
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/musicmuster"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon.addPixmap(
QtGui.QPixmap(":/icons/musicmuster"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
MainWindow.setWindowIcon(icon)
MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
@ -27,39 +31,62 @@ class Ui_MainWindow(object):
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.previous_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Preferred,
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.previous_track_2.sizePolicy().hasHeightForWidth())
sizePolicy.setHeightForWidth(
self.previous_track_2.sizePolicy().hasHeightForWidth()
)
self.previous_track_2.setSizePolicy(sizePolicy)
self.previous_track_2.setMaximumSize(QtCore.QSize(230, 16777215))
font = QtGui.QFont()
font.setFamily("Sans")
font.setPointSize(20)
self.previous_track_2.setFont(font)
self.previous_track_2.setStyleSheet("background-color: #f8d7da;\n"
"border: 1px solid rgb(85, 87, 83);")
self.previous_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.previous_track_2.setStyleSheet(
"background-color: #f8d7da;\n" "border: 1px solid rgb(85, 87, 83);"
)
self.previous_track_2.setAlignment(
QtCore.Qt.AlignmentFlag.AlignRight
| QtCore.Qt.AlignmentFlag.AlignTrailing
| QtCore.Qt.AlignmentFlag.AlignVCenter
)
self.previous_track_2.setObjectName("previous_track_2")
self.verticalLayout_3.addWidget(self.previous_track_2)
self.current_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Preferred,
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.current_track_2.sizePolicy().hasHeightForWidth())
sizePolicy.setHeightForWidth(
self.current_track_2.sizePolicy().hasHeightForWidth()
)
self.current_track_2.setSizePolicy(sizePolicy)
self.current_track_2.setMaximumSize(QtCore.QSize(230, 16777215))
font = QtGui.QFont()
font.setFamily("Sans")
font.setPointSize(20)
self.current_track_2.setFont(font)
self.current_track_2.setStyleSheet("background-color: #d4edda;\n"
"border: 1px solid rgb(85, 87, 83);")
self.current_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.current_track_2.setStyleSheet(
"background-color: #d4edda;\n" "border: 1px solid rgb(85, 87, 83);"
)
self.current_track_2.setAlignment(
QtCore.Qt.AlignmentFlag.AlignRight
| QtCore.Qt.AlignmentFlag.AlignTrailing
| QtCore.Qt.AlignmentFlag.AlignVCenter
)
self.current_track_2.setObjectName("current_track_2")
self.verticalLayout_3.addWidget(self.current_track_2)
self.next_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Preferred,
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.next_track_2.sizePolicy().hasHeightForWidth())
@ -69,19 +96,29 @@ class Ui_MainWindow(object):
font.setFamily("Sans")
font.setPointSize(20)
self.next_track_2.setFont(font)
self.next_track_2.setStyleSheet("background-color: #fff3cd;\n"
"border: 1px solid rgb(85, 87, 83);")
self.next_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.next_track_2.setStyleSheet(
"background-color: #fff3cd;\n" "border: 1px solid rgb(85, 87, 83);"
)
self.next_track_2.setAlignment(
QtCore.Qt.AlignmentFlag.AlignRight
| QtCore.Qt.AlignmentFlag.AlignTrailing
| QtCore.Qt.AlignmentFlag.AlignVCenter
)
self.next_track_2.setObjectName("next_track_2")
self.verticalLayout_3.addWidget(self.next_track_2)
self.horizontalLayout_3.addLayout(self.verticalLayout_3)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.hdrPreviousTrack = QtWidgets.QLabel(parent=self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Preferred,
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.hdrPreviousTrack.sizePolicy().hasHeightForWidth())
sizePolicy.setHeightForWidth(
self.hdrPreviousTrack.sizePolicy().hasHeightForWidth()
)
self.hdrPreviousTrack.setSizePolicy(sizePolicy)
self.hdrPreviousTrack.setMinimumSize(QtCore.QSize(0, 0))
self.hdrPreviousTrack.setMaximumSize(QtCore.QSize(16777215, 16777215))
@ -89,32 +126,43 @@ class Ui_MainWindow(object):
font.setFamily("Sans")
font.setPointSize(20)
self.hdrPreviousTrack.setFont(font)
self.hdrPreviousTrack.setStyleSheet("background-color: #f8d7da;\n"
"border: 1px solid rgb(85, 87, 83);")
self.hdrPreviousTrack.setStyleSheet(
"background-color: #f8d7da;\n" "border: 1px solid rgb(85, 87, 83);"
)
self.hdrPreviousTrack.setText("")
self.hdrPreviousTrack.setWordWrap(False)
self.hdrPreviousTrack.setObjectName("hdrPreviousTrack")
self.verticalLayout.addWidget(self.hdrPreviousTrack)
self.hdrCurrentTrack = QtWidgets.QPushButton(parent=self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Preferred,
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.hdrCurrentTrack.sizePolicy().hasHeightForWidth())
sizePolicy.setHeightForWidth(
self.hdrCurrentTrack.sizePolicy().hasHeightForWidth()
)
self.hdrCurrentTrack.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setPointSize(20)
self.hdrCurrentTrack.setFont(font)
self.hdrCurrentTrack.setStyleSheet("background-color: #d4edda;\n"
"border: 1px solid rgb(85, 87, 83);\n"
"text-align: left;\n"
"padding-left: 8px;\n"
"")
self.hdrCurrentTrack.setStyleSheet(
"background-color: #d4edda;\n"
"border: 1px solid rgb(85, 87, 83);\n"
"text-align: left;\n"
"padding-left: 8px;\n"
""
)
self.hdrCurrentTrack.setText("")
self.hdrCurrentTrack.setFlat(True)
self.hdrCurrentTrack.setObjectName("hdrCurrentTrack")
self.verticalLayout.addWidget(self.hdrCurrentTrack)
self.hdrNextTrack = QtWidgets.QPushButton(parent=self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Preferred,
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.hdrNextTrack.sizePolicy().hasHeightForWidth())
@ -122,10 +170,12 @@ class Ui_MainWindow(object):
font = QtGui.QFont()
font.setPointSize(20)
self.hdrNextTrack.setFont(font)
self.hdrNextTrack.setStyleSheet("background-color: #fff3cd;\n"
"border: 1px solid rgb(85, 87, 83);\n"
"text-align: left;\n"
"padding-left: 8px;")
self.hdrNextTrack.setStyleSheet(
"background-color: #fff3cd;\n"
"border: 1px solid rgb(85, 87, 83);\n"
"text-align: left;\n"
"padding-left: 8px;"
)
self.hdrNextTrack.setText("")
self.hdrNextTrack.setFlat(True)
self.hdrNextTrack.setObjectName("hdrNextTrack")
@ -160,7 +210,12 @@ class Ui_MainWindow(object):
self.cartsWidget.setObjectName("cartsWidget")
self.horizontalLayout_Carts = QtWidgets.QHBoxLayout(self.cartsWidget)
self.horizontalLayout_Carts.setObjectName("horizontalLayout_Carts")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
spacerItem = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_Carts.addItem(spacerItem)
self.gridLayout_4.addWidget(self.cartsWidget, 2, 0, 1, 1)
self.frame_6 = QtWidgets.QFrame(parent=self.centralwidget)
@ -205,7 +260,11 @@ class Ui_MainWindow(object):
self.btnPreview = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
self.btnPreview.setMinimumSize(QtCore.QSize(132, 41))
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/icons/headphones"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon1.addPixmap(
QtGui.QPixmap(":/icons/headphones"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.btnPreview.setIcon(icon1)
self.btnPreview.setIconSize(QtCore.QSize(30, 30))
self.btnPreview.setCheckable(True)
@ -289,10 +348,15 @@ class Ui_MainWindow(object):
self.label_silent_timer.setObjectName("label_silent_timer")
self.horizontalLayout.addWidget(self.frame_silent)
self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Preferred,
)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widgetFadeVolume.sizePolicy().hasHeightForWidth())
sizePolicy.setHeightForWidth(
self.widgetFadeVolume.sizePolicy().hasHeightForWidth()
)
self.widgetFadeVolume.setSizePolicy(sizePolicy)
self.widgetFadeVolume.setMinimumSize(QtCore.QSize(0, 0))
self.widgetFadeVolume.setObjectName("widgetFadeVolume")
@ -309,7 +373,11 @@ class Ui_MainWindow(object):
self.btnFade.setMinimumSize(QtCore.QSize(132, 32))
self.btnFade.setMaximumSize(QtCore.QSize(164, 16777215))
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/icons/fade"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon2.addPixmap(
QtGui.QPixmap(":/icons/fade"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.btnFade.setIcon(icon2)
self.btnFade.setIconSize(QtCore.QSize(30, 30))
self.btnFade.setObjectName("btnFade")
@ -317,7 +385,11 @@ class Ui_MainWindow(object):
self.btnStop = QtWidgets.QPushButton(parent=self.frame)
self.btnStop.setMinimumSize(QtCore.QSize(0, 36))
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(":/icons/stopsign"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon3.addPixmap(
QtGui.QPixmap(":/icons/stopsign"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.btnStop.setIcon(icon3)
self.btnStop.setObjectName("btnStop")
self.verticalLayout_5.addWidget(self.btnStop)
@ -343,39 +415,69 @@ class Ui_MainWindow(object):
MainWindow.setStatusBar(self.statusbar)
self.actionPlay_next = QtGui.QAction(parent=MainWindow)
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-play.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon4.addPixmap(
QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-play.png"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.actionPlay_next.setIcon(icon4)
self.actionPlay_next.setObjectName("actionPlay_next")
self.actionSkipToNext = QtGui.QAction(parent=MainWindow)
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon5.addPixmap(
QtGui.QPixmap(":/icons/next"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.actionSkipToNext.setIcon(icon5)
self.actionSkipToNext.setObjectName("actionSkipToNext")
self.actionInsertTrack = QtGui.QAction(parent=MainWindow)
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon6.addPixmap(
QtGui.QPixmap(
"app/ui/../../../../.designer/backup/icon_search_database.png"
),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.actionInsertTrack.setIcon(icon6)
self.actionInsertTrack.setObjectName("actionInsertTrack")
self.actionAdd_file = QtGui.QAction(parent=MainWindow)
icon7 = QtGui.QIcon()
icon7.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon7.addPixmap(
QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_open_file.png"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.actionAdd_file.setIcon(icon7)
self.actionAdd_file.setObjectName("actionAdd_file")
self.actionFade = QtGui.QAction(parent=MainWindow)
icon8 = QtGui.QIcon()
icon8.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-fade.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon8.addPixmap(
QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-fade.png"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.actionFade.setIcon(icon8)
self.actionFade.setObjectName("actionFade")
self.actionStop = QtGui.QAction(parent=MainWindow)
icon9 = QtGui.QIcon()
icon9.addPixmap(QtGui.QPixmap(":/icons/stop"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon9.addPixmap(
QtGui.QPixmap(":/icons/stop"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.actionStop.setIcon(icon9)
self.actionStop.setObjectName("actionStop")
self.action_Clear_selection = QtGui.QAction(parent=MainWindow)
self.action_Clear_selection.setObjectName("action_Clear_selection")
self.action_Resume_previous = QtGui.QAction(parent=MainWindow)
icon10 = QtGui.QIcon()
icon10.addPixmap(QtGui.QPixmap(":/icons/previous"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
icon10.addPixmap(
QtGui.QPixmap(":/icons/previous"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
self.action_Resume_previous.setIcon(icon10)
self.action_Resume_previous.setObjectName("action_Resume_previous")
self.actionE_xit = QtGui.QAction(parent=MainWindow)
@ -422,7 +524,9 @@ class Ui_MainWindow(object):
self.actionImport = QtGui.QAction(parent=MainWindow)
self.actionImport.setObjectName("actionImport")
self.actionDownload_CSV_of_played_tracks = QtGui.QAction(parent=MainWindow)
self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks")
self.actionDownload_CSV_of_played_tracks.setObjectName(
"actionDownload_CSV_of_played_tracks"
)
self.actionSearch = QtGui.QAction(parent=MainWindow)
self.actionSearch.setObjectName("actionSearch")
self.actionInsertSectionHeader = QtGui.QAction(parent=MainWindow)
@ -450,9 +554,13 @@ class Ui_MainWindow(object):
self.actionResume = QtGui.QAction(parent=MainWindow)
self.actionResume.setObjectName("actionResume")
self.actionSearch_title_in_Wikipedia = QtGui.QAction(parent=MainWindow)
self.actionSearch_title_in_Wikipedia.setObjectName("actionSearch_title_in_Wikipedia")
self.actionSearch_title_in_Wikipedia.setObjectName(
"actionSearch_title_in_Wikipedia"
)
self.actionSearch_title_in_Songfacts = QtGui.QAction(parent=MainWindow)
self.actionSearch_title_in_Songfacts.setObjectName("actionSearch_title_in_Songfacts")
self.actionSearch_title_in_Songfacts.setObjectName(
"actionSearch_title_in_Songfacts"
)
self.actionSelect_duplicate_rows = QtGui.QAction(parent=MainWindow)
self.actionSelect_duplicate_rows.setObjectName("actionSelect_duplicate_rows")
self.menuFile.addAction(self.actionNewPlaylist)
@ -503,7 +611,7 @@ class Ui_MainWindow(object):
self.retranslateUi(MainWindow)
self.tabPlaylist.setCurrentIndex(-1)
self.tabInfolist.setCurrentIndex(-1)
self.actionE_xit.triggered.connect(MainWindow.close) # type: ignore
self.actionE_xit.triggered.connect(MainWindow.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
@ -539,38 +647,58 @@ class Ui_MainWindow(object):
self.actionFade.setShortcut(_translate("MainWindow", "Ctrl+Z"))
self.actionStop.setText(_translate("MainWindow", "S&top"))
self.actionStop.setShortcut(_translate("MainWindow", "Ctrl+Alt+S"))
self.action_Clear_selection.setText(_translate("MainWindow", "Clear &selection"))
self.action_Clear_selection.setText(
_translate("MainWindow", "Clear &selection")
)
self.action_Clear_selection.setShortcut(_translate("MainWindow", "Esc"))
self.action_Resume_previous.setText(_translate("MainWindow", "&Resume previous"))
self.action_Resume_previous.setText(
_translate("MainWindow", "&Resume previous")
)
self.actionE_xit.setText(_translate("MainWindow", "E&xit"))
self.actionTest.setText(_translate("MainWindow", "&Test"))
self.actionOpenPlaylist.setText(_translate("MainWindow", "O&pen..."))
self.actionNewPlaylist.setText(_translate("MainWindow", "&New..."))
self.actionTestFunction.setText(_translate("MainWindow", "&Test function"))
self.actionSkipToFade.setText(_translate("MainWindow", "&Skip to start of fade"))
self.actionSkipToFade.setText(
_translate("MainWindow", "&Skip to start of fade")
)
self.actionSkipToEnd.setText(_translate("MainWindow", "Skip to &end of track"))
self.actionClosePlaylist.setText(_translate("MainWindow", "&Close"))
self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename..."))
self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te..."))
self.actionMoveSelected.setText(_translate("MainWindow", "Mo&ve selected tracks to..."))
self.actionMoveSelected.setText(
_translate("MainWindow", "Mo&ve selected tracks to...")
)
self.actionExport_playlist.setText(_translate("MainWindow", "E&xport..."))
self.actionSetNext.setText(_translate("MainWindow", "Set &next"))
self.actionSetNext.setShortcut(_translate("MainWindow", "Ctrl+N"))
self.actionSelect_next_track.setText(_translate("MainWindow", "Select next track"))
self.actionSelect_next_track.setText(
_translate("MainWindow", "Select next track")
)
self.actionSelect_next_track.setShortcut(_translate("MainWindow", "J"))
self.actionSelect_previous_track.setText(_translate("MainWindow", "Select previous track"))
self.actionSelect_previous_track.setText(
_translate("MainWindow", "Select previous track")
)
self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K"))
self.actionSelect_played_tracks.setText(_translate("MainWindow", "Select played tracks"))
self.actionMoveUnplayed.setText(_translate("MainWindow", "Move &unplayed tracks to..."))
self.actionSelect_played_tracks.setText(
_translate("MainWindow", "Select played tracks")
)
self.actionMoveUnplayed.setText(
_translate("MainWindow", "Move &unplayed tracks to...")
)
self.actionAdd_note.setText(_translate("MainWindow", "Add note..."))
self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T"))
self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls"))
self.actionImport.setText(_translate("MainWindow", "Import track..."))
self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I"))
self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks..."))
self.actionDownload_CSV_of_played_tracks.setText(
_translate("MainWindow", "Download CSV of played tracks...")
)
self.actionSearch.setText(_translate("MainWindow", "Search..."))
self.actionSearch.setShortcut(_translate("MainWindow", "/"))
self.actionInsertSectionHeader.setText(_translate("MainWindow", "Insert &section header..."))
self.actionInsertSectionHeader.setText(
_translate("MainWindow", "Insert &section header...")
)
self.actionInsertSectionHeader.setShortcut(_translate("MainWindow", "Ctrl+H"))
self.actionRemove.setText(_translate("MainWindow", "&Remove track"))
self.actionFind_next.setText(_translate("MainWindow", "Find next"))
@ -578,8 +706,12 @@ class Ui_MainWindow(object):
self.actionFind_previous.setText(_translate("MainWindow", "Find previous"))
self.actionFind_previous.setShortcut(_translate("MainWindow", "P"))
self.action_About.setText(_translate("MainWindow", "&About"))
self.actionSave_as_template.setText(_translate("MainWindow", "Save as template..."))
self.actionNew_from_template.setText(_translate("MainWindow", "New from template..."))
self.actionSave_as_template.setText(
_translate("MainWindow", "Save as template...")
)
self.actionNew_from_template.setText(
_translate("MainWindow", "New from template...")
)
self.actionDebug.setText(_translate("MainWindow", "Debug"))
self.actionAdd_cart.setText(_translate("MainWindow", "Edit cart &1..."))
self.actionMark_for_moving.setText(_translate("MainWindow", "Mark for moving"))
@ -588,10 +720,22 @@ class Ui_MainWindow(object):
self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V"))
self.actionResume.setText(_translate("MainWindow", "Resume"))
self.actionResume.setShortcut(_translate("MainWindow", "Ctrl+R"))
self.actionSearch_title_in_Wikipedia.setText(_translate("MainWindow", "Search title in Wikipedia"))
self.actionSearch_title_in_Wikipedia.setShortcut(_translate("MainWindow", "Ctrl+W"))
self.actionSearch_title_in_Songfacts.setText(_translate("MainWindow", "Search title in Songfacts"))
self.actionSearch_title_in_Songfacts.setShortcut(_translate("MainWindow", "Ctrl+S"))
self.actionSelect_duplicate_rows.setText(_translate("MainWindow", "Select duplicate rows..."))
self.actionSearch_title_in_Wikipedia.setText(
_translate("MainWindow", "Search title in Wikipedia")
)
self.actionSearch_title_in_Wikipedia.setShortcut(
_translate("MainWindow", "Ctrl+W")
)
self.actionSearch_title_in_Songfacts.setText(
_translate("MainWindow", "Search title in Songfacts")
)
self.actionSearch_title_in_Songfacts.setShortcut(
_translate("MainWindow", "Ctrl+S")
)
self.actionSelect_duplicate_rows.setText(
_translate("MainWindow", "Select duplicate rows...")
)
from infotabs import InfoTabs
from pyqtgraph import PlotWidget

View File

@ -49,9 +49,9 @@ def leading_silence(audio_segment, silence_threshold=-50.0, chunk_size=10):
trim_ms = 0 # ms
assert chunk_size > 0 # to avoid infinite loop
while (
audio_segment[trim_ms:trim_ms + chunk_size].dBFS < silence_threshold
and trim_ms < len(audio_segment)):
while audio_segment[
trim_ms : trim_ms + chunk_size
].dBFS < silence_threshold and trim_ms < len(audio_segment):
trim_ms += chunk_size
# if there is no end it should return the length of the segment
@ -72,8 +72,9 @@ def significant_fade(audio_segment, fade_threshold=-20.0, chunk_size=10):
segment_length = audio_segment.duration_seconds * 1000 # ms
trim_ms = segment_length - chunk_size
while (
audio_segment[trim_ms:trim_ms + chunk_size].dBFS < fade_threshold
and trim_ms > 0):
audio_segment[trim_ms : trim_ms + chunk_size].dBFS < fade_threshold
and trim_ms > 0
):
trim_ms -= chunk_size
# if there is no trailing silence, return lenght of track (it's less
@ -94,8 +95,9 @@ def trailing_silence(audio_segment, silence_threshold=-50.0, chunk_size=10):
segment_length = audio_segment.duration_seconds * 1000 # ms
trim_ms = segment_length - chunk_size
while (
audio_segment[trim_ms:trim_ms + chunk_size].dBFS < silence_threshold
and trim_ms > 0):
audio_segment[trim_ms : trim_ms + chunk_size].dBFS < silence_threshold
and trim_ms > 0
):
trim_ms -= chunk_size
# if there is no trailing silence, return lenght of track (it's less
@ -124,15 +126,17 @@ def update_progress(player, talk_at, silent_at):
remaining_time = total_time - elapsed_time
talk_time = remaining_time - (total_time - talk_at)
silent_time = remaining_time - (total_time - silent_at)
end_time = (dt.datetime.now() + timedelta(
milliseconds=remaining_time)).strftime("%H:%M:%S")
end_time = (dt.datetime.now() + timedelta(milliseconds=remaining_time)).strftime(
"%H:%M:%S"
)
print(
f"\t{ms_to_mmss(elapsed_time)}/"
f"{ms_to_mmss(total_time)}\t\t"
f"Talk in: {ms_to_mmss(talk_time)} "
f"Silent in: {ms_to_mmss(silent_time)} "
f"Ends at: {end_time} [{ms_to_mmss(remaining_time)}]"
, end="\r")
f"Ends at: {end_time} [{ms_to_mmss(remaining_time)}]",
end="\r",
)
# Print name of current song, print name of next song. Play current when
@ -163,21 +167,21 @@ def test():
test()
# next_song = get_next_song
#
#
# def play_track():
# r = run_aud_cmd("--current-song-length
#
#
#
#
#
#
# def play():
# play_track()
# songtimer_start()
#
#
#
#
# print("Start playing in 3 seconds")
#
#
# sleep(3)
#
#
# play()

View File

@ -1,5 +1,3 @@
# tl = Timeloop()
#
#
@ -48,34 +46,34 @@
# rt.stop() # better in a try/finally block to make sure the program ends!
# print("End")
#def kae2(self, index):
# print(f"table header click, index={index}")
# def kae2(self, index):
# print(f"table header click, index={index}")
#def kae(self, a, b, c):
# self.data.append(f"a={a}, b={b}, c={c}")
# def kae(self, a, b, c):
# self.data.append(f"a={a}, b={b}, c={c}")
#def mousePressEvent(self, QMouseEvent):
# print("mouse press")
# def mousePressEvent(self, QMouseEvent):
# print("mouse press")
#def mouseReleaseEvent(self, QMouseEvent):
# print("mouse release")
# # QMessageBox.about(
# # self,
# # "About Sample Editor",
# # "\n".join(self.data)
# # )
#def eventFilter(self, obj, event):
# # you could be doing different groups of actions
# # for different types of widgets and either filtering
# # the event or not.
# # Here we just check if its one of the layout widgets
# # if self.layout.indexOf(obj) != -1:
# # print(f"event received: {event.type()}")
# if event.type() == QEvent.MouseButtonPress:
# print("Widget click")
# # if I returned True right here, the event
# # would be filtered and not reach the obj,
# # meaning that I decided to handle it myself
# def mouseReleaseEvent(self, QMouseEvent):
# print("mouse release")
# # QMessageBox.about(
# # self,
# # "About Sample Editor",
# # "\n".join(self.data)
# # )
# def eventFilter(self, obj, event):
# # you could be doing different groups of actions
# # for different types of widgets and either filtering
# # the event or not.
# # Here we just check if its one of the layout widgets
# # if self.layout.indexOf(obj) != -1:
# # print(f"event received: {event.type()}")
# if event.type() == QEvent.MouseButtonPress:
# print("Widget click")
# # if I returned True right here, the event
# # would be filtered and not reach the obj,
# # meaning that I decided to handle it myself
# # regardless, just do the default
# return super().eventFilter(obj, event)
# # regardless, just do the default
# return super().eventFilter(obj, event)

View File

@ -7,19 +7,19 @@ from PyQt5.QtCore import Qt
qt_creator_file = "mainwindow.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qt_creator_file)
tick = QtGui.QImage('tick.png')
tick = QtGui.QImage("tick.png")
class TodoModel(QtCore.QAbstractListModel):
def __init__(self, *args, todos=None, **kwargs):
super(TodoModel, self).__init__(*args, **kwargs)
self.todos = todos or []
def data(self, index, role):
if role == Qt.DisplayRole:
_, text = self.todos[index.row()]
return text
if role == Qt.DecorationRole:
status, _ = self.todos[index.row()]
if status:
@ -51,15 +51,15 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
and then clearing it.
"""
text = self.todoEdit.text()
if text: # Don't add empty strings.
if text: # Don't add empty strings.
# Access the list via the model.
self.model.todos.append((False, text))
# Trigger refresh.
# Trigger refresh.
self.model.layoutChanged.emit()
# Empty the input
# Empty the input
self.todoEdit.setText("")
self.save()
def delete(self):
indexes = self.todoView.selectedIndexes()
if indexes:
@ -71,7 +71,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
# Clear the selection (as it is no longer valid).
self.todoView.clearSelection()
self.save()
def complete(self):
indexes = self.todoView.selectedIndexes()
if indexes:
@ -79,22 +79,22 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
row = index.row()
status, text = self.model.todos[row]
self.model.todos[row] = (True, text)
# .dataChanged takes top-left and bottom right, which are equal
# .dataChanged takes top-left and bottom right, which are equal
# for a single selection.
self.model.dataChanged.emit(index, index)
# Clear the selection (as it is no longer valid).
self.todoView.clearSelection()
self.save()
def load(self):
try:
with open('data.db', 'r') as f:
with open("data.db", "r") as f:
self.model.todos = json.load(f)
except Exception:
pass
def save(self):
with open('data.db', 'w') as f:
with open("data.db", "w") as f:
data = json.dump(self.model.todos, f)
@ -102,5 +102,3 @@ app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

View File

@ -10,23 +10,23 @@ import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '0c604bf490f8'
down_revision = '29c0d7ffc741'
revision = "0c604bf490f8"
down_revision = "29c0d7ffc741"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('playlist_rows', sa.Column('played', sa.Boolean(), nullable=False))
op.drop_index('ix_tracks_lastplayed', table_name='tracks')
op.drop_column('tracks', 'lastplayed')
op.add_column("playlist_rows", sa.Column("played", sa.Boolean(), nullable=False))
op.drop_index("ix_tracks_lastplayed", table_name="tracks")
op.drop_column("tracks", "lastplayed")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('tracks', sa.Column('lastplayed', mysql.DATETIME(), nullable=True))
op.create_index('ix_tracks_lastplayed', 'tracks', ['lastplayed'], unique=False)
op.drop_column('playlist_rows', 'played')
op.add_column("tracks", sa.Column("lastplayed", mysql.DATETIME(), nullable=True))
op.create_index("ix_tracks_lastplayed", "tracks", ["lastplayed"], unique=False)
op.drop_column("playlist_rows", "played")
# ### end Alembic commands ###

View File

@ -10,21 +10,21 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '2cc37d3cf07f'
down_revision = 'e3b04db5506f'
revision = "2cc37d3cf07f"
down_revision = "e3b04db5506f"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('playlists', sa.Column('last_used', sa.DateTime(), nullable=True))
op.add_column('playlists', sa.Column('loaded', sa.Boolean(), nullable=True))
op.add_column("playlists", sa.Column("last_used", sa.DateTime(), nullable=True))
op.add_column("playlists", sa.Column("loaded", sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('playlists', 'loaded')
op.drop_column('playlists', 'last_used')
op.drop_column("playlists", "loaded")
op.drop_column("playlists", "last_used")
# ### end Alembic commands ###

View File

@ -10,27 +10,28 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'b0983648595e'
down_revision = '1bc727e5e87f'
revision = "b0983648595e"
down_revision = "1bc727e5e87f"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('settings',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=32), nullable=False),
sa.Column('f_datetime', sa.DateTime(), nullable=True),
sa.Column('f_int', sa.Integer(), nullable=True),
sa.Column('f_string', sa.String(length=128), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
op.create_table(
"settings",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("name", sa.String(length=32), nullable=False),
sa.Column("f_datetime", sa.DateTime(), nullable=True),
sa.Column("f_int", sa.Integer(), nullable=True),
sa.Column("f_string", sa.String(length=128), nullable=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('settings')
op.drop_table("settings")
# ### end Alembic commands ###

View File

@ -10,43 +10,54 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f07b96a5e60f'
down_revision = 'b0983648595e'
revision = "f07b96a5e60f"
down_revision = "b0983648595e"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('playdates',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('lastplayed', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id')
op.create_table(
"playdates",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("lastplayed", sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f('ix_playdates_lastplayed'), 'playdates', ['lastplayed'], unique=False)
op.create_table('playlists',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=32), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
op.create_index(
op.f("ix_playdates_lastplayed"), "playdates", ["lastplayed"], unique=False
)
op.create_table('playlistracks',
sa.Column('playlist_id', sa.Integer(), nullable=True),
sa.Column('track_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['playlist_id'], ['playlists.id'], ),
sa.ForeignKeyConstraint(['track_id'], ['tracks.id'], )
op.create_table(
"playlists",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("name", sa.String(length=32), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
)
op.add_column('tracks', sa.Column('playdates_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'tracks', 'playdates', ['playdates_id'], ['id'])
op.create_table(
"playlistracks",
sa.Column("playlist_id", sa.Integer(), nullable=True),
sa.Column("track_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["playlist_id"],
["playlists.id"],
),
sa.ForeignKeyConstraint(
["track_id"],
["tracks.id"],
),
)
op.add_column("tracks", sa.Column("playdates_id", sa.Integer(), nullable=True))
op.create_foreign_key(None, "tracks", "playdates", ["playdates_id"], ["id"])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'tracks', type_='foreignkey')
op.drop_column('tracks', 'playdates_id')
op.drop_table('playlistracks')
op.drop_table('playlists')
op.drop_index(op.f('ix_playdates_lastplayed'), table_name='playdates')
op.drop_table('playdates')
op.drop_constraint(None, "tracks", type_="foreignkey")
op.drop_column("tracks", "playdates_id")
op.drop_table("playlistracks")
op.drop_table("playlists")
op.drop_index(op.f("ix_playdates_lastplayed"), table_name="playdates")
op.drop_table("playdates")
# ### end Alembic commands ###

View File

@ -12,10 +12,10 @@ import pytest
# Mark subsequent lines to ignore E402, imports not at top of file
# Set up test database before importing db
# Mark subsequent lines to ignore E402, imports not at top of file
DB_FILE = '/tmp/mm.db'
DB_FILE = "/tmp/mm.db"
if os.path.exists(DB_FILE):
os.unlink(DB_FILE)
os.environ['ALCHEMICAL_DATABASE_URI'] = 'sqlite:///' + DB_FILE
os.environ["ALCHEMICAL_DATABASE_URI"] = "sqlite:///" + DB_FILE
from models import db, Settings # noqa: E402

View File

@ -12,10 +12,10 @@ from app import helpers
# Set up test database before importing db
# Mark subsequent lines to ignore E402, imports not at top of file
DB_FILE = '/tmp/mm.db'
DB_FILE = "/tmp/mm.db"
if os.path.exists(DB_FILE):
os.unlink(DB_FILE)
os.environ['ALCHEMICAL_DATABASE_URI'] = 'sqlite:///' + DB_FILE
os.environ["ALCHEMICAL_DATABASE_URI"] = "sqlite:///" + DB_FILE
from app.models import ( # noqa: E402
db,
Carts,

View File

@ -12,12 +12,13 @@ from sqlalchemy.orm.session import Session
# App imports
from app.log import log
from app.helpers import get_file_metadata
# Set up test database before importing db
# Mark subsequent lines to ignore E402, imports not at top of file
DB_FILE = '/tmp/mm.db'
DB_FILE = "/tmp/mm.db"
if os.path.exists(DB_FILE):
os.unlink(DB_FILE)
os.environ['ALCHEMICAL_DATABASE_URI'] = 'sqlite:///' + DB_FILE
os.environ["ALCHEMICAL_DATABASE_URI"] = "sqlite:///" + DB_FILE
from app import playlistmodel # noqa: E402
from app.models import ( # noqa: E402
db,
@ -27,64 +28,83 @@ from app.models import ( # noqa: E402
)
class TestMMMisc(unittest.TestCase):
# class TestMMMiscTracks(unittest.TestCase):
# def setUp(self):
# PLAYLIST_NAME = "tracks playlist"
# self.test_tracks = [
# "testdata/isa.mp3",
# "testdata/isa_with_gap.mp3",
# "testdata/loser.mp3",
# "testdata/lovecats-10seconds.mp3",
# "testdata/lovecats.mp3",
# "testdata/mom.mp3",
# "testdata/sitting.mp3",
# ]
# db.create_all()
# # Create a playlist and model
# with db.Session() as session:
# self.playlist = Playlists(session, PLAYLIST_NAME)
# self.model = playlistmodel.PlaylistModel(self.playlist.id)
# for row in range(len(self.test_tracks)):
# track_path = self.test_tracks[row % len(self.test_tracks)]
# metadata = get_file_metadata(track_path)
# track = Tracks(session, **metadata)
# self.model.insert_row(
# proposed_row_number=row, track_id=track.id, note=f"{row=}"
# )
# session.commit()
# def tearDown(self):
# db.drop_all()
# def test_7_row_playlist(self):
# # Test auto-created playlist
# assert self.model.rowCount() == 7
# assert max(self.model.playlist_rows.keys()) == 6
# for row in range(self.model.rowCount()):
# assert row in self.model.playlist_rows
# assert self.model.playlist_rows[row].plr_rownum == row
class TestMMMiscRowMove(unittest.TestCase):
def setUp(self):
PLAYLIST_NAME = "test playlist"
self.test_tracks = [
"testdata/isa.mp3",
"testdata/isa_with_gap.mp3",
"testdata/loser.mp3",
"testdata/lovecats-10seconds.mp3",
"testdata/lovecats.mp3",
"testdata/mom.mp3",
"testdata/sitting.mp3",
]
PLAYLIST_NAME = "rowmove playlist"
ROWS_TO_CREATE = 11
db.create_all()
# Create a playlist and model
with db.Session() as session:
self.playlist = Playlists(session, PLAYLIST_NAME)
self.model = playlistmodel.PlaylistModel(self.playlist.id)
for row in range(len(self.test_tracks)):
track_path = self.test_tracks[row % len(self.test_tracks)]
metadata = get_file_metadata(track_path)
track = Tracks(session, **metadata)
self.model.insert_row(proposed_row_number=row, track_id=track.id, note=f"{row=}")
for row in range(ROWS_TO_CREATE):
print(f"{row=}")
self.model.insert_row(proposed_row_number=row, note=str(row))
session.commit()
def tearDown(self):
db.drop_all()
def test_7_row_playlist(self):
# Test auto-created playlist
assert self.model.rowCount() == 7
assert max(self.model.playlist_rows.keys()) == 6
def test_move_rows_test2(self):
# move row 3 to row 5
self.model.move_rows([3], 5)
# Check we have all rows and plr_rownums are correct
for row in range(self.model.rowCount()):
assert row in self.model.playlist_rows
assert self.model.playlist_rows[row].plr_rownum == row
# def test_move_rows_test2(monkeypatch, session):
# # move row 3 to row 5
# monkeypatch.setattr(playlistmodel, "Session", session)
# model = create_model_with_playlist_rows(session, 11)
# model.move_rows([3], 5)
# # Check we have all rows and plr_rownums are correct
# for row in range(model.rowCount()):
# assert row in model.playlist_rows
# assert model.playlist_rows[row].plr_rownum == row
# if row not in [3, 4, 5]:
# assert model.playlist_rows[row].note == str(row)
# elif row == 3:
# assert model.playlist_rows[row].note == str(4)
# elif row == 4:
# assert model.playlist_rows[row].note == str(5)
# elif row == 5:
# assert model.playlist_rows[row].note == str(3)
if row not in [3, 4, 5]:
assert self.model.playlist_rows[row].note == str(row)
elif row == 3:
assert self.model.playlist_rows[row].note == str(4)
elif row == 4:
assert self.model.playlist_rows[row].note == str(5)
elif row == 5:
assert self.model.playlist_rows[row].note == str(3)
# def test_move_rows_test3(monkeypatch, session):