Compare commits

..

4 Commits

Author SHA1 Message Date
Keith Edmunds
b706008101 Make volume fade graph update much smoother.
VLC get_time and get_position are very granular, only updating about
3-4 times a second. Instead, calculate play_time by substracting track
start time from current time and expressing that as milliseconds.
2023-06-19 00:55:04 +01:00
Keith Edmunds
4eb3a98c95 Added volume fade graph. 2023-06-18 09:20:55 +01:00
Keith Edmunds
af0d715423 Suppress pygame message at startup 2023-06-14 07:20:36 +01:00
Keith Edmunds
6ae6d8e94e WIP volume graphs using matlibplot 2023-06-13 07:55:24 +01:00
9 changed files with 18671 additions and 18670 deletions

1
.envrc
View File

@ -4,6 +4,7 @@ export MAIL_PORT=587
export MAIL_SERVER="smtp.fastmail.com"
export MAIL_USERNAME="kae@midnighthax.com"
export MAIL_USE_TLS=True
export PYGAME_HIDE_SUPPORT_PROMPT=1
branch=$(git branch --show-current)
# Always treat running from /home/kae/mm as production
if [ $(pwd) == /home/kae/mm ]; then

View File

@ -48,6 +48,9 @@ class Config(object):
DISPLAY_SQL = False
ERRORS_FROM = ['noreply@midnighthax.com']
ERRORS_TO = ['kae@midnighthax.com']
FADE_CURVE_BACKGROUND = "lightyellow"
FADE_CURVE_FOREGROUND = "blue"
FADE_CURVE_MS_BEFORE_FADE = 5000
FADE_STEPS = 20
FADE_TIME = 3000
HIDE_AFTER_PLAYING_OFFSET = 5000
@ -77,7 +80,7 @@ class Config(object):
SCROLL_TOP_MARGIN = 3
TEXT_NO_TRACK_NO_NOTE = "[Section header]"
TOD_TIME_FORMAT = "%H:%M:%S"
TIMER_MS = 500
TIMER_MS = 10
TRACK_TIME_FORMAT = "%H:%M:%S"
VOLUME_VLC_DEFAULT = 75
VOLUME_VLC_DROP3db = 65

View File

@ -1,3 +1,4 @@
import numpy as np
import os
import psutil
import shutil

View File

