Compare commits
7 Commits
30d8b0d5c8
...
234f6fcdbb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
234f6fcdbb | ||
|
|
7658dc354c | ||
|
|
3c884e54ca | ||
|
|
d7a37151b7 | ||
|
|
96080cdca0 | ||
|
|
434e45b080 | ||
|
|
829172177c |
@ -37,10 +37,11 @@ class Config(object):
|
|||||||
DEBUG_MODULES: List[Optional[str]] = []
|
DEBUG_MODULES: List[Optional[str]] = []
|
||||||
DEFAULT_COLUMN_WIDTH = 200
|
DEFAULT_COLUMN_WIDTH = 200
|
||||||
DISPLAY_SQL = False
|
DISPLAY_SQL = False
|
||||||
EPOCH = dt.datetime(1970, 1, 1)
|
|
||||||
ENGINE_OPTIONS = dict(pool_pre_ping=True)
|
ENGINE_OPTIONS = dict(pool_pre_ping=True)
|
||||||
|
EPOCH = dt.datetime(1970, 1, 1)
|
||||||
ERRORS_FROM = ["noreply@midnighthax.com"]
|
ERRORS_FROM = ["noreply@midnighthax.com"]
|
||||||
ERRORS_TO = ["kae@midnighthax.com"]
|
ERRORS_TO = ["kae@midnighthax.com"]
|
||||||
|
EXTERNAL_BROWSER_PATH = "/usr/bin/vivaldi"
|
||||||
FADE_CURVE_BACKGROUND = "lightyellow"
|
FADE_CURVE_BACKGROUND = "lightyellow"
|
||||||
FADE_CURVE_FOREGROUND = "blue"
|
FADE_CURVE_FOREGROUND = "blue"
|
||||||
FADE_CURVE_MS_BEFORE_FADE = 5000
|
FADE_CURVE_MS_BEFORE_FADE = 5000
|
||||||
@ -90,10 +91,12 @@ class Config(object):
|
|||||||
ROOT = os.environ.get("ROOT") or "/home/kae/music"
|
ROOT = os.environ.get("ROOT") or "/home/kae/music"
|
||||||
ROWS_FROM_ZERO = True
|
ROWS_FROM_ZERO = True
|
||||||
SCROLL_TOP_MARGIN = 3
|
SCROLL_TOP_MARGIN = 3
|
||||||
|
SONGFACTS_ON_NEXT = False
|
||||||
START_GAP_WARNING_THRESHOLD = 300
|
START_GAP_WARNING_THRESHOLD = 300
|
||||||
TEXT_NO_TRACK_NO_NOTE = "[Section header]"
|
TEXT_NO_TRACK_NO_NOTE = "[Section header]"
|
||||||
TOD_TIME_FORMAT = "%H:%M:%S"
|
TOD_TIME_FORMAT = "%H:%M:%S"
|
||||||
TRACK_TIME_FORMAT = "%H:%M:%S"
|
TRACK_TIME_FORMAT = "%H:%M:%S"
|
||||||
|
USE_INTERNAL_BROWSER = False
|
||||||
VLC_MAIN_PLAYER_NAME = "MusicMuster Main Player"
|
VLC_MAIN_PLAYER_NAME = "MusicMuster Main Player"
|
||||||
VLC_PREVIEW_PLAYER_NAME = "MusicMuster Preview Player"
|
VLC_PREVIEW_PLAYER_NAME = "MusicMuster Preview Player"
|
||||||
VLC_VOLUME_DEFAULT = 75
|
VLC_VOLUME_DEFAULT = 75
|
||||||
@ -101,6 +104,7 @@ class Config(object):
|
|||||||
WARNING_MS_BEFORE_FADE = 5500
|
WARNING_MS_BEFORE_FADE = 5500
|
||||||
WARNING_MS_BEFORE_SILENCE = 5500
|
WARNING_MS_BEFORE_SILENCE = 5500
|
||||||
WEB_ZOOM_FACTOR = 1.2
|
WEB_ZOOM_FACTOR = 1.2
|
||||||
|
WIKIPEDIA_ON_NEXT = False
|
||||||
|
|
||||||
# These rely on earlier definitions
|
# These rely on earlier definitions
|
||||||
IMPORT_DESTINATION = os.path.join(ROOT, "Singles")
|
IMPORT_DESTINATION = os.path.join(ROOT, "Singles")
|
||||||
|
|||||||
@ -10,7 +10,6 @@ from PyQt6.QtWidgets import (
|
|||||||
QListWidgetItem,
|
QListWidgetItem,
|
||||||
QMainWindow,
|
QMainWindow,
|
||||||
QTableWidgetItem,
|
QTableWidgetItem,
|
||||||
QWidget,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Third party imports
|
# Third party imports
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
# Standard library imports
|
# Standard library imports
|
||||||
import urllib.parse
|
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
from slugify import slugify # type: ignore
|
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
# PyQt imports
|
# PyQt imports
|
||||||
@ -13,8 +11,6 @@ from PyQt6.QtWidgets import QTabWidget, QWidget
|
|||||||
|
|
||||||
# App imports
|
# App imports
|
||||||
from config import Config
|
from config import Config
|
||||||
from classes import MusicMusterSignals
|
|
||||||
from log import log
|
|
||||||
|
|
||||||
|
|
||||||
class InfoTabs(QTabWidget):
|
class InfoTabs(QTabWidget):
|
||||||
@ -25,9 +21,7 @@ class InfoTabs(QTabWidget):
|
|||||||
def __init__(self, parent: Optional[QWidget] = None) -> None:
|
def __init__(self, parent: Optional[QWidget] = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.signals = MusicMusterSignals()
|
if Config.USE_INTERNAL_BROWSER:
|
||||||
self.signals.search_songfacts_signal.connect(self.open_in_songfacts)
|
|
||||||
self.signals.search_wikipedia_signal.connect(self.open_in_wikipedia)
|
|
||||||
# re-use the oldest one later)
|
# re-use the oldest one later)
|
||||||
self.last_update: Dict[QWebEngineView, dt.datetime] = {}
|
self.last_update: Dict[QWebEngineView, dt.datetime] = {}
|
||||||
self.tabtitles: Dict[int, str] = {}
|
self.tabtitles: Dict[int, str] = {}
|
||||||
@ -39,24 +33,6 @@ class InfoTabs(QTabWidget):
|
|||||||
self.last_update[widget] = dt.datetime.now()
|
self.last_update[widget] = dt.datetime.now()
|
||||||
_ = self.addTab(widget, "")
|
_ = self.addTab(widget, "")
|
||||||
|
|
||||||
def open_in_songfacts(self, title: str) -> None:
|
|
||||||
"""Search Songfacts for title"""
|
|
||||||
|
|
||||||
slug = slugify(title, replacements=([["'", ""]]))
|
|
||||||
log.info(f"Songfacts Infotab for {title=}")
|
|
||||||
url = f"https://www.songfacts.com/search/songs/{slug}"
|
|
||||||
|
|
||||||
self.open_tab(url, title)
|
|
||||||
|
|
||||||
def open_in_wikipedia(self, title: str) -> None:
|
|
||||||
"""Search Wikipedia for title"""
|
|
||||||
|
|
||||||
str = urllib.parse.quote_plus(title)
|
|
||||||
log.info(f"Wikipedia Infotab for {title=}")
|
|
||||||
url = f"https://www.wikipedia.org/w/index.php?search={str}"
|
|
||||||
|
|
||||||
self.open_tab(url, title)
|
|
||||||
|
|
||||||
def open_tab(self, url: str, title: str) -> None:
|
def open_tab(self, url: str, title: str) -> None:
|
||||||
"""
|
"""
|
||||||
Open passed URL. If URL currently displayed, switch to that tab.
|
Open passed URL. If URL currently displayed, switch to that tab.
|
||||||
|
|||||||
139
app/musicmuster.py
Executable file → Normal file
139
app/musicmuster.py
Executable file → Normal file
@ -7,8 +7,11 @@ import argparse
|
|||||||
import datetime as dt
|
import datetime as dt
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from slugify import slugify # type: ignore
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import urllib.parse
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
# PyQt imports
|
# PyQt imports
|
||||||
from PyQt6.QtCore import (
|
from PyQt6.QtCore import (
|
||||||
@ -238,7 +241,9 @@ class PreviewManager:
|
|||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
track = session.get(Tracks, self.track_id)
|
track = session.get(Tracks, self.track_id)
|
||||||
if not track:
|
if not track:
|
||||||
raise ValueError(f"PreviewManager: unable to retreive track {self.track_id=}")
|
raise ValueError(
|
||||||
|
f"PreviewManager: unable to retreive track {self.track_id=}"
|
||||||
|
)
|
||||||
self.intro = track.intro
|
self.intro = track.intro
|
||||||
self.path = track.path
|
self.path = track.path
|
||||||
|
|
||||||
@ -258,7 +263,9 @@ class PreviewManager:
|
|||||||
|
|
||||||
|
|
||||||
class Window(QMainWindow, Ui_MainWindow):
|
class Window(QMainWindow, Ui_MainWindow):
|
||||||
def __init__(self, parent: Optional[QWidget] = None, *args: list, **kwargs: dict) -> None:
|
def __init__(
|
||||||
|
self, parent: Optional[QWidget] = None, *args: list, **kwargs: dict
|
||||||
|
) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
@ -299,6 +306,13 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.connect_signals_slots()
|
self.connect_signals_slots()
|
||||||
self.catch_return_key = False
|
self.catch_return_key = False
|
||||||
|
|
||||||
|
if not Config.USE_INTERNAL_BROWSER:
|
||||||
|
webbrowser.register(
|
||||||
|
"browser",
|
||||||
|
None,
|
||||||
|
webbrowser.BackgroundBrowser(Config.EXTERNAL_BROWSER_PATH),
|
||||||
|
)
|
||||||
|
|
||||||
# Set up shortcut key for instant logging from keyboard
|
# Set up shortcut key for instant logging from keyboard
|
||||||
self.action_quicklog = QShortcut(QKeySequence("Ctrl+L"), self)
|
self.action_quicklog = QShortcut(QKeySequence("Ctrl+L"), self)
|
||||||
self.action_quicklog.activated.connect(self.quicklog)
|
self.action_quicklog.activated.connect(self.quicklog)
|
||||||
@ -458,7 +472,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.actionPaste.triggered.connect(self.paste_rows)
|
self.actionPaste.triggered.connect(self.paste_rows)
|
||||||
self.actionPlay_next.triggered.connect(self.play_next)
|
self.actionPlay_next.triggered.connect(self.play_next)
|
||||||
self.actionRenamePlaylist.triggered.connect(self.rename_playlist)
|
self.actionRenamePlaylist.triggered.connect(self.rename_playlist)
|
||||||
self.actionReplace_files.triggered.connect(self.replace_files)
|
self.actionReplace_files.triggered.connect(self.import_files)
|
||||||
self.actionResume.triggered.connect(self.resume)
|
self.actionResume.triggered.connect(self.resume)
|
||||||
self.actionSave_as_template.triggered.connect(self.save_as_template)
|
self.actionSave_as_template.triggered.connect(self.save_as_template)
|
||||||
self.actionSearch_title_in_Songfacts.triggered.connect(
|
self.actionSearch_title_in_Songfacts.triggered.connect(
|
||||||
@ -504,6 +518,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.timer100.timeout.connect(self.tick_100ms)
|
self.timer100.timeout.connect(self.tick_100ms)
|
||||||
self.timer1000.timeout.connect(self.tick_1000ms)
|
self.timer1000.timeout.connect(self.tick_1000ms)
|
||||||
|
|
||||||
|
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
|
||||||
|
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
|
||||||
|
|
||||||
def create_playlist(
|
def create_playlist(
|
||||||
self, session: Session, playlist_name: Optional[str] = None
|
self, session: Session, playlist_name: Optional[str] = None
|
||||||
) -> Optional[Playlists]:
|
) -> Optional[Playlists]:
|
||||||
@ -1028,6 +1045,30 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
self.tabPlaylist.setCurrentIndex(idx)
|
self.tabPlaylist.setCurrentIndex(idx)
|
||||||
|
|
||||||
|
def open_songfacts_browser(self, title: str) -> None:
|
||||||
|
"""Search Songfacts for title"""
|
||||||
|
|
||||||
|
slug = slugify(title, replacements=([["'", ""]]))
|
||||||
|
log.info(f"Songfacts browser tab for {title=}")
|
||||||
|
url = f"https://www.songfacts.com/search/songs/{slug}"
|
||||||
|
|
||||||
|
if Config.USE_INTERNAL_BROWSER:
|
||||||
|
self.tabInfolist.open_tab(url, title)
|
||||||
|
else:
|
||||||
|
webbrowser.get('browser').open_new_tab(url)
|
||||||
|
|
||||||
|
def open_wikipedia_browser(self, title: str) -> None:
|
||||||
|
"""Search Wikipedia for title"""
|
||||||
|
|
||||||
|
str = urllib.parse.quote_plus(title)
|
||||||
|
log.info(f"Wikipedia browser tab for {title=}")
|
||||||
|
url = f"https://www.wikipedia.org/w/index.php?search={str}"
|
||||||
|
|
||||||
|
if Config.USE_INTERNAL_BROWSER:
|
||||||
|
self.tabInfolist.open_tab(url, title)
|
||||||
|
else:
|
||||||
|
webbrowser.get('browser').open_new_tab(url)
|
||||||
|
|
||||||
def paste_rows(self) -> None:
|
def paste_rows(self) -> None:
|
||||||
"""
|
"""
|
||||||
Paste earlier cut rows.
|
Paste earlier cut rows.
|
||||||
@ -1082,34 +1123,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Check for inadvertent press of 'return'
|
# Check for inadvertent press of 'return'
|
||||||
if track_sequence.current and self.catch_return_key:
|
if self.return_pressed_in_error():
|
||||||
# Suppress inadvertent double press
|
|
||||||
if (
|
|
||||||
track_sequence.current
|
|
||||||
and track_sequence.current.start_time
|
|
||||||
and track_sequence.current.start_time
|
|
||||||
+ dt.timedelta(milliseconds=Config.RETURN_KEY_DEBOUNCE_MS)
|
|
||||||
> dt.datetime.now()
|
|
||||||
):
|
|
||||||
return
|
|
||||||
# If return is pressed during first PLAY_NEXT_GUARD_MS then
|
|
||||||
# default to NOT playing the next track, else default to
|
|
||||||
# playing it.
|
|
||||||
default_yes: bool = track_sequence.current.start_time is not None and (
|
|
||||||
(dt.datetime.now() - track_sequence.current.start_time).total_seconds()
|
|
||||||
* 1000
|
|
||||||
> Config.PLAY_NEXT_GUARD_MS
|
|
||||||
)
|
|
||||||
if default_yes:
|
|
||||||
msg = "Hit return to play next track now"
|
|
||||||
else:
|
|
||||||
msg = "Press tab to select Yes and hit return to play next track"
|
|
||||||
if not helpers.ask_yes_no(
|
|
||||||
"Play next track",
|
|
||||||
msg,
|
|
||||||
default_yes=default_yes,
|
|
||||||
parent=self,
|
|
||||||
):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Issue #223 concerns a very short pause (maybe 0.1s) sometimes
|
# Issue #223 concerns a very short pause (maybe 0.1s) sometimes
|
||||||
@ -1189,8 +1203,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
# Otherwise get track_id to next track to play
|
# Otherwise get track_id to next track to play
|
||||||
if track_sequence.next:
|
if track_sequence.next:
|
||||||
if track_sequence.next.path:
|
if track_sequence.next.path:
|
||||||
track_info = TrackInfo(track_sequence.next.track_id,
|
track_info = TrackInfo(
|
||||||
track_sequence.next.row_number
|
track_sequence.next.track_id, track_sequence.next.row_number
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
@ -1276,7 +1290,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.tabBar.setTabText(idx, new_name)
|
self.tabBar.setTabText(idx, new_name)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
def replace_files(self) -> None:
|
def import_files(self) -> None:
|
||||||
"""
|
"""
|
||||||
Scan source directory and offer to replace existing files with "similar"
|
Scan source directory and offer to replace existing files with "similar"
|
||||||
files, or import the source file as a new track.
|
files, or import the source file as a new track.
|
||||||
@ -1345,6 +1359,46 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
session.rollback()
|
session.rollback()
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
|
def return_pressed_in_error(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check whether Return key has been pressed in error.
|
||||||
|
|
||||||
|
Return True if it has, False if not
|
||||||
|
"""
|
||||||
|
|
||||||
|
if track_sequence.current and self.catch_return_key:
|
||||||
|
# Suppress inadvertent double press
|
||||||
|
if (
|
||||||
|
track_sequence.current
|
||||||
|
and track_sequence.current.start_time
|
||||||
|
and track_sequence.current.start_time
|
||||||
|
+ dt.timedelta(milliseconds=Config.RETURN_KEY_DEBOUNCE_MS)
|
||||||
|
> dt.datetime.now()
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If return is pressed during first PLAY_NEXT_GUARD_MS then
|
||||||
|
# default to NOT playing the next track, else default to
|
||||||
|
# playing it.
|
||||||
|
default_yes: bool = track_sequence.current.start_time is not None and (
|
||||||
|
(dt.datetime.now() - track_sequence.current.start_time).total_seconds()
|
||||||
|
* 1000
|
||||||
|
> Config.PLAY_NEXT_GUARD_MS
|
||||||
|
)
|
||||||
|
if default_yes:
|
||||||
|
msg = "Hit return to play next track now"
|
||||||
|
else:
|
||||||
|
msg = "Press tab to select Yes and hit return to play next track"
|
||||||
|
if not helpers.ask_yes_no(
|
||||||
|
"Play next track",
|
||||||
|
msg,
|
||||||
|
default_yes=default_yes,
|
||||||
|
parent=self,
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def resume(self) -> None:
|
def resume(self) -> None:
|
||||||
"""
|
"""
|
||||||
Resume playing last track. We may be playing the next track
|
Resume playing last track. We may be playing the next track
|
||||||
@ -1456,13 +1510,18 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
y = Settings.get_setting(session, "mainwindow_y").f_int or 100
|
y = Settings.get_setting(session, "mainwindow_y").f_int or 100
|
||||||
width = Settings.get_setting(session, "mainwindow_width").f_int or 100
|
width = Settings.get_setting(session, "mainwindow_width").f_int or 100
|
||||||
height = Settings.get_setting(session, "mainwindow_height").f_int or 100
|
height = Settings.get_setting(session, "mainwindow_height").f_int or 100
|
||||||
splitter_top = Settings.get_setting(session, "splitter_top").f_int or 100
|
self.setGeometry(x, y, width, height)
|
||||||
|
|
||||||
|
if Config.USE_INTERNAL_BROWSER:
|
||||||
|
splitter_top = (
|
||||||
|
Settings.get_setting(session, "splitter_top").f_int or 100
|
||||||
|
)
|
||||||
splitter_bottom = (
|
splitter_bottom = (
|
||||||
Settings.get_setting(session, "splitter_bottom").f_int or 100
|
Settings.get_setting(session, "splitter_bottom").f_int or 100
|
||||||
)
|
)
|
||||||
|
|
||||||
self.setGeometry(x, y, width, height)
|
|
||||||
self.splitter.setSizes([splitter_top, splitter_bottom])
|
self.splitter.setSizes([splitter_top, splitter_bottom])
|
||||||
|
else:
|
||||||
|
self.tabInfolist.hide()
|
||||||
|
|
||||||
def set_selected_track_next(self) -> None:
|
def set_selected_track_next(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1607,7 +1666,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.label_intro_timer.setStyleSheet("")
|
self.label_intro_timer.setStyleSheet("")
|
||||||
self.label_intro_timer.setText("0.0")
|
self.label_intro_timer.setText("0.0")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# currnent track ended during servicing tick
|
# current track ended during servicing tick
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Ensure preview button is reset if preview finishes playing
|
# Ensure preview button is reset if preview finishes playing
|
||||||
@ -1615,7 +1674,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
if self.btnPreview.isChecked():
|
if self.btnPreview.isChecked():
|
||||||
if self.preview_manager.is_playing():
|
if self.preview_manager.is_playing():
|
||||||
self.btnPreview.setChecked(True)
|
self.btnPreview.setChecked(True)
|
||||||
minutes, seconds = divmod(self.preview_manager.get_playtime() / 1000, 60)
|
minutes, seconds = divmod(
|
||||||
|
self.preview_manager.get_playtime() / 1000, 60
|
||||||
|
)
|
||||||
self.label_intro_timer.setText(f"{int(minutes)}:{seconds:04.1f}")
|
self.label_intro_timer.setText(f"{int(minutes)}:{seconds:04.1f}")
|
||||||
# if self.preview_track_manager.time_remaining_intro() <= 50:
|
# if self.preview_track_manager.time_remaining_intro() <= 50:
|
||||||
# self.label_intro_timer.setStyleSheet(
|
# self.label_intro_timer.setStyleSheet(
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from random import shuffle
|
from random import shuffle
|
||||||
from typing import List, Optional
|
from typing import Optional
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -94,6 +94,39 @@ class _PlaylistRowData:
|
|||||||
f"note='{self.note}', title='{self.title}', artist='{self.artist}'>"
|
f"note='{self.note}', title='{self.title}', artist='{self.artist}'>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def set_start(
|
||||||
|
self, modified_rows: list[int], start: Optional[dt.datetime]
|
||||||
|
) -> Optional[dt.datetime]:
|
||||||
|
"""
|
||||||
|
Set start time for this row
|
||||||
|
|
||||||
|
Update passed modified rows list if we changed the row.
|
||||||
|
|
||||||
|
Return new start time
|
||||||
|
"""
|
||||||
|
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
if self.start_time != start:
|
||||||
|
self.start_time = start
|
||||||
|
changed = True
|
||||||
|
if start is None:
|
||||||
|
if self.end_time is not None:
|
||||||
|
self.end_time = None
|
||||||
|
changed = True
|
||||||
|
new_start_time = None
|
||||||
|
else:
|
||||||
|
end_time = start + dt.timedelta(milliseconds=self.duration)
|
||||||
|
new_start_time = end_time
|
||||||
|
if self.end_time != end_time:
|
||||||
|
self.end_time = end_time
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if changed and self.plr_rownum not in modified_rows:
|
||||||
|
modified_rows.append(self.plr_rownum)
|
||||||
|
|
||||||
|
return new_start_time
|
||||||
|
|
||||||
|
|
||||||
class PlaylistModel(QAbstractTableModel):
|
class PlaylistModel(QAbstractTableModel):
|
||||||
"""
|
"""
|
||||||
@ -310,7 +343,9 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole) -> QVariant:
|
def data(
|
||||||
|
self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole
|
||||||
|
) -> QVariant:
|
||||||
"""Return data to view"""
|
"""Return data to view"""
|
||||||
|
|
||||||
if not index.isValid() or not (0 <= index.row() < len(self.playlist_rows)):
|
if not index.isValid() or not (0 <= index.row() < len(self.playlist_rows)):
|
||||||
@ -349,7 +384,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
# Fall through to no-op
|
# Fall through to no-op
|
||||||
return QVariant()
|
return QVariant()
|
||||||
|
|
||||||
def delete_rows(self, row_numbers: List[int]) -> None:
|
def delete_rows(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Delete passed rows from model
|
Delete passed rows from model
|
||||||
|
|
||||||
@ -506,7 +541,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return QVariant(boldfont)
|
return QVariant(boldfont)
|
||||||
|
|
||||||
def get_duplicate_rows(self) -> List[int]:
|
def get_duplicate_rows(self) -> list[int]:
|
||||||
"""
|
"""
|
||||||
Return a list of duplicate rows. If track appears in rows 2, 3 and 4, return [3, 4]
|
Return a list of duplicate rows. If track appears in rows 2, 3 and 4, return [3, 4]
|
||||||
(ie, ignore the first, not-yet-duplicate, track).
|
(ie, ignore the first, not-yet-duplicate, track).
|
||||||
@ -572,7 +607,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return self.playlist_rows[row_number].path
|
return self.playlist_rows[row_number].path
|
||||||
|
|
||||||
def get_rows_duration(self, row_numbers: List[int]) -> int:
|
def get_rows_duration(self, row_numbers: list[int]) -> int:
|
||||||
"""
|
"""
|
||||||
Return the total duration of the passed rows
|
Return the total duration of the passed rows
|
||||||
"""
|
"""
|
||||||
@ -583,7 +618,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return duration
|
return duration
|
||||||
|
|
||||||
def get_unplayed_rows(self) -> List[int]:
|
def get_unplayed_rows(self) -> list[int]:
|
||||||
"""
|
"""
|
||||||
Return a list of unplayed row numbers
|
Return a list of unplayed row numbers
|
||||||
"""
|
"""
|
||||||
@ -606,28 +641,22 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
Return text for headers
|
Return text for headers
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
display_dispatch_table = {
|
||||||
|
Col.START_GAP.value: QVariant(Config.HEADER_START_GAP),
|
||||||
|
Col.INTRO.value: QVariant(Config.HEADER_INTRO),
|
||||||
|
Col.TITLE.value: QVariant(Config.HEADER_TITLE),
|
||||||
|
Col.ARTIST.value: QVariant(Config.HEADER_ARTIST),
|
||||||
|
Col.DURATION.value: QVariant(Config.HEADER_DURATION),
|
||||||
|
Col.START_TIME.value: QVariant(Config.HEADER_START_TIME),
|
||||||
|
Col.END_TIME.value: QVariant(Config.HEADER_END_TIME),
|
||||||
|
Col.LAST_PLAYED.value: QVariant(Config.HEADER_LAST_PLAYED),
|
||||||
|
Col.BITRATE.value: QVariant(Config.HEADER_BITRATE),
|
||||||
|
Col.NOTE.value: QVariant(Config.HEADER_NOTE),
|
||||||
|
}
|
||||||
|
|
||||||
if role == Qt.ItemDataRole.DisplayRole:
|
if role == Qt.ItemDataRole.DisplayRole:
|
||||||
if orientation == Qt.Orientation.Horizontal:
|
if orientation == Qt.Orientation.Horizontal:
|
||||||
if section == Col.START_GAP.value:
|
return display_dispatch_table[section]
|
||||||
return QVariant(Config.HEADER_START_GAP)
|
|
||||||
if section == Col.INTRO.value:
|
|
||||||
return QVariant(Config.HEADER_INTRO)
|
|
||||||
elif section == Col.TITLE.value:
|
|
||||||
return QVariant(Config.HEADER_TITLE)
|
|
||||||
elif section == Col.ARTIST.value:
|
|
||||||
return QVariant(Config.HEADER_ARTIST)
|
|
||||||
elif section == Col.DURATION.value:
|
|
||||||
return QVariant(Config.HEADER_DURATION)
|
|
||||||
elif section == Col.START_TIME.value:
|
|
||||||
return QVariant(Config.HEADER_START_TIME)
|
|
||||||
elif section == Col.END_TIME.value:
|
|
||||||
return QVariant(Config.HEADER_END_TIME)
|
|
||||||
elif section == Col.LAST_PLAYED.value:
|
|
||||||
return QVariant(Config.HEADER_LAST_PLAYED)
|
|
||||||
elif section == Col.BITRATE.value:
|
|
||||||
return QVariant(Config.HEADER_BITRATE)
|
|
||||||
elif section == Col.NOTE.value:
|
|
||||||
return QVariant(Config.HEADER_NOTE)
|
|
||||||
else:
|
else:
|
||||||
if Config.ROWS_FROM_ZERO:
|
if Config.ROWS_FROM_ZERO:
|
||||||
return QVariant(str(section))
|
return QVariant(str(section))
|
||||||
@ -646,84 +675,15 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
Process possible section timing directives embeded in header
|
Process possible section timing directives embeded in header
|
||||||
"""
|
"""
|
||||||
|
|
||||||
count: int = 0
|
|
||||||
unplayed_count: int = 0
|
|
||||||
duration: int = 0
|
|
||||||
|
|
||||||
if prd.note.endswith("+"):
|
if prd.note.endswith("+"):
|
||||||
# This header is the start of a timed section
|
return self.start_of_timed_section_header(prd)
|
||||||
for row_number in range(prd.plr_rownum + 1, len(self.playlist_rows)):
|
|
||||||
row_prd = self.playlist_rows[row_number]
|
|
||||||
if self.is_header_row(row_number):
|
|
||||||
if row_prd.note.endswith("-"):
|
|
||||||
return (
|
|
||||||
f"{prd.note[:-1].strip()} "
|
|
||||||
f"[{count} tracks, {ms_to_mmss(duration)} unplayed]"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
count += 1
|
|
||||||
if not row_prd.played:
|
|
||||||
unplayed_count += 1
|
|
||||||
duration += row_prd.duration
|
|
||||||
return (
|
|
||||||
f"{prd.note[:-1].strip()} "
|
|
||||||
f"[{count} tracks, {ms_to_mmss(duration, none='none')} "
|
|
||||||
"unplayed (to end of playlist)]"
|
|
||||||
)
|
|
||||||
elif prd.note.endswith("="):
|
elif prd.note.endswith("="):
|
||||||
# Show subtotal
|
return self.section_subtotal_header(prd)
|
||||||
for row_number in range(prd.plr_rownum - 1, -1, -1):
|
|
||||||
row_prd = self.playlist_rows[row_number]
|
|
||||||
if self.is_header_row(row_number):
|
|
||||||
if row_prd.note.endswith("-"):
|
|
||||||
# There was no start of section
|
|
||||||
return prd.note
|
|
||||||
if row_prd.note.endswith(("+", "=")):
|
|
||||||
# If we are playing this section, also
|
|
||||||
# calculate end time if all tracks are played.
|
|
||||||
end_time_str = ""
|
|
||||||
if (
|
|
||||||
track_sequence.current
|
|
||||||
and track_sequence.current.end_time
|
|
||||||
and (
|
|
||||||
row_number
|
|
||||||
< track_sequence.current.row_number
|
|
||||||
< prd.plr_rownum
|
|
||||||
)
|
|
||||||
):
|
|
||||||
section_end_time = (
|
|
||||||
track_sequence.current.end_time
|
|
||||||
+ dt.timedelta(milliseconds=duration)
|
|
||||||
)
|
|
||||||
end_time_str = (
|
|
||||||
", section end time "
|
|
||||||
+ section_end_time.strftime(Config.TRACK_TIME_FORMAT)
|
|
||||||
)
|
|
||||||
stripped_note = prd.note[:-1].strip()
|
|
||||||
if stripped_note:
|
|
||||||
return (
|
|
||||||
f"{stripped_note} ["
|
|
||||||
f"{unplayed_count}/{count} track{'s' if count > 1 else ''} "
|
|
||||||
f"({ms_to_mmss(duration)}) unplayed{end_time_str}]"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
f"[{unplayed_count}/{count} track{'s' if count > 1 else ''} "
|
|
||||||
f"({ms_to_mmss(duration)}) unplayed{end_time_str}]"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
count += 1
|
|
||||||
if not row_prd.played:
|
|
||||||
unplayed_count += 1
|
|
||||||
duration += row_prd.duration
|
|
||||||
|
|
||||||
elif prd.note == "-":
|
elif prd.note == "-":
|
||||||
# If the hyphen is the only thing on the line, echo the note
|
# If the hyphen is the only thing on the line, echo the note
|
||||||
# tha started the section without the trailing "+".
|
# that started the section without the trailing "+".
|
||||||
for row_number in range(prd.plr_rownum - 1, -1, -1):
|
for row_number in range(prd.plr_rownum - 1, -1, -1):
|
||||||
row_prd = self.playlist_rows[row_number]
|
row_prd = self.playlist_rows[row_number]
|
||||||
if self.is_header_row(row_number):
|
if self.is_header_row(row_number):
|
||||||
@ -788,7 +748,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.index(modified_row, self.columnCount() - 1),
|
self.index(modified_row, self.columnCount() - 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
def invalidate_rows(self, modified_rows: List[int]) -> None:
|
def invalidate_rows(self, modified_rows: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Signal to view to refresh invlidated rows
|
Signal to view to refresh invlidated rows
|
||||||
"""
|
"""
|
||||||
@ -824,7 +784,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def mark_unplayed(self, row_numbers: List[int]) -> None:
|
def mark_unplayed(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Mark row as unplayed
|
Mark row as unplayed
|
||||||
"""
|
"""
|
||||||
@ -838,9 +798,10 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
session.commit()
|
session.commit()
|
||||||
self.refresh_row(session, row_number)
|
self.refresh_row(session, row_number)
|
||||||
|
|
||||||
|
self.update_track_times()
|
||||||
self.invalidate_rows(row_numbers)
|
self.invalidate_rows(row_numbers)
|
||||||
|
|
||||||
def move_rows(self, from_rows: List[int], to_row_number: int) -> None:
|
def move_rows(self, from_rows: list[int], to_row_number: int) -> None:
|
||||||
"""
|
"""
|
||||||
Move the playlist rows given to to_row and below.
|
Move the playlist rows given to to_row and below.
|
||||||
"""
|
"""
|
||||||
@ -901,7 +862,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
# For SQLAlchemy, build a list of dictionaries that map plrid to
|
# For SQLAlchemy, build a list of dictionaries that map plrid to
|
||||||
# new row number:
|
# new row number:
|
||||||
sqla_map: List[dict[str, int]] = []
|
sqla_map: list[dict[str, int]] = []
|
||||||
for oldrow, newrow in row_map.items():
|
for oldrow, newrow in row_map.items():
|
||||||
plrid = self.playlist_rows[oldrow].plrid
|
plrid = self.playlist_rows[oldrow].plrid
|
||||||
sqla_map.append({"plrid": plrid, "plr_rownum": newrow})
|
sqla_map.append({"plrid": plrid, "plr_rownum": newrow})
|
||||||
@ -914,10 +875,11 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
# Update display
|
# Update display
|
||||||
self.reset_track_sequence_row_numbers()
|
self.reset_track_sequence_row_numbers()
|
||||||
|
self.update_track_times()
|
||||||
self.invalidate_rows(list(row_map.keys()))
|
self.invalidate_rows(list(row_map.keys()))
|
||||||
|
|
||||||
def move_rows_between_playlists(
|
def move_rows_between_playlists(
|
||||||
self, from_rows: List[int], to_row_number: int, to_playlist_id: int
|
self, from_rows: list[int], to_row_number: int, to_playlist_id: int
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Move the playlist rows given to to_row and below of to_playlist.
|
Move the playlist rows given to to_row and below of to_playlist.
|
||||||
@ -1155,8 +1117,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.update_track_times()
|
self.update_track_times()
|
||||||
|
|
||||||
def _reversed_contiguous_row_groups(
|
def _reversed_contiguous_row_groups(
|
||||||
self, row_numbers: List[int]
|
self, row_numbers: list[int]
|
||||||
) -> List[List[int]]:
|
) -> list[list[int]]:
|
||||||
"""
|
"""
|
||||||
Take the list of row numbers and split into groups of contiguous rows. Return as a list
|
Take the list of row numbers and split into groups of contiguous rows. Return as a list
|
||||||
of lists with the highest row numbers first.
|
of lists with the highest row numbers first.
|
||||||
@ -1168,8 +1130,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
log.debug(f"_reversed_contiguous_row_groups({row_numbers=} called")
|
log.debug(f"_reversed_contiguous_row_groups({row_numbers=} called")
|
||||||
|
|
||||||
result: List[List[int]] = []
|
result: list[list[int]] = []
|
||||||
temp: List[int] = []
|
temp: list[int] = []
|
||||||
last_value = row_numbers[0] - 1
|
last_value = row_numbers[0] - 1
|
||||||
|
|
||||||
for idx in range(len(row_numbers)):
|
for idx in range(len(row_numbers)):
|
||||||
@ -1190,7 +1152,68 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return len(self.playlist_rows)
|
return len(self.playlist_rows)
|
||||||
|
|
||||||
def selection_is_sortable(self, row_numbers: List[int]) -> bool:
|
def section_subtotal_header(self, prd: _PlaylistRowData) -> str:
|
||||||
|
"""
|
||||||
|
Process this row as subtotal within a timed section and
|
||||||
|
return display text for this row
|
||||||
|
"""
|
||||||
|
|
||||||
|
count: int = 0
|
||||||
|
unplayed_count: int = 0
|
||||||
|
duration: int = 0
|
||||||
|
|
||||||
|
# Show subtotal
|
||||||
|
for row_number in range(prd.plr_rownum - 1, -1, -1):
|
||||||
|
row_prd = self.playlist_rows[row_number]
|
||||||
|
if self.is_header_row(row_number):
|
||||||
|
if row_prd.note.endswith("-"):
|
||||||
|
# There was no start of section
|
||||||
|
return prd.note
|
||||||
|
if row_prd.note.endswith(("+", "=")):
|
||||||
|
# If we are playing this section, also
|
||||||
|
# calculate end time if all tracks are played.
|
||||||
|
end_time_str = ""
|
||||||
|
if (
|
||||||
|
track_sequence.current
|
||||||
|
and track_sequence.current.end_time
|
||||||
|
and (
|
||||||
|
row_number
|
||||||
|
< track_sequence.current.row_number
|
||||||
|
< prd.plr_rownum
|
||||||
|
)
|
||||||
|
):
|
||||||
|
section_end_time = (
|
||||||
|
track_sequence.current.end_time
|
||||||
|
+ dt.timedelta(milliseconds=duration)
|
||||||
|
)
|
||||||
|
end_time_str = (
|
||||||
|
", section end time "
|
||||||
|
+ section_end_time.strftime(Config.TRACK_TIME_FORMAT)
|
||||||
|
)
|
||||||
|
stripped_note = prd.note[:-1].strip()
|
||||||
|
if stripped_note:
|
||||||
|
return (
|
||||||
|
f"{stripped_note} ["
|
||||||
|
f"{unplayed_count}/{count} track{'s' if count > 1 else ''} "
|
||||||
|
f"({ms_to_mmss(duration)}) unplayed{end_time_str}]"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return (
|
||||||
|
f"[{unplayed_count}/{count} track{'s' if count > 1 else ''} "
|
||||||
|
f"({ms_to_mmss(duration)}) unplayed{end_time_str}]"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
count += 1
|
||||||
|
if not row_prd.played:
|
||||||
|
unplayed_count += 1
|
||||||
|
duration += row_prd.duration
|
||||||
|
|
||||||
|
# Should never get here
|
||||||
|
return f"Error calculating subtotal ({row_prd.note})"
|
||||||
|
|
||||||
|
def selection_is_sortable(self, row_numbers: list[int]) -> bool:
|
||||||
"""
|
"""
|
||||||
Return True if the selection is sortable. That means:
|
Return True if the selection is sortable. That means:
|
||||||
- at least two rows selected
|
- at least two rows selected
|
||||||
@ -1259,9 +1282,14 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
log.error(f"Error creating MainTrackManager({prd=}): ({str(e)})")
|
log.error(f"Error creating MainTrackManager({prd=}): ({str(e)})")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if Config.WIKIPEDIA_ON_NEXT:
|
||||||
self.signals.search_wikipedia_signal.emit(
|
self.signals.search_wikipedia_signal.emit(
|
||||||
self.playlist_rows[row_number].title
|
self.playlist_rows[row_number].title
|
||||||
)
|
)
|
||||||
|
if Config.SONGFACTS_ON_NEXT:
|
||||||
|
self.signals.search_songfacts_signal.emit(
|
||||||
|
self.playlist_rows[row_number].title
|
||||||
|
)
|
||||||
if old_next_row:
|
if old_next_row:
|
||||||
self.invalidate_row(old_next_row)
|
self.invalidate_row(old_next_row)
|
||||||
self.invalidate_row(row_number)
|
self.invalidate_row(row_number)
|
||||||
@ -1325,14 +1353,14 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def sort_by_artist(self, row_numbers: List[int]) -> None:
|
def sort_by_artist(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Sort selected rows by artist
|
Sort selected rows by artist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.sort_by_attribute(row_numbers, "artist")
|
self.sort_by_attribute(row_numbers, "artist")
|
||||||
|
|
||||||
def sort_by_attribute(self, row_numbers: List[int], attr_name: str) -> None:
|
def sort_by_attribute(self, row_numbers: list[int], attr_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Sort selected rows by passed attribute name where 'attribute' is a
|
Sort selected rows by passed attribute name where 'attribute' is a
|
||||||
key in PlaylistRowData
|
key in PlaylistRowData
|
||||||
@ -1347,21 +1375,21 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
self.move_rows(sorted_list, min(sorted_list))
|
self.move_rows(sorted_list, min(sorted_list))
|
||||||
|
|
||||||
def sort_by_duration(self, row_numbers: List[int]) -> None:
|
def sort_by_duration(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Sort selected rows by duration
|
Sort selected rows by duration
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.sort_by_attribute(row_numbers, "duration")
|
self.sort_by_attribute(row_numbers, "duration")
|
||||||
|
|
||||||
def sort_by_lastplayed(self, row_numbers: List[int]) -> None:
|
def sort_by_lastplayed(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Sort selected rows by lastplayed
|
Sort selected rows by lastplayed
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.sort_by_attribute(row_numbers, "lastplayed")
|
self.sort_by_attribute(row_numbers, "lastplayed")
|
||||||
|
|
||||||
def sort_randomly(self, row_numbers: List[int]) -> None:
|
def sort_randomly(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Sort selected rows randomly
|
Sort selected rows randomly
|
||||||
"""
|
"""
|
||||||
@ -1369,13 +1397,44 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
shuffle(row_numbers)
|
shuffle(row_numbers)
|
||||||
self.move_rows(row_numbers, min(row_numbers))
|
self.move_rows(row_numbers, min(row_numbers))
|
||||||
|
|
||||||
def sort_by_title(self, row_numbers: List[int]) -> None:
|
def sort_by_title(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Sort selected rows by title
|
Sort selected rows by title
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.sort_by_attribute(row_numbers, "title")
|
self.sort_by_attribute(row_numbers, "title")
|
||||||
|
|
||||||
|
def start_of_timed_section_header(self, prd: _PlaylistRowData) -> str:
|
||||||
|
"""
|
||||||
|
Process this row as the start of a timed section and
|
||||||
|
return display text for this row
|
||||||
|
"""
|
||||||
|
|
||||||
|
count: int = 0
|
||||||
|
unplayed_count: int = 0
|
||||||
|
duration: int = 0
|
||||||
|
|
||||||
|
for row_number in range(prd.plr_rownum + 1, len(self.playlist_rows)):
|
||||||
|
row_prd = self.playlist_rows[row_number]
|
||||||
|
if self.is_header_row(row_number):
|
||||||
|
if row_prd.note.endswith("-"):
|
||||||
|
return (
|
||||||
|
f"{prd.note[:-1].strip()} "
|
||||||
|
f"[{count} tracks, {ms_to_mmss(duration)} unplayed]"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
count += 1
|
||||||
|
if not row_prd.played:
|
||||||
|
unplayed_count += 1
|
||||||
|
duration += row_prd.duration
|
||||||
|
return (
|
||||||
|
f"{prd.note[:-1].strip()} "
|
||||||
|
f"[{count} tracks, {ms_to_mmss(duration, none='none')} "
|
||||||
|
"unplayed (to end of playlist)]"
|
||||||
|
)
|
||||||
|
|
||||||
def supportedDropActions(self) -> Qt.DropAction:
|
def supportedDropActions(self) -> Qt.DropAction:
|
||||||
return Qt.DropAction.MoveAction | Qt.DropAction.CopyAction
|
return Qt.DropAction.MoveAction | Qt.DropAction.CopyAction
|
||||||
|
|
||||||
@ -1408,51 +1467,33 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
log.debug("update_track_times()")
|
log.debug("update_track_times()")
|
||||||
|
|
||||||
next_start_time: Optional[dt.datetime] = None
|
next_start_time: Optional[dt.datetime] = None
|
||||||
update_rows: List[int] = []
|
update_rows: list[int] = []
|
||||||
playlist_length = len(self.playlist_rows)
|
row_count = len(self.playlist_rows)
|
||||||
if not playlist_length:
|
|
||||||
return
|
|
||||||
|
|
||||||
for row_number in range(playlist_length):
|
current_track_row = None
|
||||||
|
next_track_row = None
|
||||||
|
if track_sequence.current:
|
||||||
|
current_track_row = track_sequence.current.row_number
|
||||||
|
# Update current track details now so that they are available
|
||||||
|
# when we deal with next track row which may be above current
|
||||||
|
# track row.
|
||||||
|
self.playlist_rows[current_track_row].set_start(
|
||||||
|
update_rows, track_sequence.current.start_time
|
||||||
|
)
|
||||||
|
|
||||||
|
if track_sequence.next:
|
||||||
|
next_track_row = track_sequence.next.row_number
|
||||||
|
|
||||||
|
for row_number in range(row_count):
|
||||||
prd = self.playlist_rows[row_number]
|
prd = self.playlist_rows[row_number]
|
||||||
|
|
||||||
# Reset start_time if this is the current row
|
# Don't update times for tracks that have been played, for
|
||||||
if track_sequence.current:
|
# unreadable tracks or for the current track, handled above.
|
||||||
if row_number == track_sequence.current.row_number:
|
|
||||||
prd.start_time = track_sequence.current.start_time
|
|
||||||
prd.end_time = track_sequence.current.end_time
|
|
||||||
update_rows.append(row_number)
|
|
||||||
if not next_start_time:
|
|
||||||
next_start_time = prd.end_time
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Set start time for next row if we have a current track
|
|
||||||
if track_sequence.next and track_sequence.current.end_time:
|
|
||||||
if row_number == track_sequence.next.row_number:
|
|
||||||
prd.start_time = track_sequence.current.end_time
|
|
||||||
prd.end_time = prd.start_time + dt.timedelta(
|
|
||||||
milliseconds=prd.duration
|
|
||||||
)
|
|
||||||
next_start_time = prd.end_time
|
|
||||||
update_rows.append(row_number)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Don't update times for tracks that have been played
|
|
||||||
if prd.played:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# If we're between the current and next row, zero out
|
|
||||||
# times
|
|
||||||
if (
|
if (
|
||||||
track_sequence.current
|
prd.played
|
||||||
and track_sequence.next
|
or row_number == current_track_row
|
||||||
and track_sequence.current.row_number
|
or (prd.path and file_is_unreadable(prd.path))
|
||||||
< row_number
|
|
||||||
< track_sequence.next.row_number
|
|
||||||
):
|
):
|
||||||
prd.start_time = None
|
|
||||||
prd.end_time = None
|
|
||||||
update_rows.append(row_number)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Reset start time if timing in header
|
# Reset start time if timing in header
|
||||||
@ -1462,28 +1503,25 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
next_start_time = header_time
|
next_start_time = header_time
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# This is an unplayed track
|
# Set start time for next row if we have a current track
|
||||||
# Don't schedule unplayable tracks
|
if (
|
||||||
if file_is_unreadable(prd.path):
|
row_number == next_track_row
|
||||||
|
and track_sequence.current
|
||||||
|
and track_sequence.current.end_time
|
||||||
|
):
|
||||||
|
next_start_time = prd.set_start(
|
||||||
|
update_rows, track_sequence.current.end_time
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Set start/end if we have a start time
|
# If we're between the current and next row, zero out
|
||||||
if next_start_time is None:
|
# times
|
||||||
|
if (current_track_row or row_count) < row_number < (next_track_row or 0):
|
||||||
|
prd.set_start(update_rows, None)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Update start time of this row if it's incorrect
|
# Set start/end
|
||||||
if prd.start_time != next_start_time:
|
next_start_time = prd.set_start(update_rows, next_start_time)
|
||||||
prd.start_time = next_start_time
|
|
||||||
update_rows.append(row_number)
|
|
||||||
|
|
||||||
# Calculate next start time
|
|
||||||
next_start_time += dt.timedelta(milliseconds=prd.duration)
|
|
||||||
|
|
||||||
# Update end time of this row if it's incorrect
|
|
||||||
if prd.end_time != next_start_time:
|
|
||||||
prd.end_time = next_start_time
|
|
||||||
if row_number not in update_rows:
|
|
||||||
update_rows.append(row_number)
|
|
||||||
|
|
||||||
# Update start/stop times of rows that have changed
|
# Update start/stop times of rows that have changed
|
||||||
for updated_row in update_rows:
|
for updated_row in update_rows:
|
||||||
@ -1524,7 +1562,8 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
# Don't hide current track
|
# Don't hide current track
|
||||||
if (
|
if (
|
||||||
track_sequence.current
|
track_sequence.current
|
||||||
and track_sequence.current.playlist_id == self.source_model.playlist_id
|
and track_sequence.current.playlist_id
|
||||||
|
== self.source_model.playlist_id
|
||||||
and track_sequence.current.row_number == source_row
|
and track_sequence.current.row_number == source_row
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
@ -1540,7 +1579,8 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
# Handle previous track
|
# Handle previous track
|
||||||
if track_sequence.previous:
|
if track_sequence.previous:
|
||||||
if (
|
if (
|
||||||
track_sequence.previous.playlist_id != self.source_model.playlist_id
|
track_sequence.previous.playlist_id
|
||||||
|
!= self.source_model.playlist_id
|
||||||
or track_sequence.previous.row_number != source_row
|
or track_sequence.previous.row_number != source_row
|
||||||
):
|
):
|
||||||
# This row isn't our previous track: hide it
|
# This row isn't our previous track: hide it
|
||||||
@ -1549,11 +1589,10 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
# This row is our previous track. Don't hide it
|
# This row is our previous track. Don't hide it
|
||||||
# until HIDE_AFTER_PLAYING_OFFSET milliseconds
|
# until HIDE_AFTER_PLAYING_OFFSET milliseconds
|
||||||
# after current track has started
|
# after current track has started
|
||||||
if (
|
if track_sequence.current.start_time and dt.datetime.now() > (
|
||||||
track_sequence.current.start_time
|
track_sequence.current.start_time
|
||||||
and dt.datetime.now() > (
|
+ dt.timedelta(
|
||||||
track_sequence.current.start_time
|
milliseconds=Config.HIDE_AFTER_PLAYING_OFFSET
|
||||||
+ dt.timedelta(milliseconds=Config.HIDE_AFTER_PLAYING_OFFSET)
|
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
@ -1565,9 +1604,7 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
# true next time through.
|
# true next time through.
|
||||||
QTimer.singleShot(
|
QTimer.singleShot(
|
||||||
Config.HIDE_AFTER_PLAYING_OFFSET + 100,
|
Config.HIDE_AFTER_PLAYING_OFFSET + 100,
|
||||||
lambda: self.source_model.invalidate_row(
|
lambda: self.source_model.invalidate_row(source_row),
|
||||||
source_row
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
# Next track not playing yet so don't hide previous
|
# Next track not playing yet so don't hide previous
|
||||||
@ -1597,13 +1634,13 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
def current_track_started(self):
|
def current_track_started(self):
|
||||||
return self.source_model.current_track_started()
|
return self.source_model.current_track_started()
|
||||||
|
|
||||||
def delete_rows(self, row_numbers: List[int]) -> None:
|
def delete_rows(self, row_numbers: list[int]) -> None:
|
||||||
return self.source_model.delete_rows(row_numbers)
|
return self.source_model.delete_rows(row_numbers)
|
||||||
|
|
||||||
def get_duplicate_rows(self) -> List[int]:
|
def get_duplicate_rows(self) -> list[int]:
|
||||||
return self.source_model.get_duplicate_rows()
|
return self.source_model.get_duplicate_rows()
|
||||||
|
|
||||||
def get_rows_duration(self, row_numbers: List[int]) -> int:
|
def get_rows_duration(self, row_numbers: list[int]) -> int:
|
||||||
return self.source_model.get_rows_duration(row_numbers)
|
return self.source_model.get_rows_duration(row_numbers)
|
||||||
|
|
||||||
def get_row_info(self, row_number: int) -> _PlaylistRowData:
|
def get_row_info(self, row_number: int) -> _PlaylistRowData:
|
||||||
@ -1612,7 +1649,7 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
def get_row_track_path(self, row_number: int) -> str:
|
def get_row_track_path(self, row_number: int) -> str:
|
||||||
return self.source_model.get_row_track_path(row_number)
|
return self.source_model.get_row_track_path(row_number)
|
||||||
|
|
||||||
def get_unplayed_rows(self) -> List[int]:
|
def get_unplayed_rows(self) -> list[int]:
|
||||||
return self.source_model.get_unplayed_rows()
|
return self.source_model.get_unplayed_rows()
|
||||||
|
|
||||||
def hide_played_tracks(self, hide: bool) -> None:
|
def hide_played_tracks(self, hide: bool) -> None:
|
||||||
@ -1635,14 +1672,14 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
def is_track_in_playlist(self, track_id: int) -> Optional[_PlaylistRowData]:
|
def is_track_in_playlist(self, track_id: int) -> Optional[_PlaylistRowData]:
|
||||||
return self.source_model.is_track_in_playlist(track_id)
|
return self.source_model.is_track_in_playlist(track_id)
|
||||||
|
|
||||||
def mark_unplayed(self, row_numbers: List[int]) -> None:
|
def mark_unplayed(self, row_numbers: list[int]) -> None:
|
||||||
return self.source_model.mark_unplayed(row_numbers)
|
return self.source_model.mark_unplayed(row_numbers)
|
||||||
|
|
||||||
def move_rows(self, from_rows: List[int], to_row_number: int) -> None:
|
def move_rows(self, from_rows: list[int], to_row_number: int) -> None:
|
||||||
return self.source_model.move_rows(from_rows, to_row_number)
|
return self.source_model.move_rows(from_rows, to_row_number)
|
||||||
|
|
||||||
def move_rows_between_playlists(
|
def move_rows_between_playlists(
|
||||||
self, from_rows: List[int], to_row_number: int, to_playlist_id: int
|
self, from_rows: list[int], to_row_number: int, to_playlist_id: int
|
||||||
) -> None:
|
) -> None:
|
||||||
return self.source_model.move_rows_between_playlists(
|
return self.source_model.move_rows_between_playlists(
|
||||||
from_rows, to_row_number, to_playlist_id
|
from_rows, to_row_number, to_playlist_id
|
||||||
@ -1675,19 +1712,19 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
def set_next_row(self, row_number: Optional[int]) -> bool:
|
def set_next_row(self, row_number: Optional[int]) -> bool:
|
||||||
return self.source_model.set_next_row(row_number)
|
return self.source_model.set_next_row(row_number)
|
||||||
|
|
||||||
def sort_by_artist(self, row_numbers: List[int]) -> None:
|
def sort_by_artist(self, row_numbers: list[int]) -> None:
|
||||||
return self.source_model.sort_by_artist(row_numbers)
|
return self.source_model.sort_by_artist(row_numbers)
|
||||||
|
|
||||||
def sort_by_duration(self, row_numbers: List[int]) -> None:
|
def sort_by_duration(self, row_numbers: list[int]) -> None:
|
||||||
return self.source_model.sort_by_duration(row_numbers)
|
return self.source_model.sort_by_duration(row_numbers)
|
||||||
|
|
||||||
def sort_by_lastplayed(self, row_numbers: List[int]) -> None:
|
def sort_by_lastplayed(self, row_numbers: list[int]) -> None:
|
||||||
return self.source_model.sort_by_lastplayed(row_numbers)
|
return self.source_model.sort_by_lastplayed(row_numbers)
|
||||||
|
|
||||||
def sort_randomly(self, row_numbers: List[int]) -> None:
|
def sort_randomly(self, row_numbers: list[int]) -> None:
|
||||||
return self.source_model.sort_randomly(row_numbers)
|
return self.source_model.sort_randomly(row_numbers)
|
||||||
|
|
||||||
def sort_by_title(self, row_numbers: List[int]) -> None:
|
def sort_by_title(self, row_numbers: list[int]) -> None:
|
||||||
return self.source_model.sort_by_title(row_numbers)
|
return self.source_model.sort_by_title(row_numbers)
|
||||||
|
|
||||||
def update_track_times(self) -> None:
|
def update_track_times(self) -> None:
|
||||||
|
|||||||
@ -1367,7 +1367,7 @@ padding-left: 8px;</string>
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionReplace_files">
|
<action name="actionReplace_files">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Replace files...</string>
|
<string>Import files...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@ -15,11 +15,7 @@ class Ui_MainWindow(object):
|
|||||||
MainWindow.resize(1280, 857)
|
MainWindow.resize(1280, 857)
|
||||||
MainWindow.setMinimumSize(QtCore.QSize(1280, 0))
|
MainWindow.setMinimumSize(QtCore.QSize(1280, 0))
|
||||||
icon = QtGui.QIcon()
|
icon = QtGui.QIcon()
|
||||||
icon.addPixmap(
|
icon.addPixmap(QtGui.QPixmap(":/icons/musicmuster"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(":/icons/musicmuster"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
MainWindow.setWindowIcon(icon)
|
MainWindow.setWindowIcon(icon)
|
||||||
MainWindow.setStyleSheet("")
|
MainWindow.setStyleSheet("")
|
||||||
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
|
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
|
||||||
@ -31,62 +27,39 @@ class Ui_MainWindow(object):
|
|||||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
|
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
|
||||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||||
self.previous_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.previous_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(
|
sizePolicy.setHeightForWidth(self.previous_track_2.sizePolicy().hasHeightForWidth())
|
||||||
self.previous_track_2.sizePolicy().hasHeightForWidth()
|
|
||||||
)
|
|
||||||
self.previous_track_2.setSizePolicy(sizePolicy)
|
self.previous_track_2.setSizePolicy(sizePolicy)
|
||||||
self.previous_track_2.setMaximumSize(QtCore.QSize(230, 16777215))
|
self.previous_track_2.setMaximumSize(QtCore.QSize(230, 16777215))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Sans")
|
font.setFamily("Sans")
|
||||||
font.setPointSize(20)
|
font.setPointSize(20)
|
||||||
self.previous_track_2.setFont(font)
|
self.previous_track_2.setFont(font)
|
||||||
self.previous_track_2.setStyleSheet(
|
self.previous_track_2.setStyleSheet("background-color: #f8d7da;\n"
|
||||||
"background-color: #f8d7da;\n" "border: 1px solid rgb(85, 87, 83);"
|
"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.setAlignment(
|
|
||||||
QtCore.Qt.AlignmentFlag.AlignRight
|
|
||||||
| QtCore.Qt.AlignmentFlag.AlignTrailing
|
|
||||||
| QtCore.Qt.AlignmentFlag.AlignVCenter
|
|
||||||
)
|
|
||||||
self.previous_track_2.setObjectName("previous_track_2")
|
self.previous_track_2.setObjectName("previous_track_2")
|
||||||
self.verticalLayout_3.addWidget(self.previous_track_2)
|
self.verticalLayout_3.addWidget(self.previous_track_2)
|
||||||
self.current_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.current_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(
|
sizePolicy.setHeightForWidth(self.current_track_2.sizePolicy().hasHeightForWidth())
|
||||||
self.current_track_2.sizePolicy().hasHeightForWidth()
|
|
||||||
)
|
|
||||||
self.current_track_2.setSizePolicy(sizePolicy)
|
self.current_track_2.setSizePolicy(sizePolicy)
|
||||||
self.current_track_2.setMaximumSize(QtCore.QSize(230, 16777215))
|
self.current_track_2.setMaximumSize(QtCore.QSize(230, 16777215))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Sans")
|
font.setFamily("Sans")
|
||||||
font.setPointSize(20)
|
font.setPointSize(20)
|
||||||
self.current_track_2.setFont(font)
|
self.current_track_2.setFont(font)
|
||||||
self.current_track_2.setStyleSheet(
|
self.current_track_2.setStyleSheet("background-color: #d4edda;\n"
|
||||||
"background-color: #d4edda;\n" "border: 1px solid rgb(85, 87, 83);"
|
"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.setAlignment(
|
|
||||||
QtCore.Qt.AlignmentFlag.AlignRight
|
|
||||||
| QtCore.Qt.AlignmentFlag.AlignTrailing
|
|
||||||
| QtCore.Qt.AlignmentFlag.AlignVCenter
|
|
||||||
)
|
|
||||||
self.current_track_2.setObjectName("current_track_2")
|
self.current_track_2.setObjectName("current_track_2")
|
||||||
self.verticalLayout_3.addWidget(self.current_track_2)
|
self.verticalLayout_3.addWidget(self.current_track_2)
|
||||||
self.next_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.next_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.next_track_2.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.next_track_2.sizePolicy().hasHeightForWidth())
|
||||||
@ -96,29 +69,19 @@ class Ui_MainWindow(object):
|
|||||||
font.setFamily("Sans")
|
font.setFamily("Sans")
|
||||||
font.setPointSize(20)
|
font.setPointSize(20)
|
||||||
self.next_track_2.setFont(font)
|
self.next_track_2.setFont(font)
|
||||||
self.next_track_2.setStyleSheet(
|
self.next_track_2.setStyleSheet("background-color: #fff3cd;\n"
|
||||||
"background-color: #fff3cd;\n" "border: 1px solid rgb(85, 87, 83);"
|
"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.setAlignment(
|
|
||||||
QtCore.Qt.AlignmentFlag.AlignRight
|
|
||||||
| QtCore.Qt.AlignmentFlag.AlignTrailing
|
|
||||||
| QtCore.Qt.AlignmentFlag.AlignVCenter
|
|
||||||
)
|
|
||||||
self.next_track_2.setObjectName("next_track_2")
|
self.next_track_2.setObjectName("next_track_2")
|
||||||
self.verticalLayout_3.addWidget(self.next_track_2)
|
self.verticalLayout_3.addWidget(self.next_track_2)
|
||||||
self.horizontalLayout_3.addLayout(self.verticalLayout_3)
|
self.horizontalLayout_3.addLayout(self.verticalLayout_3)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.hdrPreviousTrack = QtWidgets.QLabel(parent=self.centralwidget)
|
self.hdrPreviousTrack = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(
|
sizePolicy.setHeightForWidth(self.hdrPreviousTrack.sizePolicy().hasHeightForWidth())
|
||||||
self.hdrPreviousTrack.sizePolicy().hasHeightForWidth()
|
|
||||||
)
|
|
||||||
self.hdrPreviousTrack.setSizePolicy(sizePolicy)
|
self.hdrPreviousTrack.setSizePolicy(sizePolicy)
|
||||||
self.hdrPreviousTrack.setMinimumSize(QtCore.QSize(0, 0))
|
self.hdrPreviousTrack.setMinimumSize(QtCore.QSize(0, 0))
|
||||||
self.hdrPreviousTrack.setMaximumSize(QtCore.QSize(16777215, 16777215))
|
self.hdrPreviousTrack.setMaximumSize(QtCore.QSize(16777215, 16777215))
|
||||||
@ -126,43 +89,32 @@ class Ui_MainWindow(object):
|
|||||||
font.setFamily("Sans")
|
font.setFamily("Sans")
|
||||||
font.setPointSize(20)
|
font.setPointSize(20)
|
||||||
self.hdrPreviousTrack.setFont(font)
|
self.hdrPreviousTrack.setFont(font)
|
||||||
self.hdrPreviousTrack.setStyleSheet(
|
self.hdrPreviousTrack.setStyleSheet("background-color: #f8d7da;\n"
|
||||||
"background-color: #f8d7da;\n" "border: 1px solid rgb(85, 87, 83);"
|
"border: 1px solid rgb(85, 87, 83);")
|
||||||
)
|
|
||||||
self.hdrPreviousTrack.setText("")
|
self.hdrPreviousTrack.setText("")
|
||||||
self.hdrPreviousTrack.setWordWrap(False)
|
self.hdrPreviousTrack.setWordWrap(False)
|
||||||
self.hdrPreviousTrack.setObjectName("hdrPreviousTrack")
|
self.hdrPreviousTrack.setObjectName("hdrPreviousTrack")
|
||||||
self.verticalLayout.addWidget(self.hdrPreviousTrack)
|
self.verticalLayout.addWidget(self.hdrPreviousTrack)
|
||||||
self.hdrCurrentTrack = QtWidgets.QPushButton(parent=self.centralwidget)
|
self.hdrCurrentTrack = QtWidgets.QPushButton(parent=self.centralwidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(
|
sizePolicy.setHeightForWidth(self.hdrCurrentTrack.sizePolicy().hasHeightForWidth())
|
||||||
self.hdrCurrentTrack.sizePolicy().hasHeightForWidth()
|
|
||||||
)
|
|
||||||
self.hdrCurrentTrack.setSizePolicy(sizePolicy)
|
self.hdrCurrentTrack.setSizePolicy(sizePolicy)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(20)
|
font.setPointSize(20)
|
||||||
self.hdrCurrentTrack.setFont(font)
|
self.hdrCurrentTrack.setFont(font)
|
||||||
self.hdrCurrentTrack.setStyleSheet(
|
self.hdrCurrentTrack.setStyleSheet("background-color: #d4edda;\n"
|
||||||
"background-color: #d4edda;\n"
|
"border: 1px solid rgb(85, 87, 83);\n"
|
||||||
"border: 1px solid rgb(85, 87, 83);\n"
|
"text-align: left;\n"
|
||||||
"text-align: left;\n"
|
"padding-left: 8px;\n"
|
||||||
"padding-left: 8px;\n"
|
"")
|
||||||
""
|
|
||||||
)
|
|
||||||
self.hdrCurrentTrack.setText("")
|
self.hdrCurrentTrack.setText("")
|
||||||
self.hdrCurrentTrack.setFlat(True)
|
self.hdrCurrentTrack.setFlat(True)
|
||||||
self.hdrCurrentTrack.setObjectName("hdrCurrentTrack")
|
self.hdrCurrentTrack.setObjectName("hdrCurrentTrack")
|
||||||
self.verticalLayout.addWidget(self.hdrCurrentTrack)
|
self.verticalLayout.addWidget(self.hdrCurrentTrack)
|
||||||
self.hdrNextTrack = QtWidgets.QPushButton(parent=self.centralwidget)
|
self.hdrNextTrack = QtWidgets.QPushButton(parent=self.centralwidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.hdrNextTrack.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.hdrNextTrack.sizePolicy().hasHeightForWidth())
|
||||||
@ -170,12 +122,10 @@ class Ui_MainWindow(object):
|
|||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(20)
|
font.setPointSize(20)
|
||||||
self.hdrNextTrack.setFont(font)
|
self.hdrNextTrack.setFont(font)
|
||||||
self.hdrNextTrack.setStyleSheet(
|
self.hdrNextTrack.setStyleSheet("background-color: #fff3cd;\n"
|
||||||
"background-color: #fff3cd;\n"
|
"border: 1px solid rgb(85, 87, 83);\n"
|
||||||
"border: 1px solid rgb(85, 87, 83);\n"
|
"text-align: left;\n"
|
||||||
"text-align: left;\n"
|
"padding-left: 8px;")
|
||||||
"padding-left: 8px;"
|
|
||||||
)
|
|
||||||
self.hdrNextTrack.setText("")
|
self.hdrNextTrack.setText("")
|
||||||
self.hdrNextTrack.setFlat(True)
|
self.hdrNextTrack.setFlat(True)
|
||||||
self.hdrNextTrack.setObjectName("hdrNextTrack")
|
self.hdrNextTrack.setObjectName("hdrNextTrack")
|
||||||
@ -222,12 +172,7 @@ class Ui_MainWindow(object):
|
|||||||
self.cartsWidget.setObjectName("cartsWidget")
|
self.cartsWidget.setObjectName("cartsWidget")
|
||||||
self.horizontalLayout_Carts = QtWidgets.QHBoxLayout(self.cartsWidget)
|
self.horizontalLayout_Carts = QtWidgets.QHBoxLayout(self.cartsWidget)
|
||||||
self.horizontalLayout_Carts.setObjectName("horizontalLayout_Carts")
|
self.horizontalLayout_Carts.setObjectName("horizontalLayout_Carts")
|
||||||
spacerItem = QtWidgets.QSpacerItem(
|
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||||
40,
|
|
||||||
20,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Expanding,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Minimum,
|
|
||||||
)
|
|
||||||
self.horizontalLayout_Carts.addItem(spacerItem)
|
self.horizontalLayout_Carts.addItem(spacerItem)
|
||||||
self.gridLayout_4.addWidget(self.cartsWidget, 2, 0, 1, 1)
|
self.gridLayout_4.addWidget(self.cartsWidget, 2, 0, 1, 1)
|
||||||
self.frame_6 = QtWidgets.QFrame(parent=self.centralwidget)
|
self.frame_6 = QtWidgets.QFrame(parent=self.centralwidget)
|
||||||
@ -272,11 +217,7 @@ class Ui_MainWindow(object):
|
|||||||
self.btnPreview = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
|
self.btnPreview = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
|
||||||
self.btnPreview.setMinimumSize(QtCore.QSize(132, 41))
|
self.btnPreview.setMinimumSize(QtCore.QSize(132, 41))
|
||||||
icon1 = QtGui.QIcon()
|
icon1 = QtGui.QIcon()
|
||||||
icon1.addPixmap(
|
icon1.addPixmap(QtGui.QPixmap(":/icons/headphones"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(":/icons/headphones"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.btnPreview.setIcon(icon1)
|
self.btnPreview.setIcon(icon1)
|
||||||
self.btnPreview.setIconSize(QtCore.QSize(30, 30))
|
self.btnPreview.setIconSize(QtCore.QSize(30, 30))
|
||||||
self.btnPreview.setCheckable(True)
|
self.btnPreview.setCheckable(True)
|
||||||
@ -298,16 +239,8 @@ class Ui_MainWindow(object):
|
|||||||
self.btnPreviewArm.setMaximumSize(QtCore.QSize(44, 23))
|
self.btnPreviewArm.setMaximumSize(QtCore.QSize(44, 23))
|
||||||
self.btnPreviewArm.setText("")
|
self.btnPreviewArm.setText("")
|
||||||
icon2 = QtGui.QIcon()
|
icon2 = QtGui.QIcon()
|
||||||
icon2.addPixmap(
|
icon2.addPixmap(QtGui.QPixmap(":/icons/record-button.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(":/icons/record-button.png"),
|
icon2.addPixmap(QtGui.QPixmap(":/icons/record-red-button.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.On)
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
icon2.addPixmap(
|
|
||||||
QtGui.QPixmap(":/icons/record-red-button.png"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.On,
|
|
||||||
)
|
|
||||||
self.btnPreviewArm.setIcon(icon2)
|
self.btnPreviewArm.setIcon(icon2)
|
||||||
self.btnPreviewArm.setCheckable(True)
|
self.btnPreviewArm.setCheckable(True)
|
||||||
self.btnPreviewArm.setObjectName("btnPreviewArm")
|
self.btnPreviewArm.setObjectName("btnPreviewArm")
|
||||||
@ -328,16 +261,8 @@ class Ui_MainWindow(object):
|
|||||||
self.btnPreviewMark.setMaximumSize(QtCore.QSize(44, 23))
|
self.btnPreviewMark.setMaximumSize(QtCore.QSize(44, 23))
|
||||||
self.btnPreviewMark.setText("")
|
self.btnPreviewMark.setText("")
|
||||||
icon3 = QtGui.QIcon()
|
icon3 = QtGui.QIcon()
|
||||||
icon3.addPixmap(
|
icon3.addPixmap(QtGui.QPixmap(":/icons/star.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.On)
|
||||||
QtGui.QPixmap(":/icons/star.png"),
|
icon3.addPixmap(QtGui.QPixmap(":/icons/star_empty.png"), QtGui.QIcon.Mode.Disabled, QtGui.QIcon.State.Off)
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.On,
|
|
||||||
)
|
|
||||||
icon3.addPixmap(
|
|
||||||
QtGui.QPixmap(":/icons/star_empty.png"),
|
|
||||||
QtGui.QIcon.Mode.Disabled,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.btnPreviewMark.setIcon(icon3)
|
self.btnPreviewMark.setIcon(icon3)
|
||||||
self.btnPreviewMark.setObjectName("btnPreviewMark")
|
self.btnPreviewMark.setObjectName("btnPreviewMark")
|
||||||
self.btnPreviewFwd = QtWidgets.QPushButton(parent=self.groupBoxIntroControls)
|
self.btnPreviewFwd = QtWidgets.QPushButton(parent=self.groupBoxIntroControls)
|
||||||
@ -438,15 +363,10 @@ class Ui_MainWindow(object):
|
|||||||
self.verticalLayout_7.addWidget(self.label_silent_timer)
|
self.verticalLayout_7.addWidget(self.label_silent_timer)
|
||||||
self.horizontalLayout.addWidget(self.frame_silent)
|
self.horizontalLayout.addWidget(self.frame_silent)
|
||||||
self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame)
|
self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
|
||||||
)
|
|
||||||
sizePolicy.setHorizontalStretch(1)
|
sizePolicy.setHorizontalStretch(1)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(
|
sizePolicy.setHeightForWidth(self.widgetFadeVolume.sizePolicy().hasHeightForWidth())
|
||||||
self.widgetFadeVolume.sizePolicy().hasHeightForWidth()
|
|
||||||
)
|
|
||||||
self.widgetFadeVolume.setSizePolicy(sizePolicy)
|
self.widgetFadeVolume.setSizePolicy(sizePolicy)
|
||||||
self.widgetFadeVolume.setMinimumSize(QtCore.QSize(0, 0))
|
self.widgetFadeVolume.setMinimumSize(QtCore.QSize(0, 0))
|
||||||
self.widgetFadeVolume.setObjectName("widgetFadeVolume")
|
self.widgetFadeVolume.setObjectName("widgetFadeVolume")
|
||||||
@ -463,11 +383,7 @@ class Ui_MainWindow(object):
|
|||||||
self.btnFade.setMinimumSize(QtCore.QSize(132, 32))
|
self.btnFade.setMinimumSize(QtCore.QSize(132, 32))
|
||||||
self.btnFade.setMaximumSize(QtCore.QSize(164, 16777215))
|
self.btnFade.setMaximumSize(QtCore.QSize(164, 16777215))
|
||||||
icon4 = QtGui.QIcon()
|
icon4 = QtGui.QIcon()
|
||||||
icon4.addPixmap(
|
icon4.addPixmap(QtGui.QPixmap(":/icons/fade"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(":/icons/fade"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.btnFade.setIcon(icon4)
|
self.btnFade.setIcon(icon4)
|
||||||
self.btnFade.setIconSize(QtCore.QSize(30, 30))
|
self.btnFade.setIconSize(QtCore.QSize(30, 30))
|
||||||
self.btnFade.setObjectName("btnFade")
|
self.btnFade.setObjectName("btnFade")
|
||||||
@ -475,11 +391,7 @@ class Ui_MainWindow(object):
|
|||||||
self.btnStop = QtWidgets.QPushButton(parent=self.frame)
|
self.btnStop = QtWidgets.QPushButton(parent=self.frame)
|
||||||
self.btnStop.setMinimumSize(QtCore.QSize(0, 36))
|
self.btnStop.setMinimumSize(QtCore.QSize(0, 36))
|
||||||
icon5 = QtGui.QIcon()
|
icon5 = QtGui.QIcon()
|
||||||
icon5.addPixmap(
|
icon5.addPixmap(QtGui.QPixmap(":/icons/stopsign"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(":/icons/stopsign"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.btnStop.setIcon(icon5)
|
self.btnStop.setIcon(icon5)
|
||||||
self.btnStop.setObjectName("btnStop")
|
self.btnStop.setObjectName("btnStop")
|
||||||
self.verticalLayout_5.addWidget(self.btnStop)
|
self.verticalLayout_5.addWidget(self.btnStop)
|
||||||
@ -505,71 +417,39 @@ class Ui_MainWindow(object):
|
|||||||
MainWindow.setStatusBar(self.statusbar)
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
self.actionPlay_next = QtGui.QAction(parent=MainWindow)
|
self.actionPlay_next = QtGui.QAction(parent=MainWindow)
|
||||||
icon6 = QtGui.QIcon()
|
icon6 = QtGui.QIcon()
|
||||||
icon6.addPixmap(
|
icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-play.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-play.png"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.actionPlay_next.setIcon(icon6)
|
self.actionPlay_next.setIcon(icon6)
|
||||||
self.actionPlay_next.setObjectName("actionPlay_next")
|
self.actionPlay_next.setObjectName("actionPlay_next")
|
||||||
self.actionSkipToNext = QtGui.QAction(parent=MainWindow)
|
self.actionSkipToNext = QtGui.QAction(parent=MainWindow)
|
||||||
icon7 = QtGui.QIcon()
|
icon7 = QtGui.QIcon()
|
||||||
icon7.addPixmap(
|
icon7.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(":/icons/next"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.actionSkipToNext.setIcon(icon7)
|
self.actionSkipToNext.setIcon(icon7)
|
||||||
self.actionSkipToNext.setObjectName("actionSkipToNext")
|
self.actionSkipToNext.setObjectName("actionSkipToNext")
|
||||||
self.actionInsertTrack = QtGui.QAction(parent=MainWindow)
|
self.actionInsertTrack = QtGui.QAction(parent=MainWindow)
|
||||||
icon8 = QtGui.QIcon()
|
icon8 = QtGui.QIcon()
|
||||||
icon8.addPixmap(
|
icon8.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(
|
|
||||||
"app/ui/../../../../../../.designer/backup/icon_search_database.png"
|
|
||||||
),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.actionInsertTrack.setIcon(icon8)
|
self.actionInsertTrack.setIcon(icon8)
|
||||||
self.actionInsertTrack.setObjectName("actionInsertTrack")
|
self.actionInsertTrack.setObjectName("actionInsertTrack")
|
||||||
self.actionAdd_file = QtGui.QAction(parent=MainWindow)
|
self.actionAdd_file = QtGui.QAction(parent=MainWindow)
|
||||||
icon9 = QtGui.QIcon()
|
icon9 = QtGui.QIcon()
|
||||||
icon9.addPixmap(
|
icon9.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(
|
|
||||||
"app/ui/../../../../../../.designer/backup/icon_open_file.png"
|
|
||||||
),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.actionAdd_file.setIcon(icon9)
|
self.actionAdd_file.setIcon(icon9)
|
||||||
self.actionAdd_file.setObjectName("actionAdd_file")
|
self.actionAdd_file.setObjectName("actionAdd_file")
|
||||||
self.actionFade = QtGui.QAction(parent=MainWindow)
|
self.actionFade = QtGui.QAction(parent=MainWindow)
|
||||||
icon10 = QtGui.QIcon()
|
icon10 = QtGui.QIcon()
|
||||||
icon10.addPixmap(
|
icon10.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-fade.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-fade.png"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.actionFade.setIcon(icon10)
|
self.actionFade.setIcon(icon10)
|
||||||
self.actionFade.setObjectName("actionFade")
|
self.actionFade.setObjectName("actionFade")
|
||||||
self.actionStop = QtGui.QAction(parent=MainWindow)
|
self.actionStop = QtGui.QAction(parent=MainWindow)
|
||||||
icon11 = QtGui.QIcon()
|
icon11 = QtGui.QIcon()
|
||||||
icon11.addPixmap(
|
icon11.addPixmap(QtGui.QPixmap(":/icons/stop"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(":/icons/stop"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.actionStop.setIcon(icon11)
|
self.actionStop.setIcon(icon11)
|
||||||
self.actionStop.setObjectName("actionStop")
|
self.actionStop.setObjectName("actionStop")
|
||||||
self.action_Clear_selection = QtGui.QAction(parent=MainWindow)
|
self.action_Clear_selection = QtGui.QAction(parent=MainWindow)
|
||||||
self.action_Clear_selection.setObjectName("action_Clear_selection")
|
self.action_Clear_selection.setObjectName("action_Clear_selection")
|
||||||
self.action_Resume_previous = QtGui.QAction(parent=MainWindow)
|
self.action_Resume_previous = QtGui.QAction(parent=MainWindow)
|
||||||
icon12 = QtGui.QIcon()
|
icon12 = QtGui.QIcon()
|
||||||
icon12.addPixmap(
|
icon12.addPixmap(QtGui.QPixmap(":/icons/previous"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
QtGui.QPixmap(":/icons/previous"),
|
|
||||||
QtGui.QIcon.Mode.Normal,
|
|
||||||
QtGui.QIcon.State.Off,
|
|
||||||
)
|
|
||||||
self.action_Resume_previous.setIcon(icon12)
|
self.action_Resume_previous.setIcon(icon12)
|
||||||
self.action_Resume_previous.setObjectName("action_Resume_previous")
|
self.action_Resume_previous.setObjectName("action_Resume_previous")
|
||||||
self.actionE_xit = QtGui.QAction(parent=MainWindow)
|
self.actionE_xit = QtGui.QAction(parent=MainWindow)
|
||||||
@ -616,9 +496,7 @@ class Ui_MainWindow(object):
|
|||||||
self.actionImport = QtGui.QAction(parent=MainWindow)
|
self.actionImport = QtGui.QAction(parent=MainWindow)
|
||||||
self.actionImport.setObjectName("actionImport")
|
self.actionImport.setObjectName("actionImport")
|
||||||
self.actionDownload_CSV_of_played_tracks = QtGui.QAction(parent=MainWindow)
|
self.actionDownload_CSV_of_played_tracks = QtGui.QAction(parent=MainWindow)
|
||||||
self.actionDownload_CSV_of_played_tracks.setObjectName(
|
self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks")
|
||||||
"actionDownload_CSV_of_played_tracks"
|
|
||||||
)
|
|
||||||
self.actionSearch = QtGui.QAction(parent=MainWindow)
|
self.actionSearch = QtGui.QAction(parent=MainWindow)
|
||||||
self.actionSearch.setObjectName("actionSearch")
|
self.actionSearch.setObjectName("actionSearch")
|
||||||
self.actionInsertSectionHeader = QtGui.QAction(parent=MainWindow)
|
self.actionInsertSectionHeader = QtGui.QAction(parent=MainWindow)
|
||||||
@ -646,13 +524,9 @@ class Ui_MainWindow(object):
|
|||||||
self.actionResume = QtGui.QAction(parent=MainWindow)
|
self.actionResume = QtGui.QAction(parent=MainWindow)
|
||||||
self.actionResume.setObjectName("actionResume")
|
self.actionResume.setObjectName("actionResume")
|
||||||
self.actionSearch_title_in_Wikipedia = QtGui.QAction(parent=MainWindow)
|
self.actionSearch_title_in_Wikipedia = QtGui.QAction(parent=MainWindow)
|
||||||
self.actionSearch_title_in_Wikipedia.setObjectName(
|
self.actionSearch_title_in_Wikipedia.setObjectName("actionSearch_title_in_Wikipedia")
|
||||||
"actionSearch_title_in_Wikipedia"
|
|
||||||
)
|
|
||||||
self.actionSearch_title_in_Songfacts = QtGui.QAction(parent=MainWindow)
|
self.actionSearch_title_in_Songfacts = QtGui.QAction(parent=MainWindow)
|
||||||
self.actionSearch_title_in_Songfacts.setObjectName(
|
self.actionSearch_title_in_Songfacts.setObjectName("actionSearch_title_in_Songfacts")
|
||||||
"actionSearch_title_in_Songfacts"
|
|
||||||
)
|
|
||||||
self.actionSelect_duplicate_rows = QtGui.QAction(parent=MainWindow)
|
self.actionSelect_duplicate_rows = QtGui.QAction(parent=MainWindow)
|
||||||
self.actionSelect_duplicate_rows.setObjectName("actionSelect_duplicate_rows")
|
self.actionSelect_duplicate_rows.setObjectName("actionSelect_duplicate_rows")
|
||||||
self.actionReplace_files = QtGui.QAction(parent=MainWindow)
|
self.actionReplace_files = QtGui.QAction(parent=MainWindow)
|
||||||
@ -749,58 +623,38 @@ class Ui_MainWindow(object):
|
|||||||
self.actionFade.setShortcut(_translate("MainWindow", "Ctrl+Z"))
|
self.actionFade.setShortcut(_translate("MainWindow", "Ctrl+Z"))
|
||||||
self.actionStop.setText(_translate("MainWindow", "S&top"))
|
self.actionStop.setText(_translate("MainWindow", "S&top"))
|
||||||
self.actionStop.setShortcut(_translate("MainWindow", "Ctrl+Alt+S"))
|
self.actionStop.setShortcut(_translate("MainWindow", "Ctrl+Alt+S"))
|
||||||
self.action_Clear_selection.setText(
|
self.action_Clear_selection.setText(_translate("MainWindow", "Clear &selection"))
|
||||||
_translate("MainWindow", "Clear &selection")
|
|
||||||
)
|
|
||||||
self.action_Clear_selection.setShortcut(_translate("MainWindow", "Esc"))
|
self.action_Clear_selection.setShortcut(_translate("MainWindow", "Esc"))
|
||||||
self.action_Resume_previous.setText(
|
self.action_Resume_previous.setText(_translate("MainWindow", "&Resume previous"))
|
||||||
_translate("MainWindow", "&Resume previous")
|
|
||||||
)
|
|
||||||
self.actionE_xit.setText(_translate("MainWindow", "E&xit"))
|
self.actionE_xit.setText(_translate("MainWindow", "E&xit"))
|
||||||
self.actionTest.setText(_translate("MainWindow", "&Test"))
|
self.actionTest.setText(_translate("MainWindow", "&Test"))
|
||||||
self.actionOpenPlaylist.setText(_translate("MainWindow", "O&pen..."))
|
self.actionOpenPlaylist.setText(_translate("MainWindow", "O&pen..."))
|
||||||
self.actionNewPlaylist.setText(_translate("MainWindow", "&New..."))
|
self.actionNewPlaylist.setText(_translate("MainWindow", "&New..."))
|
||||||
self.actionTestFunction.setText(_translate("MainWindow", "&Test function"))
|
self.actionTestFunction.setText(_translate("MainWindow", "&Test function"))
|
||||||
self.actionSkipToFade.setText(
|
self.actionSkipToFade.setText(_translate("MainWindow", "&Skip to start of fade"))
|
||||||
_translate("MainWindow", "&Skip to start of fade")
|
|
||||||
)
|
|
||||||
self.actionSkipToEnd.setText(_translate("MainWindow", "Skip to &end of track"))
|
self.actionSkipToEnd.setText(_translate("MainWindow", "Skip to &end of track"))
|
||||||
self.actionClosePlaylist.setText(_translate("MainWindow", "&Close"))
|
self.actionClosePlaylist.setText(_translate("MainWindow", "&Close"))
|
||||||
self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename..."))
|
self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename..."))
|
||||||
self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te..."))
|
self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te..."))
|
||||||
self.actionMoveSelected.setText(
|
self.actionMoveSelected.setText(_translate("MainWindow", "Mo&ve selected tracks to..."))
|
||||||
_translate("MainWindow", "Mo&ve selected tracks to...")
|
|
||||||
)
|
|
||||||
self.actionExport_playlist.setText(_translate("MainWindow", "E&xport..."))
|
self.actionExport_playlist.setText(_translate("MainWindow", "E&xport..."))
|
||||||
self.actionSetNext.setText(_translate("MainWindow", "Set &next"))
|
self.actionSetNext.setText(_translate("MainWindow", "Set &next"))
|
||||||
self.actionSetNext.setShortcut(_translate("MainWindow", "Ctrl+N"))
|
self.actionSetNext.setShortcut(_translate("MainWindow", "Ctrl+N"))
|
||||||
self.actionSelect_next_track.setText(
|
self.actionSelect_next_track.setText(_translate("MainWindow", "Select next track"))
|
||||||
_translate("MainWindow", "Select next track")
|
|
||||||
)
|
|
||||||
self.actionSelect_next_track.setShortcut(_translate("MainWindow", "J"))
|
self.actionSelect_next_track.setShortcut(_translate("MainWindow", "J"))
|
||||||
self.actionSelect_previous_track.setText(
|
self.actionSelect_previous_track.setText(_translate("MainWindow", "Select previous track"))
|
||||||
_translate("MainWindow", "Select previous track")
|
|
||||||
)
|
|
||||||
self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K"))
|
self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K"))
|
||||||
self.actionSelect_played_tracks.setText(
|
self.actionSelect_played_tracks.setText(_translate("MainWindow", "Select played tracks"))
|
||||||
_translate("MainWindow", "Select played tracks")
|
self.actionMoveUnplayed.setText(_translate("MainWindow", "Move &unplayed tracks to..."))
|
||||||
)
|
|
||||||
self.actionMoveUnplayed.setText(
|
|
||||||
_translate("MainWindow", "Move &unplayed tracks to...")
|
|
||||||
)
|
|
||||||
self.actionAdd_note.setText(_translate("MainWindow", "Add note..."))
|
self.actionAdd_note.setText(_translate("MainWindow", "Add note..."))
|
||||||
self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T"))
|
self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T"))
|
||||||
self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls"))
|
self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls"))
|
||||||
self.actionImport.setText(_translate("MainWindow", "Import track..."))
|
self.actionImport.setText(_translate("MainWindow", "Import track..."))
|
||||||
self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I"))
|
self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I"))
|
||||||
self.actionDownload_CSV_of_played_tracks.setText(
|
self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks..."))
|
||||||
_translate("MainWindow", "Download CSV of played tracks...")
|
|
||||||
)
|
|
||||||
self.actionSearch.setText(_translate("MainWindow", "Search..."))
|
self.actionSearch.setText(_translate("MainWindow", "Search..."))
|
||||||
self.actionSearch.setShortcut(_translate("MainWindow", "/"))
|
self.actionSearch.setShortcut(_translate("MainWindow", "/"))
|
||||||
self.actionInsertSectionHeader.setText(
|
self.actionInsertSectionHeader.setText(_translate("MainWindow", "Insert §ion header..."))
|
||||||
_translate("MainWindow", "Insert §ion header...")
|
|
||||||
)
|
|
||||||
self.actionInsertSectionHeader.setShortcut(_translate("MainWindow", "Ctrl+H"))
|
self.actionInsertSectionHeader.setShortcut(_translate("MainWindow", "Ctrl+H"))
|
||||||
self.actionRemove.setText(_translate("MainWindow", "&Remove track"))
|
self.actionRemove.setText(_translate("MainWindow", "&Remove track"))
|
||||||
self.actionFind_next.setText(_translate("MainWindow", "Find next"))
|
self.actionFind_next.setText(_translate("MainWindow", "Find next"))
|
||||||
@ -808,12 +662,8 @@ class Ui_MainWindow(object):
|
|||||||
self.actionFind_previous.setText(_translate("MainWindow", "Find previous"))
|
self.actionFind_previous.setText(_translate("MainWindow", "Find previous"))
|
||||||
self.actionFind_previous.setShortcut(_translate("MainWindow", "P"))
|
self.actionFind_previous.setShortcut(_translate("MainWindow", "P"))
|
||||||
self.action_About.setText(_translate("MainWindow", "&About"))
|
self.action_About.setText(_translate("MainWindow", "&About"))
|
||||||
self.actionSave_as_template.setText(
|
self.actionSave_as_template.setText(_translate("MainWindow", "Save as template..."))
|
||||||
_translate("MainWindow", "Save as template...")
|
self.actionNew_from_template.setText(_translate("MainWindow", "New from template..."))
|
||||||
)
|
|
||||||
self.actionNew_from_template.setText(
|
|
||||||
_translate("MainWindow", "New from template...")
|
|
||||||
)
|
|
||||||
self.actionDebug.setText(_translate("MainWindow", "Debug"))
|
self.actionDebug.setText(_translate("MainWindow", "Debug"))
|
||||||
self.actionAdd_cart.setText(_translate("MainWindow", "Edit cart &1..."))
|
self.actionAdd_cart.setText(_translate("MainWindow", "Edit cart &1..."))
|
||||||
self.actionMark_for_moving.setText(_translate("MainWindow", "Mark for moving"))
|
self.actionMark_for_moving.setText(_translate("MainWindow", "Mark for moving"))
|
||||||
@ -822,23 +672,11 @@ class Ui_MainWindow(object):
|
|||||||
self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V"))
|
self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V"))
|
||||||
self.actionResume.setText(_translate("MainWindow", "Resume"))
|
self.actionResume.setText(_translate("MainWindow", "Resume"))
|
||||||
self.actionResume.setShortcut(_translate("MainWindow", "Ctrl+R"))
|
self.actionResume.setShortcut(_translate("MainWindow", "Ctrl+R"))
|
||||||
self.actionSearch_title_in_Wikipedia.setText(
|
self.actionSearch_title_in_Wikipedia.setText(_translate("MainWindow", "Search title in Wikipedia"))
|
||||||
_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_Wikipedia.setShortcut(
|
self.actionSearch_title_in_Songfacts.setShortcut(_translate("MainWindow", "Ctrl+S"))
|
||||||
_translate("MainWindow", "Ctrl+W")
|
self.actionSelect_duplicate_rows.setText(_translate("MainWindow", "Select duplicate rows..."))
|
||||||
)
|
self.actionReplace_files.setText(_translate("MainWindow", "Import files..."))
|
||||||
self.actionSearch_title_in_Songfacts.setText(
|
from infotabs import InfoTabs # type: ignore
|
||||||
_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.actionReplace_files.setText(_translate("MainWindow", "Replace files..."))
|
|
||||||
|
|
||||||
|
|
||||||
from infotabs import InfoTabs
|
|
||||||
from pyqtgraph import PlotWidget # type: ignore
|
from pyqtgraph import PlotWidget # type: ignore
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user