@ -4,11 +4,14 @@ from log import log
from os.path import basename
import argparse
import os
import numpy as np
import pyqtgraph as pg # type: ignore
import stackprinter # type: ignore
import subprocess
import sys
import threading
import icons_rc
from datetime import datetime, timedelta
from pygame import mixer
from time import sleep
@ -140,78 +143,63 @@ class CartButton(QPushButton):
self.pgb.setGeometry(0, 0, self.width(), 10)
class PlaylistTrack:
"""
Used to provide a single reference point for specific playlist tracks,
typically the previous, current and next track.
"""
class FadeCurve:
GraphWidget = None
def __init__(self) -> None:
def __init__(self, track):
"""
Only initialises data structure. Call set_plr to populate.
Do NOT store row_number here - that changes if tracks are reordered
in playlist (add, remove, drag/drop) and we shouldn't care about row
number: that's the playlist's problem.
Set up fade graph array
"""
self.artist: Optional[str] = None
self.duration: Optional[int] = None
self.end_time: Optional[datetime] = None
self.fade_at: Optional[int] = None
self.fade_length: Optional[int] = None
self.path: Optional[str] = None
self.playlist_id: Optional[int] = None
self.playlist_tab: Optional[PlaylistTab] = None
self.plr_id: Optional[int] = None
self.silence_at: Optional[int] = None
self.start_gap: Optional[int] = None
self.start_time: Optional[datetime] = None
self.title: Optional[str] = None
self.track_id: Optional[int] = None
audio = helpers.get_audio_segment(track.path)
if not audio:
return None
def __repr__(self) -> str:
return (
f"<PlaylistTrack(title={self.title}, artist={self.artist}, "
f"playlist_id={self.playlist_id}>"
# Start point of curve is Config.FADE_CURVE_MS_BEFORE_FADE
# milliseconds before fade starts to silence
self.start_ms = max(
0, track.fade_at - Config.FADE_CURVE_MS_BEFORE_FADE - 1)
self.end_ms = track.silence_at
self.audio_segment = audio[self.start_ms:self.end_ms]
self.graph_array = np.array(self.audio_segment.get_array_of_samples())
# Calculate the factor to map milliseconds of track to array
self.ms_to_array_factor = (
len(self.graph_array) / (self.end_ms - self.start_ms)
)
def set_plr(self, session: scoped_session, plr: PlaylistRows,
tab: PlaylistTab) -> None:
"""
Update with new plr information
"""
self.region = None
self.playlist_tab = tab
def clear(self) -> None:
"""Clear the current graph"""
session.add(plr)
track = plr.track
if self.GraphWidget:
self.GraphWidget.clear()
self.artist = track.artist
self.duration = track.duration
self.end_time = None
self.fade_at = track.fade_at
self.path = track.path
self.playlist_id = plr.playlist_id
self.plr_id = plr.id
self.silence_at = track.silence_at
self.start_gap = track.start_gap
self.start_time = None
self.title = track.title
self.track_id = track.id
def plot(self):
self.curve = self.GraphWidget.plot(self.graph_array)
self.curve.setPen(Config.FADE_CURVE_FOREGROUND)
if track.silence_at and track.fade_at:
self.fade_length = track.silence_at - track.fade_at
def tick(self, play_time) -> None:
"""Update volume fade curve"""
def start(self) -> None:
"""
Called when track starts playing
"""
if not self.GraphWidget:
return
self.start_time = datetime.now()
if self.duration:
self.end_time = (
self.start_time + timedelta(milliseconds=self.duration))
ms_of_graph = play_time - self.start_ms
if ms_of_graph < 0:
return
if self.region is None:
# Create the region now that we're into fade
self.region = pg.LinearRegionItem(
[0, 0],
bounds=[0, len(self.graph_array)]
)
self.GraphWidget.addItem(self.region)
# Update region position
self.region.setRegion([0, ms_of_graph * self.ms_to_array_factor])
class ImportTrack(QObject):
@ -261,13 +249,91 @@ class MusicMusterSignals(QObject):
set_next_track_signal = pyqtSignal(int, int)
class PlaylistTrack:
"""
Used to provide a single reference point for specific playlist tracks,
typically the previous, current and next track.
"""
def __init__(self) -> None:
"""
Only initialises data structure. Call set_plr to populate.
Do NOT store row_number here - that changes if tracks are reordered
in playlist (add, remove, drag/drop) and we shouldn't care about row
number: that's the playlist's problem.
"""
self.artist: Optional[str] = None
self.duration: Optional[int] = None
self.end_time: Optional[datetime] = None
self.fade_at: Optional[int] = None
self.fade_curve: Optional[FadeCurve] = None
self.fade_length: Optional[int] = None
self.path: Optional[str] = None
self.playlist_id: Optional[int] = None
self.playlist_tab: Optional[PlaylistTab] = None
self.plr_id: Optional[int] = None
self.silence_at: Optional[int] = None
self.start_gap: Optional[int] = None
self.start_time: Optional[datetime] = None
self.title: Optional[str] = None
self.track_id: Optional[int] = None
def __repr__(self) -> str:
return (
f"<PlaylistTrack(title={self.title}, artist={self.artist}, "
f"playlist_id={self.playlist_id}>"
)
def set_plr(self, session: scoped_session, plr: PlaylistRows,
tab: PlaylistTab) -> None:
"""
Update with new plr information
"""
if not plr.track:
return
self.playlist_tab = tab
session.add(plr)
track = plr.track
self.artist = track.artist
self.duration = track.duration
self.end_time = None
self.fade_at = track.fade_at
self.fade_graph = FadeCurve(track)
self.path = track.path
self.playlist_id = plr.playlist_id
self.plr_id = plr.id
self.silence_at = track.silence_at
self.start_gap = track.start_gap
self.start_time = None
self.title = track.title
self.track_id = track.id
if track.silence_at and track.fade_at:
self.fade_length = track.silence_at - track.fade_at
def start(self) -> None:
"""
Called when track starts playing
"""
self.start_time = datetime.now()
if self.duration:
self.end_time = (
self.start_time + timedelta(milliseconds=self.duration))
class Window(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.setupUi(self)
self.timer: QTimer = QTimer()
self.even_tick: bool = True
self.clock_counter: int = 0
self.music: music.Music = music.Music()
self.playing: bool = False
@ -289,6 +355,10 @@ class Window(QMainWindow, Ui_MainWindow):
self.txtSearch.setHidden(True)
self.hide_played_tracks = False
mixer.init()
self.widgetFadeVolume.hideAxis('bottom')
self.widgetFadeVolume.hideAxis('left')
self.widgetFadeVolume.setBackground(Config.FADE_CURVE_BACKGROUND)
FadeCurve.GraphWidget = self.widgetFadeVolume
self.visible_playlist_tab: Callable[[], PlaylistTab] = \
self.tabPlaylist.currentWidget
@ -300,6 +370,7 @@ class Window(QMainWindow, Ui_MainWindow):
else:
self.carts_init()
self.enable_play_next_controls()
self.clock_counter = 0
self.timer.start(Config.TIMER_MS)
self.connect_signals_slots()
@ -751,6 +822,7 @@ class Window(QMainWindow, Ui_MainWindow):
- Tell playlist_tab track has finished
- Reset PlaylistTrack objects
- Reset clocks
- Reset fade graph
- Update headers
- Enable controls
"""
@ -763,6 +835,9 @@ class Window(QMainWindow, Ui_MainWindow):
if self.current_track.playlist_tab:
self.current_track.playlist_tab.play_ended()
# Reset fade graph
self.current_track.fade_graph.clear()
# Reset PlaylistTrack objects
if self.current_track.track_id:
self.previous_track = self.current_track
@ -775,21 +850,6 @@ class Window(QMainWindow, Ui_MainWindow):
self.label_end_timer.setText("00:00")
self.label_fade_timer.setText("00:00")
self.label_silent_timer.setText("00:00")
self.label_start_time.setText("00:00:00")
self.label_end_time.setText("00:00:00")
if self.next_track.track_id:
self.label_track_length.setText(
helpers.ms_to_mmss(self.next_track.duration)
)
if self.next_track.silence_at and self.next_track.fade_at:
self.label_fade_length.setText(helpers.ms_to_mmss(
self.next_track.silence_at - self.next_track.fade_at))
else:
self.label_fade_length.setText("0:00")
else:
self.label_track_length.setText("0:00")
self.label_fade_length.setText("0:00")
# Update headers
self.update_headers()
@ -1249,6 +1309,9 @@ class Window(QMainWindow, Ui_MainWindow):
break
sleep(0.1)
# Show closing volume graph
self.current_track.fade_graph.plot()
# Tell database to record it as played
Playdates(session, self.current_track.track_id)
@ -1265,21 +1328,6 @@ class Window(QMainWindow, Ui_MainWindow):
# Update headers
self.update_headers()
# Update clocks
self.label_track_length.setText(
helpers.ms_to_mmss(self.current_track.duration)
)
self.label_fade_length.setText(
helpers.ms_to_mmss(self.current_track.fade_length))
if self.current_track.start_time:
self.label_start_time.setText(
self.current_track.start_time.strftime(
Config.TRACK_TIME_FORMAT))
if self.current_track.end_time:
self.label_end_time.setText(
self.current_track.end_time.strftime(
Config.TRACK_TIME_FORMAT))
def preview(self) -> None:
"""
Preview selected or next track. We use a different mechanism to
@ -1548,7 +1596,7 @@ class Window(QMainWindow, Ui_MainWindow):
def set_next_plr_id(self, next_plr_id: Optional[int],
playlist_tab: PlaylistTab) -> None:
"""
Set passed plr_id as next track to play, or clear next track if None
Set passed plr_id as next track to play, or clear next track if None
Actions required:
- Update self.next_track PlaylistTrack structure
@ -1625,8 +1673,13 @@ class Window(QMainWindow, Ui_MainWindow):
"""
Carry out clock tick actions.
self.clock_counter is incrememted at each tick (100ms), and this
value is used to determine the actions to take.
The Fade Volume graph is updated every 10ms.
The Time of Day clock and any cart progress bars are updated
every tick (500ms).
every 500ms.
All other timers are updated every second. As the timer displays
have a one-second resolution, updating every 500ms can result in
@ -1634,6 +1687,7 @@ class Window(QMainWindow, Ui_MainWindow):
updating. That looks odd.
Actions required:
- Update Fade Volume graph
- Update TOD clock
- Call cart_tick
- If track is playing:
@ -1642,74 +1696,85 @@ class Window(QMainWindow, Ui_MainWindow):
run stop_track
"""
# Update TOD clock
self.lblTOD.setText(datetime.now().strftime(Config.TOD_TIME_FORMAT))
# Update carts
self.cart_tick()
# Update volume fade curve
if (
self.current_track.track_id and
self.current_track.fade_graph and
self.current_track.start_time
):
play_time = (
datetime.now() - self.current_track.start_time
).total_seconds() * 1000
self.current_track.fade_graph.tick(play_time)
self.even_tick = not self.even_tick
if not self.even_tick:
return
if not self.playing:
return
if self.clock_counter % 20 == 0:
# Update TOD clock
self.lblTOD.setText(datetime.now().strftime(
Config.TOD_TIME_FORMAT))
# Update carts
self.cart_tick()
# If track is playing, update track clocks time and colours
# There is a discrete time between starting playing a track and
# player.is_playing() returning True, so assume playing if less
# than Config.PLAY_SETTLE microseconds have passed since
# starting play.
if self.music.player and self.current_track.start_time and (
self.music.player.is_playing() or
(datetime.now() - self.current_track.start_time)
< timedelta(microseconds=Config.PLAY_SETTLE)):
playtime = self.music.get_playtime()
time_to_fade = (self.current_track.fade_at - playtime)
time_to_silence = (
self.current_track.silence_at - playtime)
time_to_end = (self.current_track.duration - playtime)
if self.clock_counter % 50 == 0:
if not self.playing:
return
# Elapsed time
self.label_elapsed_timer.setText(helpers.ms_to_mmss(playtime))
# If track is playing, update track clocks time and colours
# There is a discrete time between starting playing a track and
# player.is_playing() returning True, so assume playing if less
# than Config.PLAY_SETTLE microseconds have passed since
# starting play.
if self.music.player and self.current_track.start_time and (
self.music.player.is_playing() or
(datetime.now() - self.current_track.start_time)
< timedelta(microseconds=Config.PLAY_SETTLE)):
playtime = self.music.get_playtime()
time_to_fade = (self.current_track.fade_at - playtime)
time_to_silence = (
self.current_track.silence_at - playtime)
time_to_end = (self.current_track.duration - playtime)
# Time to fade
self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade))
# Elapsed time
self.label_elapsed_timer.setText(helpers.ms_to_mmss(playtime))
# If silent in the next 5 seconds, put warning colour on
# time to silence box and enable play controls
if time_to_silence <= 5500:
self.frame_silent.setStyleSheet(
f"background: {Config.COLOUR_ENDING_TIMER}"
# Time to fade
self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade))
# If silent in the next 5 seconds, put warning colour on
# time to silence box and enable play controls
if time_to_silence <= 5500:
self.frame_silent.setStyleSheet(
f"background: {Config.COLOUR_ENDING_TIMER}"
)
self.enable_play_next_controls()
# Set warning colour on time to silence box when fade starts
elif time_to_fade <= 500:
self.frame_silent.setStyleSheet(
f"background: {Config.COLOUR_WARNING_TIMER}"
)
# Five seconds before fade starts, set warning colour on
# time to silence box and enable play controls
elif time_to_fade <= 5500:
self.frame_fade.setStyleSheet(
f"background: {Config.COLOUR_WARNING_TIMER}"
)
self.enable_play_next_controls()
else:
self.frame_silent.setStyleSheet("")
self.frame_fade.setStyleSheet("")
self.label_silent_timer.setText(
helpers.ms_to_mmss(time_to_silence)
)
self.enable_play_next_controls()
# Set warning colour on time to silence box when fade starts
elif time_to_fade <= 500:
self.frame_silent.setStyleSheet(
f"background: {Config.COLOUR_WARNING_TIMER}"
)
# Five seconds before fade starts, set warning colour on
# time to silence box and enable play controls
elif time_to_fade <= 5500:
self.frame_fade.setStyleSheet(
f"background: {Config.COLOUR_WARNING_TIMER}"
)
self.enable_play_next_controls()
# Time to end
self.label_end_timer.setText(helpers.ms_to_mmss(time_to_end))
# Autoplay next track
# if time_to_silence <= 1500:
# self.play_next()
else:
self.frame_silent.setStyleSheet("")
self.frame_fade.setStyleSheet("")
self.label_silent_timer.setText(
helpers.ms_to_mmss(time_to_silence)
)
# Time to end
self.label_end_timer.setText(helpers.ms_to_mmss(time_to_end))
# Autoplay next track
# if time_to_silence <= 1500:
# self.play_next()
else:
if self.playing:
self.stop_playing()
if self.playing:
self.stop_playing()
def update_headers(self) -> None:
"""

File diff suppressed because it is too large Load Diff

View File

@ -349,6 +349,12 @@ padding-left: 8px;</string>
</item>
<item row="5" column="0">
<widget class="QFrame" name="InfoFooterFrame">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(192, 191, 188)</string>
</property>
@ -365,8 +371,14 @@ padding-left: 8px;</string>
<widget class="QFrame" name="FadeStopInfoFrame">
<property name="minimumSize">
<size>
<width>321</width>
<height>0</height>
<width>152</width>
<height>112</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>184</width>
<height>16777215</height>
</size>
</property>
<property name="frameShape">
@ -376,28 +388,20 @@ padding-left: 8px;</string>
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Fade length:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_x_2">
<property name="text">
<string>Start:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="0" column="0" colspan="2">
<widget class="QPushButton" name="btnFade">
<property name="minimumSize">
<size>
<width>132</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>164</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string> Fade</string>
</property>
@ -413,92 +417,14 @@ padding-left: 8px;</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_start_time">
<property name="font">
<font>
<family>FreeSans</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>00:00:00</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_end_time">
<property name="font">
<font>
<family>FreeSans</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>00:00:00</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_x">
<property name="text">
<string>Track length:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_fade_length">
<property name="font">
<font>
<family>FreeSans</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>0:00</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>End:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_track_length">
<property name="font">
<font>
<family>FreeSans</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>0:00</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2">
<item row="1" column="0" colspan="2">
<widget class="QPushButton" name="btnPreview">
<property name="minimumSize">
<size>
<width>132</width>
<height>36</height>
</size>
</property>
<property name="text">
<string> Preview</string>
</property>
@ -524,7 +450,7 @@ padding-left: 8px;</string>
<widget class="QFrame" name="frame_elapsed">
<property name="minimumSize">
<size>
<width>0</width>
<width>152</width>
<height>112</height>
</size>
</property>
@ -576,7 +502,7 @@ padding-left: 8px;</string>
<widget class="QFrame" name="frame_fade">
<property name="minimumSize">
<size>
<width>0</width>
<width>152</width>
<height>112</height>
</size>
</property>
@ -622,10 +548,26 @@ padding-left: 8px;</string>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_silent">
<widget class="PlotWidget" name="widgetFadeVolume" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_silent">
<property name="minimumSize">
<size>
<width>152</width>
<height>112</height>
</size>
</property>
@ -674,7 +616,7 @@ padding-left: 8px;</string>
<widget class="QFrame" name="frame_end">
<property name="minimumSize">
<size>
<width>0</width>
<width>152</width>
<height>112</height>
</size>
</property>
@ -1228,6 +1170,12 @@ padding-left: 8px;</string>
<header>infotabs</header>
<container>1</container>
</customwidget>
<customwidget>
<class>PlotWidget</class>
<extends>QWidget</extends>
<header>pyqtgraph</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="icons.qrc"/>

View File

@ -179,6 +179,7 @@ class Ui_MainWindow(object):
self.tabInfolist.setObjectName("tabInfolist")
self.gridLayout_4.addWidget(self.splitter, 4, 0, 1, 1)
self.InfoFooterFrame = QtWidgets.QFrame(parent=self.centralwidget)
self.InfoFooterFrame.setMaximumSize(QtCore.QSize(16777215, 16777215))
self.InfoFooterFrame.setStyleSheet("background-color: rgb(192, 191, 188)")
self.InfoFooterFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.InfoFooterFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
@ -188,78 +189,34 @@ class Ui_MainWindow(object):
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.FadeStopInfoFrame = QtWidgets.QFrame(parent=self.InfoFooterFrame)
self.FadeStopInfoFrame.setMinimumSize(QtCore.QSize(321, 0))
self.FadeStopInfoFrame.setMinimumSize(QtCore.QSize(152, 112))
self.FadeStopInfoFrame.setMaximumSize(QtCore.QSize(184, 16777215))
self.FadeStopInfoFrame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.FadeStopInfoFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
self.FadeStopInfoFrame.setObjectName("FadeStopInfoFrame")
self.gridLayout = QtWidgets.QGridLayout(self.FadeStopInfoFrame)
self.gridLayout.setObjectName("gridLayout")
self.label_7 = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
self.label_7.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.label_7.setObjectName("label_7")
self.gridLayout.addWidget(self.label_7, 1, 0, 1, 1)
self.label_x_2 = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
self.label_x_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.label_x_2.setObjectName("label_x_2")
self.gridLayout.addWidget(self.label_x_2, 0, 2, 1, 1)
self.btnFade = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
self.btnFade.setMinimumSize(QtCore.QSize(132, 36))
self.btnFade.setMaximumSize(QtCore.QSize(164, 16777215))
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/icons/fade"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.btnFade.setIcon(icon1)
self.btnFade.setIconSize(QtCore.QSize(30, 30))
self.btnFade.setObjectName("btnFade")
self.gridLayout.addWidget(self.btnFade, 2, 0, 1, 2)
self.label_start_time = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
font = QtGui.QFont()
font.setFamily("FreeSans")
font.setPointSize(16)
self.label_start_time.setFont(font)
self.label_start_time.setScaledContents(False)
self.label_start_time.setObjectName("label_start_time")
self.gridLayout.addWidget(self.label_start_time, 0, 3, 1, 1)
self.label_end_time = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
font = QtGui.QFont()
font.setFamily("FreeSans")
font.setPointSize(16)
self.label_end_time.setFont(font)
self.label_end_time.setScaledContents(False)
self.label_end_time.setObjectName("label_end_time")
self.gridLayout.addWidget(self.label_end_time, 1, 3, 1, 1)
self.label_x = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
self.label_x.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.label_x.setObjectName("label_x")
self.gridLayout.addWidget(self.label_x, 0, 0, 1, 1)
self.label_fade_length = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
font = QtGui.QFont()
font.setFamily("FreeSans")
font.setPointSize(16)
self.label_fade_length.setFont(font)
self.label_fade_length.setScaledContents(False)
self.label_fade_length.setObjectName("label_fade_length")
self.gridLayout.addWidget(self.label_fade_length, 1, 1, 1, 1)
self.label_8 = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
self.label_8.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.label_8.setObjectName("label_8")
self.gridLayout.addWidget(self.label_8, 1, 2, 1, 1)
self.label_track_length = QtWidgets.QLabel(parent=self.FadeStopInfoFrame)
font = QtGui.QFont()
font.setFamily("FreeSans")
font.setPointSize(16)
self.label_track_length.setFont(font)
self.label_track_length.setScaledContents(False)
self.label_track_length.setObjectName("label_track_length")
self.gridLayout.addWidget(self.label_track_length, 0, 1, 1, 1)
self.gridLayout.addWidget(self.btnFade, 0, 0, 1, 2)
self.btnPreview = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
self.btnPreview.setMinimumSize(QtCore.QSize(132, 36))
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/icons/headphones"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.btnPreview.setIcon(icon2)
self.btnPreview.setIconSize(QtCore.QSize(30, 30))
self.btnPreview.setCheckable(True)
self.btnPreview.setObjectName("btnPreview")
self.gridLayout.addWidget(self.btnPreview, 2, 2, 1, 2)
self.gridLayout.addWidget(self.btnPreview, 1, 0, 1, 2)
self.horizontalLayout.addWidget(self.FadeStopInfoFrame)
self.frame_elapsed = QtWidgets.QFrame(parent=self.InfoFooterFrame)
self.frame_elapsed.setMinimumSize(QtCore.QSize(0, 112))
self.frame_elapsed.setMinimumSize(QtCore.QSize(152, 112))
self.frame_elapsed.setStyleSheet("")
self.frame_elapsed.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame_elapsed.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
@ -283,7 +240,7 @@ class Ui_MainWindow(object):
self.verticalLayout_4.addWidget(self.label_elapsed_timer)
self.horizontalLayout.addWidget(self.frame_elapsed)
self.frame_fade = QtWidgets.QFrame(parent=self.InfoFooterFrame)
self.frame_fade.setMinimumSize(QtCore.QSize(0, 112))
self.frame_fade.setMinimumSize(QtCore.QSize(152, 112))
self.frame_fade.setStyleSheet("")
self.frame_fade.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame_fade.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
@ -305,8 +262,17 @@ class Ui_MainWindow(object):
self.label_fade_timer.setObjectName("label_fade_timer")
self.verticalLayout_2.addWidget(self.label_fade_timer)
self.horizontalLayout.addWidget(self.frame_fade)
self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widgetFadeVolume.sizePolicy().hasHeightForWidth())
self.widgetFadeVolume.setSizePolicy(sizePolicy)
self.widgetFadeVolume.setMinimumSize(QtCore.QSize(0, 0))
self.widgetFadeVolume.setObjectName("widgetFadeVolume")
self.horizontalLayout.addWidget(self.widgetFadeVolume)
self.frame_silent = QtWidgets.QFrame(parent=self.InfoFooterFrame)
self.frame_silent.setMinimumSize(QtCore.QSize(0, 112))
self.frame_silent.setMinimumSize(QtCore.QSize(152, 112))
self.frame_silent.setStyleSheet("")
self.frame_silent.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame_silent.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
@ -329,7 +295,7 @@ class Ui_MainWindow(object):
self.verticalLayout_5.addWidget(self.label_silent_timer)
self.horizontalLayout.addWidget(self.frame_silent)
self.frame_end = QtWidgets.QFrame(parent=self.InfoFooterFrame)
self.frame_end.setMinimumSize(QtCore.QSize(0, 112))
self.frame_end.setMinimumSize(QtCore.QSize(152, 112))
self.frame_end.setStyleSheet("")
self.frame_end.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame_end.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
@ -574,15 +540,7 @@ class Ui_MainWindow(object):
self.current_track_2.setText(_translate("MainWindow", "Current track:"))
self.next_track_2.setText(_translate("MainWindow", "Next track:"))
self.lblTOD.setText(_translate("MainWindow", "00:00:00"))
self.label_7.setText(_translate("MainWindow", "Fade length:"))
self.label_x_2.setText(_translate("MainWindow", "Start:"))
self.btnFade.setText(_translate("MainWindow", " Fade"))
self.label_start_time.setText(_translate("MainWindow", "00:00:00"))
self.label_end_time.setText(_translate("MainWindow", "00:00:00"))
self.label_x.setText(_translate("MainWindow", "Track length:"))
self.label_fade_length.setText(_translate("MainWindow", "0:00"))
self.label_8.setText(_translate("MainWindow", "End:"))
self.label_track_length.setText(_translate("MainWindow", "0:00"))
self.btnPreview.setText(_translate("MainWindow", " Preview"))
self.label.setText(_translate("MainWindow", "Elapsed time"))
self.label_elapsed_timer.setText(_translate("MainWindow", "00:00"))
@ -664,3 +622,4 @@ class Ui_MainWindow(object):
self.actionSearch_title_in_Songfacts.setText(_translate("MainWindow", "Search title in Songfacts"))
self.actionSearch_title_in_Songfacts.setShortcut(_translate("MainWindow", "Ctrl+S"))
from infotabs import InfoTabs
from pyqtgraph import PlotWidget

55
poetry.lock generated
View File

@ -631,6 +631,44 @@ files = [
{file = "mysqlclient-2.1.1.tar.gz", hash = "sha256:828757e419fb11dd6c5ed2576ec92c3efaa93a0f7c39e263586d1ee779c3d782"},
]
[[package]]
name = "numpy"
version = "1.24.3"
description = "Fundamental package for array computing in Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "numpy-1.24.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c1104d3c036fb81ab923f507536daedc718d0ad5a8707c6061cdfd6d184e570"},
{file = "numpy-1.24.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:202de8f38fc4a45a3eea4b63e2f376e5f2dc64ef0fa692838e31a808520efaf7"},
{file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8535303847b89aa6b0f00aa1dc62867b5a32923e4d1681a35b5eef2d9591a463"},
{file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d926b52ba1367f9acb76b0df6ed21f0b16a1ad87c6720a1121674e5cf63e2b6"},
{file = "numpy-1.24.3-cp310-cp310-win32.whl", hash = "sha256:f21c442fdd2805e91799fbe044a7b999b8571bb0ab0f7850d0cb9641a687092b"},
{file = "numpy-1.24.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f23af8c16022663a652d3b25dcdc272ac3f83c3af4c02eb8b824e6b3ab9d7"},
{file = "numpy-1.24.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a7721ec204d3a237225db3e194c25268faf92e19338a35f3a224469cb6039a3"},
{file = "numpy-1.24.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6cc757de514c00b24ae8cf5c876af2a7c3df189028d68c0cb4eaa9cd5afc2bf"},
{file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e3f4e85fc5d4fd311f6e9b794d0c00e7002ec122be271f2019d63376f1d385"},
{file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d3c026f57ceaad42f8231305d4653d5f05dc6332a730ae5c0bea3513de0950"},
{file = "numpy-1.24.3-cp311-cp311-win32.whl", hash = "sha256:c91c4afd8abc3908e00a44b2672718905b8611503f7ff87390cc0ac3423fb096"},
{file = "numpy-1.24.3-cp311-cp311-win_amd64.whl", hash = "sha256:5342cf6aad47943286afa6f1609cad9b4266a05e7f2ec408e2cf7aea7ff69d80"},
{file = "numpy-1.24.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7776ea65423ca6a15255ba1872d82d207bd1e09f6d0894ee4a64678dd2204078"},
{file = "numpy-1.24.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae8d0be48d1b6ed82588934aaaa179875e7dc4f3d84da18d7eae6eb3f06c242c"},
{file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecde0f8adef7dfdec993fd54b0f78183051b6580f606111a6d789cd14c61ea0c"},
{file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4749e053a29364d3452c034827102ee100986903263e89884922ef01a0a6fd2f"},
{file = "numpy-1.24.3-cp38-cp38-win32.whl", hash = "sha256:d933fabd8f6a319e8530d0de4fcc2e6a61917e0b0c271fded460032db42a0fe4"},
{file = "numpy-1.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:56e48aec79ae238f6e4395886b5eaed058abb7231fb3361ddd7bfdf4eed54289"},
{file = "numpy-1.24.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4719d5aefb5189f50887773699eaf94e7d1e02bf36c1a9d353d9f46703758ca4"},
{file = "numpy-1.24.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187"},
{file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8282b9bcfe2b5e7d491d0bf7f3e2da29700cec05b49e64d6246923329f2b02"},
{file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210461d87fb02a84ef243cac5e814aad2b7f4be953b32cb53327bb49fd77fbb4"},
{file = "numpy-1.24.3-cp39-cp39-win32.whl", hash = "sha256:784c6da1a07818491b0ffd63c6bbe5a33deaa0e25a20e1b3ea20cf0e43f8046c"},
{file = "numpy-1.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:d5036197ecae68d7f491fcdb4df90082b0d4960ca6599ba2659957aafced7c17"},
{file = "numpy-1.24.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:352ee00c7f8387b44d19f4cada524586f07379c0d49270f87233983bc5087ca0"},
{file = "numpy-1.24.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d6acc2e7524c9955e5c903160aa4ea083736fde7e91276b0e5d98e6332812"},
{file = "numpy-1.24.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:35400e6a8d102fd07c71ed7dcadd9eb62ee9a6e84ec159bd48c28235bbb0f8e4"},
{file = "numpy-1.24.3.tar.gz", hash = "sha256:ab344f1bf21f140adab8e47fdbc7c35a477dc01408791f8ba00d018dd0bc5155"},
]
[[package]]
name = "obsws-python"
version = "1.4.2"
@ -1134,6 +1172,21 @@ files = [
{file = "PyQt6_WebEngine_Qt6-6.5.0-py3-none-win_amd64.whl", hash = "sha256:5acadcc6608df8d9eba385e04ced2fc88e7eb92e366556ee4ac3c57a02c00088"},
]
[[package]]
name = "pyqtgraph"
version = "0.13.3"
description = "Scientific Graphics and GUI Library for Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "pyqtgraph-0.13.3-py3-none-any.whl", hash = "sha256:fdcc04ac4b32a7bedf1bf3cf74cbb93ab3ba5687791712bbfa8d0712377d2f2b"},
{file = "pyqtgraph-0.13.3.tar.gz", hash = "sha256:58108d8411c7054e0841d8b791ee85e101fc296b9b359c0e01dde38a98ff2ace"},
]
[package.dependencies]
numpy = ">=1.20.0"
[[package]]
name = "pyqtwebengine"
version = "5.15.6"
@ -1606,4 +1659,4 @@ test = ["websockets"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "5b335197cebe3355472e3d0024a978e4853efce33802008987351b5196d8746d"
content-hash = "e195143fc7756994b10a3c4472a17f2288829ebd6a75ef75f6b683905cb8a543"

View File

@ -26,6 +26,7 @@ obsws-python = "^1.4.2"
pyqt6 = "^6.5.0"
pyqt6-webengine = "^6.5.0"
pygame = "^2.4.0"
pyqtgraph = "^0.13.3"
[tool.poetry.dev-dependencies]
ipdb = "^0.13.9"