musicmuster/app/musicmuster.py
Keith Edmunds 99409e8626 Right-click menu mostly working
Still to implement:
 - Move to playlist
 - Remove row
2022-08-07 20:20:56 +01:00

1184 lines
44 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
from log import log
# import argparse
# import psutil
import sys
# import threading
# import urllib.parse
# import webbrowser
#
#
# from datetime import datetime, timedelta
# from typing import Callable, Dict, List, Optional, Tuple
#
# from PyQt5.QtCore import QDate, QEvent, QProcess, Qt, QTime, QTimer, QUrl
# from PyQt5.QtGui import QColor
# from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView
from PyQt5.QtWidgets import (
QApplication,
# QDialog,
# QFileDialog,
# QInputDialog,
QLabel,
# QLineEdit,
# QListWidgetItem,
QMainWindow,
# QMessageBox,
)
#
from dbconfig import engine, Session
# import helpers
# import music
#
# from config import Config
from models import (
Base,
# Playdates,
Playlists,
# Settings,
Tracks
)
from playlists import PlaylistTab
# from sqlalchemy.orm.exc import DetachedInstanceError
# from ui.dlg_search_database_ui import Ui_Dialog
# from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist
# from ui.downloadcsv_ui import Ui_DateSelect
from config import Config
from ui.main_window_ui import Ui_MainWindow
# from utilities import create_track_from_file, update_db
#
#
# log = logging.getLogger(Config.LOG_NAME)
# class TrackData:
# def __init__(self, track):
# self.id = track.id
# self.title = track.title
# self.artist = track.artist
# self.duration = track.duration
# self.start_gap = track.start_gap
# self.fade_at = track.fade_at
# self.silence_at = track.silence_at
# self.path = track.path
# self.mtime = track.mtime
# self.lastplayed = track.lastplayed
class Window(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.setupUi(self)
# self.timer: QTimer = QTimer()
# self.even_tick: bool = True
# self.playing: bool = False
# self.disable_play_next_controls()
#
# self.music: music.Music = music.Music()
# self.current_track: Optional[Tracks] = None
# self.current_track_playlist_tab: Optional[PlaylistTab] = None
# self.info_tabs: Optional[Dict[str, QWebView]] = {}
# self.next_track: Optional[Tracks] = None
# self.next_track_playlist_tab: Optional[PlaylistTab] = None
# self.previous_track: Optional[Tracks] = None
# self.previous_track_position: Optional[int] = None
#
# self.set_main_window_size()
self.lblSumPlaytime = QLabel("")
self.statusbar.addPermanentWidget(self.lblSumPlaytime)
# self.txtSearch = QLineEdit()
# self.statusbar.addWidget(self.txtSearch)
# self.txtSearch.setHidden(True)
# self.hide_played_tracks = False
#
self.visible_playlist_tab: Callable[[], PlaylistTab] = \
self.tabPlaylist.currentWidget
#
self._load_last_playlists()
# self.enable_play_next_controls()
# self.check_audacity()
# self.timer.start(Config.TIMER_MS)
self.connect_signals_slots()
#
# def set_main_window_size(self) -> None:
# """Set size of window from database"""
#
# with Session() as session:
# record = Settings.get_int_settings(session, "mainwindow_x")
# x = record.f_int or 1
# record = Settings.get_int_settings(session, "mainwindow_y")
# y = record.f_int or 1
# record = Settings.get_int_settings(session, "mainwindow_width")
# width = record.f_int or 1599
# record = Settings.get_int_settings(session, "mainwindow_height")
# height = record.f_int or 981
# self.setGeometry(x, y, width, height)
# return
#
# @staticmethod
# def kae():
# with Session() as session:
# db = session.bind.engine.url.database
# print(f"kae(): {db=}")
#
# @staticmethod
# def check_audacity() -> None:
# """Offer to run Audacity if not running"""
#
# if not Config.CHECK_AUDACITY_AT_STARTUP:
# return
#
# if "audacity" in [i.name() for i in psutil.process_iter()]:
# return
#
# if helpers.ask_yes_no("Audacity not running", "Start Audacity?"):
# QProcess.startDetached(Config.AUDACITY_COMMAND, [])
def clear_selection(self) -> None:
""" Clear selected row"""
if self.visible_playlist_tab():
self.visible_playlist_tab().clear_selection()
#
# def closeEvent(self, event: QEvent) -> None:
# """Don't allow window to close when a track is playing"""
#
# if self.music.playing():
# log.debug("closeEvent() ignored as music is playing")
# event.ignore()
# helpers.show_warning(
# "Track playing",
# "Can't close application while track is playing")
# else:
# log.debug("closeEvent() accepted")
#
# with Session() as session:
# record = Settings.get_int_settings(
# session, "mainwindow_height")
# if record.f_int != self.height():
# record.update(session, {'f_int': self.height()})
#
# record = Settings.get_int_settings(session, "mainwindow_width")
# if record.f_int != self.width():
# record.update(session, {'f_int': self.width()})
#
# record = Settings.get_int_settings(session, "mainwindow_x")
# if record.f_int != self.x():
# record.update(session, {'f_int': self.x()})
#
# record = Settings.get_int_settings(session, "mainwindow_y")
# if record.f_int != self.y():
# record.update(session, {'f_int': self.y()})
#
# # Find a playlist tab (as opposed to an info tab) and
# # save column widths
# if self.current_track_playlist_tab:
# self.current_track_playlist_tab.close()
# elif self.next_track_playlist_tab:
# self.next_track_playlist_tab.close()
#
# event.accept()
def connect_signals_slots(self) -> None:
# self.actionAdd_note.triggered.connect(self.create_note)
self.action_Clear_selection.triggered.connect(self.clear_selection)
# self.actionClosePlaylist.triggered.connect(self.close_playlist_tab)
# self.actionDownload_CSV_of_played_tracks.triggered.connect(
# self.download_played_tracks)
# self.actionEnable_controls.triggered.connect(
# self.enable_play_next_controls)
# self.actionExport_playlist.triggered.connect(self.export_playlist_tab)
# self.actionImport.triggered.connect(self.import_track)
# self.actionFade.triggered.connect(self.fade)
# self.actionMoveSelected.triggered.connect(self.move_selected)
# self.actionNewPlaylist.triggered.connect(self.create_playlist)
# self.actionOpenPlaylist.triggered.connect(self.open_playlist)
# self.actionPlay_next.triggered.connect(self.play_next)
# self.actionSearch.triggered.connect(self.search_playlist)
# self.actionSearch_database.triggered.connect(self.search_database)
# self.actionSelect_next_track.triggered.connect(self.select_next_row)
# self.actionSelect_played_tracks.triggered.connect(self.select_played)
# self.actionSelect_previous_track.triggered.connect(
# self.select_previous_row)
# self.actionSelect_unplayed_tracks.triggered.connect(
# self.select_unplayed)
# self.actionSetNext.triggered.connect(
# lambda: self.tabPlaylist.currentWidget().set_selected_as_next())
# self.actionSkip_next.triggered.connect(self.play_next)
# self.actionStop.triggered.connect(self.stop)
# # self.btnAddNote.clicked.connect(self.create_note)
# # self.btnDatabase.clicked.connect(self.search_database)
# self.btnDrop3db.clicked.connect(self.drop3db)
# self.btnHidePlayed.clicked.connect(self.hide_played)
# self.btnFade.clicked.connect(self.fade)
# # self.btnPlay.clicked.connect(self.play_next)
# # self.btnSetNext.clicked.connect(
# # lambda: self.tabPlaylist.currentWidget().set_selected_as_next())
# # self.btnSongInfo.clicked.connect(self.song_info_search)
# self.btnStop.clicked.connect(self.stop)
# self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
# self.txtSearch.returnPressed.connect(self.search_playlist_return)
# self.txtSearch.textChanged.connect(self.search_playlist_update)
#
# self.timer.timeout.connect(self.tick)
#
# def create_playlist(self) -> None:
# """Create new playlist"""
#
# dlg = QInputDialog(self)
# dlg.setInputMode(QInputDialog.TextInput)
# dlg.setLabelText("Playlist name:")
# dlg.resize(500, 100)
# ok = dlg.exec()
# if ok:
# with Session() as session:
# playlist = Playlists(session, dlg.textValue())
# self.create_playlist_tab(session, playlist)
#
# def close_playlist_tab(self) -> None:
# """Close active playlist tab"""
#
# self.close_tab(self.tabPlaylist.currentIndex())
#
# def close_tab(self, index: int) -> None:
# """
# Close tab unless it holds the curren or next track
# """
#
# if hasattr(self.tabPlaylist.widget(index), 'playlist_id'):
# if self.tabPlaylist.widget(index) == (
# self.current_track_playlist_tab):
# self.statusbar.showMessage(
# "Can't close current track playlist", 5000)
# return
# if self.tabPlaylist.widget(index) == self.next_track_playlist_tab:
# self.statusbar.showMessage(
# "Can't close next track playlist", 5000)
# return
# # It's OK to close this playlist so remove from open playlist list
# self.tabPlaylist.widget(index).close()
#
# # Close regardless of tab type
# self.tabPlaylist.removeTab(index)
#
# def create_note(self) -> None:
# """Call playlist to create note"""
#
# try:
# self.visible_playlist_tab().create_note()
# except AttributeError:
# # Just return if there's no visible playlist tab
# return
def create_playlist_tab(self, session: Session,
playlist: Playlists) -> None:
"""
Take the passed playlist database object, create a playlist tab and
add tab to display.
"""
playlist_tab: PlaylistTab = PlaylistTab(
musicmuster=self, session=session, playlist_id=playlist.id)
idx: int = self.tabPlaylist.addTab(playlist_tab, playlist.name)
self.tabPlaylist.setCurrentIndex(idx)
#
# def disable_play_next_controls(self) -> None:
# """
# Disable "play next" keyboard controls
# """
#
# log.debug("disable_play_next_controls()")
# self.actionPlay_next.setEnabled(False)
# self.statusbar.showMessage("Play controls: Disabled", 0)
#
# def download_played_tracks(self) -> None:
# """Download a CSV of played tracks"""
#
# dlg = DownloadCSV(self)
# if dlg.exec():
# start_dt = dlg.ui.dateTimeEdit.dateTime().toPyDateTime()
# # Get output filename
# pathspec: Tuple[str, str] = QFileDialog.getSaveFileName(
# self, 'Save CSV of tracks played',
# directory="/tmp/playlist.csv",
# filter="CSV files (*.csv)"
# )
# if not pathspec:
# return
#
# path: str = pathspec[0]
# if not path.endswith(".csv"):
# path += ".csv"
#
# with open(path, "w") as f:
# with Session() as session:
# for playdate in Playdates.played_after(session, start_dt):
# f.write(
# f"{playdate.track.artist},{playdate.track.title}\n"
# )
#
# def drop3db(self) -> None:
# """Drop music level by 3db if button checked"""
#
# if self.btnDrop3db.isChecked():
# self.music.set_volume(Config.VOLUME_VLC_DROP3db, set_default=False)
# else:
# self.music.set_volume(Config.VOLUME_VLC_DEFAULT, set_default=False)
#
# def enable_play_next_controls(self) -> None:
# """
# Enable "play next" keyboard controls
# """
#
# log.debug("enable_play_next_controls()")
# self.actionPlay_next.setEnabled(True)
# self.statusbar.showMessage("Play controls: Enabled", 0)
#
# def end_of_track_actions(self) -> None:
# """
# Clean up after track played
#
# Actions required:
# - Set flag to say we're not playing a track
# - Reset current track
# - Tell playlist_tab track has finished
# - Reset current playlist_tab
# - Reset clocks
# - Update headers
# - Enable controls
# """
#
# # Set flag to say we're not playing a track so that tick()
# # doesn't see player=None and kick off end-of-track actions
# self.playing = False
#
# # Reset current track
# if self.current_track:
# self.previous_track = self.current_track
# self.current_track = None
#
# # Tell playlist_tab track has finished and
# # reset current playlist_tab
# if self.current_track_playlist_tab:
# self.current_track_playlist_tab.play_stopped()
# self.current_track_playlist_tab = None
#
# # Reset clocks
# self.frame_fade.setStyleSheet("")
# self.frame_silent.setStyleSheet("")
# self.label_elapsed_timer.setText("00:00")
# self.label_end_timer.setText("00:00")
# self.label_fade_length.setText("0:00")
# self.label_fade_timer.setText("00:00")
# self.label_silent_timer.setText("00:00")
# self.label_track_length.setText("0:00")
# self.label_start_time.setText("00:00:00")
# self.label_end_time.setText("00:00:00")
#
# # Update headers
# self.update_headers()
#
# # Enable controls
# self.enable_play_next_controls()
#
# def export_playlist_tab(self) -> None:
# """Export the current playlist to an m3u file"""
#
# if not self.visible_playlist_tab():
# return
#
# with Session() as session:
# playlist = Playlists.get_by_id(
# session, self.visible_playlist_tab().playlist_id)
# # Get output filename
# pathspec: Tuple[str, str] = QFileDialog.getSaveFileName(
# self, 'Save Playlist',
# directory=f"{playlist.name}.m3u",
# filter="M3U files (*.m3u);;All files (*.*)"
# )
# if not pathspec:
# return
#
# path: str = pathspec[0]
# if not path.endswith(".m3u"):
# path += ".m3u"
#
# with open(path, "w") as f:
# # Required directive on first line
# f.write("#EXTM3U\n")
# for _, track in playlist.tracks.items():
# f.write(
# "#EXTINF:"
# f"{int(track.duration / 1000)},"
# f"{track.title} - "
# f"{track.artist}"
# "\n"
# f"{track.path}"
# "\n"
# )
#
# def fade(self) -> None:
# """Fade currently playing track"""
#
# log.debug("musicmuster:fade()", True)
#
# self.stop_playing(fade=True)
#
# def hide_played(self):
# """Toggle hide played tracks"""
#
# if self.hide_played_tracks:
# self.hide_played_tracks = False
# self.btnHidePlayed.setText("Hide played")
# else:
# self.hide_played_tracks = True
# self.btnHidePlayed.setText("Show played")
# if self.current_track_playlist_tab:
# with Session() as session:
# self.current_track_playlist_tab.update_display(session)
#
# def import_track(self) -> None:
# """Import track file"""
#
# dlg = QFileDialog()
# dlg.setFileMode(QFileDialog.ExistingFiles)
# dlg.setViewMode(QFileDialog.Detail)
# dlg.setDirectory(Config.IMPORT_DESTINATION)
# dlg.setNameFilter("Music files (*.flac *.mp3)")
#
# if dlg.exec_():
# with Session() as session:
# txt: str = ""
# new_tracks = []
# for fname in dlg.selectedFiles():
# tags = helpers.get_tags(fname)
# new_tracks.append((fname, tags))
# title = tags['title']
# artist = tags['artist']
# possible_matches = Tracks.search_titles(session, title)
# if possible_matches:
# txt += 'Similar to new track '
# txt += f'"{title}" by "{artist} ({fname})":\n\n'
# for track in possible_matches:
# txt += f' "{track.title}" by {track.artist}'
# txt += f' ({track.path})\n'
# txt += "\n"
# # Check whether to proceed if there were potential matches
# if txt:
# txt += "Proceed with import?"
# result = QMessageBox.question(self,
# "Possible duplicates",
# txt,
# QMessageBox.Ok,
# QMessageBox.Cancel
# )
# if result == QMessageBox.Cancel:
# return
#
# # Import in separate thread
# thread = threading.Thread(target=self._import_tracks,
# args=(new_tracks,))
# thread.start()
#
# def _import_tracks(self, tracks: list):
# """
# Import passed files. Don't use parent session as that may be invalid
# by the time we need it.
# """
#
# with Session() as session:
# for (fname, tags) in tracks:
# track = create_track_from_file(session, fname, tags=tags)
# # Add to playlist on screen
# # If we don't specify "repaint=False", playlist will
# # also be saved to database
# self.visible_playlist_tab().insert_track(session, track)
def _load_last_playlists(self) -> None:
"""Load the playlists that were open when the last session closed"""
with Session() as session:
for playlist in Playlists.get_open(session):
self.create_playlist_tab(session, playlist)
playlist.mark_open(session)
def move_selected(self) -> None:
"""Move selected rows to another playlist"""
# ***KAE
pass
# with Session() as session:
# visible_tab = self.visible_playlist_tab()
# visible_tab_id = visible_tab.playlist_id
#
# source_playlist = None
# playlists = []
# for playlist in Playlists.get_all(session):
# if playlist.id == visible_tab_id:
# source_playlist = playlist
# else:
# playlists.append(playlist)
#
# # Get destination playlist id
# dlg = SelectPlaylistDialog(self, playlists=playlists,
# session=session)
# dlg.exec()
# if not dlg.playlist:
# return
# destination_playlist = dlg.playlist
#
# self.visible_playlist_tab().move_selected_to_playlist(
# session, destination_playlist.id)
#
# # Update destination playlist_tab if visible (if not visible, it
# # will be re-populated when it is opened)
# destination_visible_playlist_tab = None
# for tab in range(self.tabPlaylist.count()):
# # Non-playlist tabs won't have a 'playlist_id' attribute
# if not hasattr(self.tabPlaylist.widget(tab), 'playlist_id'):
# continue
# if self.tabPlaylist.widget(tab).playlist_id == dlg.playlist.id:
# destination_visible_playlist_tab = (
# self.tabPlaylist.widget(tab))
# break
#
# if destination_visible_playlist_tab:
# # We need to commit to database for populate to work
# session.commit()
# destination_visible_playlist_tab.populate(
# session, dlg.playlist.id)
#
# def open_info_tabs(self) -> None:
# """
# Ensure we have info tabs for next and current track titles
# """
#
# title_list: List[str] = []
#
# if self.previous_track:
# title_list.append(self.previous_track.title)
# if self.current_track:
# title_list.append(self.current_track.title)
# if self.next_track:
# title_list.append(self.next_track.title)
#
# for title in title_list:
# if title in self.info_tabs.keys():
# # We already have a tab for this track
# continue
# if len(self.info_tabs) >= Config.MAX_log.info_TABS:
# # Find an unneeded info tab
# try:
# old_title = list(
# set(self.info_tabs.keys()) - set(title_list)
# )[0]
# except IndexError:
# log.debug(
# f"ensure_info_tabs({title_list}): unable to add "
# f"{title=}"
# )
# return
# # Assign redundant widget a new title
# widget = self.info_tabs[title] = self.info_tabs[old_title]
# idx = self.tabPlaylist.indexOf(widget)
# self.tabPlaylist.setTabText(
# idx, title[:Config.log.info_TAB_TITLE_LENGTH])
# del self.info_tabs[old_title]
# else:
# # Create a new tab for this title
# widget = self.info_tabs[title] = QWebView()
# widget.setZoomFactor(Config.WEB_ZOOM_FACTOR)
# self.tabPlaylist.addTab(
# widget, title[:Config.log.info_TAB_TITLE_LENGTH])
# txt = urllib.parse.quote_plus(title)
# url = Config.log.info_TAB_URL % txt
# widget.setUrl(QUrl(url))
#
# def play_next(self) -> None:
# """
# Play next track.
#
# Actions required:
# - If there is no next track set, return.
# - If there's currently a track playing, fade it.
# - Move next track to current track.
# - Update record of current track playlist_tab
# - If current track on different playlist_tab to last, reset
# last track playlist_tab colour
# - Set current track playlist_tab colour
# - Restore volume if -3dB active
# - Play (new) current track.
# - Tell database to record it as played
# - Tell playlist track is now playing
# - Disable play next controls
# - Update headers
# - Update clocks
# """
#
# log.debug(
# "musicmuster.play_next(), "
# f"next_track={self.next_track.title if self.next_track else None} "
# "current_track="
# f"{self.current_track.title if self.current_track else None}",
# True
# )
#
# # If there is no next track set, return.
# if not self.next_track:
# log.debug("musicmuster.play_next(): no next track selected", True)
# return
#
# with Session() as session:
# # If there's currently a track playing, fade it.
# self.stop_playing(fade=True)
#
# # Move next track to current track.
# self.current_track = self.next_track
# self.next_track = None
#
# # If current track on different playlist_tab to last, reset
# # last track playlist_tab colour
# # Set current track playlist_tab colour
# if self.current_track_playlist_tab != self.next_track_playlist_tab:
# self.set_tab_colour(self.current_track_playlist_tab,
# QColor(Config.COLOUR_NORMAL_TAB))
#
# # Update record of current track playlist_tab
# self.current_track_playlist_tab = self.next_track_playlist_tab
# self.next_track_playlist_tab = None
#
# # Set current track playlist_tab colour
# self.set_tab_colour(self.current_track_playlist_tab,
# QColor(Config.COLOUR_CURRENT_TAB))
#
# # Restore volume if -3dB active
# if self.btnDrop3db.isChecked():
# self.btnDrop3db.setChecked(False)
#
# # Play (new) current track
# start_at = datetime.now()
# self.music.play(self.current_track.path)
#
# # Tell database to record it as played
# Playdates(session, self.current_track.id)
#
# # Set last_played date
# Tracks.update_lastplayed(session, self.current_track.id)
#
# # Tell playlist track is now playing
# self.current_track_playlist_tab.play_started(session)
#
# # Disable play next controls
# self.disable_play_next_controls()
#
# # Update headers
# self.update_headers()
#
# # Update clocks
# self.label_track_length.setText(
# helpers.ms_to_mmss(self.current_track.duration)
# )
# fade_at = self.current_track.fade_at
# silence_at = self.current_track.silence_at
# length = self.current_track.duration
# self.label_fade_length.setText(
# helpers.ms_to_mmss(silence_at - fade_at))
# self.label_start_time.setText(
# start_at.strftime(Config.TRACK_TIME_FORMAT))
# end_at = start_at + timedelta(
# milliseconds=self.current_track.duration)
# self.label_end_time.setText(
# end_at.strftime(Config.TRACK_TIME_FORMAT))
#
# def search_database(self) -> None:
# """Show dialog box to select and cue track from database"""
#
# with Session() as session:
# dlg = DbDialog(self, session)
# dlg.exec()
#
# def search_playlist(self):
# """Show text box to search playlist"""
#
# self.disable_play_next_controls()
# self.txtSearch.setHidden(False)
# self.txtSearch.setFocus()
#
# def search_playlist_return(self):
# """Close off search box when return pressed"""
#
# self.txtSearch.setText("")
# self.txtSearch.setHidden(True)
# self.enable_play_next_controls()
# self.visible_playlist_tab().set_filter("")
#
# def search_playlist_update(self):
# """Update search when search string changes"""
#
# self.visible_playlist_tab().set_filter(self.txtSearch.text())
#
# def open_playlist(self):
# with Session() as session:
# playlists = Playlists.get_closed(session)
# dlg = SelectPlaylistDialog(self, playlists=playlists,
# session=session)
# dlg.exec()
# playlist = dlg.playlist
# if playlist:
# playlist.mark_open(session)
# self.create_playlist_tab(session, playlist)
#
# def select_next_row(self) -> None:
# """Select next or first row in playlist"""
#
# self.visible_playlist_tab().select_next_row()
#
# def select_played(self) -> None:
# """Select all played tracks in playlist"""
#
# self.visible_playlist_tab().select_played_tracks()
#
# def select_previous_row(self) -> None:
# """Select previous or first row in playlist"""
#
# self.visible_playlist_tab().select_previous_row()
#
# def select_unplayed(self) -> None:
# """Select all unplayed tracks in playlist"""
#
# self.visible_playlist_tab().select_unplayed_tracks()
#
# def set_tab_colour(self, widget, colour) -> None:
# """
# Find the tab containing the widget and set the text colour
# """
#
# idx: int = self.tabPlaylist.indexOf(widget)
# self.tabPlaylist.tabBar().setTabTextColor(idx, colour)
#
# def song_info_search(self) -> None:
# """
# Open browser tab for Wikipedia, searching for
# the first that exists of:
# - selected track
# - next track
# - current track
# """
#
# title: Optional[str] = self.visible_playlist_tab().get_selected_title()
# if not title:
# if self.next_track:
# title = self.next_track.title
# if not title:
# if self.current_track:
# title = self.current_track.title
# if title:
# txt = urllib.parse.quote_plus(title)
# url = Config.log.info_TAB_URL % txt
# webbrowser.open(url, new=2)
#
# def stop(self) -> None:
# """Stop playing immediately"""
#
# log.debug("musicmuster.stop()")
#
# self.stop_playing(fade=False)
#
# def stop_playing(self, fade=True) -> None:
# """
# Stop playing current track
#
# Actions required:
# - Return if not playing
# - Stop/fade track
# - Reset playlist_tab colour
# - Run end-of-track actions
# """
#
# log.debug(f"musicmuster.stop_playing({fade=})", True)
#
# # Return if not playing
# if not self.playing:
# log.debug("musicmuster.stop_playing(): not playing", True)
# return
#
# # Stop/fade track
# self.previous_track_position = self.music.get_position()
# if fade:
# log.debug("musicmuster.stop_playing(): fading music", True)
# self.music.fade()
# else:
# log.debug("musicmuster.stop_playing(): stopping music", True)
# self.music.stop()
#
# # Reset playlist_tab colour
# if self.current_track_playlist_tab == self.next_track_playlist_tab:
# self.set_tab_colour(self.current_track_playlist_tab,
# QColor(Config.COLOUR_NEXT_TAB))
# else:
# self.set_tab_colour(self.current_track_playlist_tab,
# QColor(Config.COLOUR_NORMAL_TAB))
#
# # Run end-of-track actions
# self.end_of_track_actions()
def this_is_the_next_track(self, playlist_tab: PlaylistTab,
track: Tracks, session) -> None:
"""
This is notification from a playlist tab that it holds the next
track to be played.
Actions required:
- Clear next track if on other tab
- Reset tab colour if on other tab
- Note next playlist tab
- Set next playlist_tab tab colour
- Note next track
- Update headers
- Populate info tabs
"""
# ***kae
return
# Clear next track if on another tab
if self.next_track_playlist_tab != playlist_tab:
# We need to reset the ex-next-track playlist
if self.next_track_playlist_tab:
self.next_track_playlist_tab.clear_next(session)
# Reset tab colour if on other tab
if (self.next_track_playlist_tab !=
self.current_track_playlist_tab):
self.set_tab_colour(
self.next_track_playlist_tab,
QColor(Config.COLOUR_NORMAL_TAB))
# Note next playlist tab
self.next_track_playlist_tab = playlist_tab
# Set next playlist_tab tab colour if it isn't the
# currently-playing tab
if (self.next_track_playlist_tab !=
self.current_track_playlist_tab):
self.set_tab_colour(
self.next_track_playlist_tab,
QColor(Config.COLOUR_NEXT_TAB))
# Note next track
self.next_track = TrackData(track)
# Update headers
self.update_headers()
# Populate 'info' tabs
self.open_info_tabs()
#
# def tick(self) -> None:
# """
# Carry out clock tick actions.
#
# The Time of Day clock is updated every tick (500ms).
#
# All other timers are updated every second. As the timers have a
# one-second resolution, updating every 500ms can result in some
# timers updating and then, 500ms later, other timers updating. That
# looks odd.
#
# Actions required:
# - Update TOD clock
# - If track is playing, update track clocks time and colours
# - Else: run stop_track
# """
#
# # Update TOD clock
# self.lblTOD.setText(datetime.now().strftime(Config.TOD_TIME_FORMAT))
#
# self.even_tick = not self.even_tick
# if not self.even_tick:
# return
#
# # If track is playing, update track clocks time and colours
# if self.music.player and self.music.playing():
# self.playing = True
# playtime: int = self.music.get_playtime()
# time_to_fade: int = (self.current_track.fade_at - playtime)
# time_to_silence: int = (
# self.current_track.silence_at - playtime)
# time_to_end: int = (self.current_track.duration - playtime)
#
# # Elapsed time
# if time_to_end < 500:
# self.label_elapsed_timer.setText(
# helpers.ms_to_mmss(playtime)
# )
# else:
# self.label_elapsed_timer.setText(
# helpers.ms_to_mmss(playtime)
# )
#
# # 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)
# )
#
# # Time to end
# self.label_end_timer.setText(helpers.ms_to_mmss(time_to_end))
#
# else:
# if self.playing:
# self.stop_playing()
#
# def update_headers(self) -> None:
# """
# Update last / current / next track headers
#
# If multiple tracks are played quickly in succession, it's possible
# for the self.{previous,current,next} track to not be in the session.
# Unlikely to happen in normal use so handle by blanking title.
# """
#
# try:
# self.hdrPreviousTrack.setText(
# f"{self.previous_track.title} - {self.previous_track.artist}"
# )
# except (AttributeError, DetachedInstanceError):
# self.hdrPreviousTrack.setText("")
#
# try:
# self.hdrCurrentTrack.setText(
# f"{self.current_track.title} - {self.current_track.artist}"
# )
# except (AttributeError, DetachedInstanceError):
# self.hdrCurrentTrack.setText("")
#
# try:
# self.hdrNextTrack.setText(
# f"{self.next_track.title} - {self.next_track.artist}"
# )
# except (AttributeError, DetachedInstanceError):
# self.hdrNextTrack.setText("")
#
#
# class DbDialog(QDialog):
# """Select track from database"""
#
# def __init__(self, parent, session): # review
# super().__init__(parent)
# self.session = session
# self.ui = Ui_Dialog()
# self.ui.setupUi(self)
# self.ui.btnAdd.clicked.connect(self.add_selected)
# self.ui.btnAddClose.clicked.connect(self.add_selected_and_close)
# self.ui.btnClose.clicked.connect(self.close)
# self.ui.matchList.itemDoubleClicked.connect(self.double_click)
# self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
# self.ui.radioTitle.toggled.connect(self.title_artist_toggle)
# self.ui.searchString.textEdited.connect(self.chars_typed)
#
# record = Settings.get_int_settings(self.session, "dbdialog_width")
# width = record.f_int or 800
# record = Settings.get_int_settings(self.session, "dbdialog_height")
# height = record.f_int or 600
# self.resize(width, height)
#
# def __del__(self): # review
# record = Settings.get_int_settings(self.session, "dbdialog_height")
# if record.f_int != self.height():
# record.update(self.session, {'f_int': self.height()})
#
# record = Settings.get_int_settings(self.session, "dbdialog_width")
# if record.f_int != self.width():
# record.update(self.session, {'f_int': self.width()})
#
# def add_selected(self): # review
# if not self.ui.matchList.selectedItems():
# return
#
# item = self.ui.matchList.currentItem()
# track = item.data(Qt.UserRole)
# self.add_track(track)
#
# def add_selected_and_close(self): # review
# self.add_selected()
# self.close()
#
# def title_artist_toggle(self): # review
# """
# Handle switching between searching for artists and searching for
# titles
# """
#
# # Logic is handled already in chars_typed(), so just call that.
# self.chars_typed(self.ui.searchString.text())
#
# def chars_typed(self, s): # review
# if len(s) > 0:
# if self.ui.radioTitle.isChecked():
# matches = Tracks.search_titles(self.session, s)
# else:
# matches = Tracks.search_artists(self.session, s)
# self.ui.matchList.clear()
# if matches:
# for track in matches:
# t = QListWidgetItem()
# t.setText(
# f"{track.title} - {track.artist} "
# f"[{helpers.ms_to_mmss(track.duration)}] "
# f"({helpers.get_relative_date(track.lastplayed)})"
# )
# t.setData(Qt.UserRole, track)
# self.ui.matchList.addItem(t)
#
# def double_click(self, entry): # review
# track = entry.data(Qt.UserRole)
# self.add_track(track)
# # Select search text to make it easier for next search
# self.select_searchtext()
#
# def add_track(self, track): # review
# # Add to playlist on screen
# self.parent().visible_playlist_tab().insert_track(
# self.session, track)
# # Commit session to get correct row numbers if more tracks added
# self.session.commit()
# # Select search text to make it easier for next search
# self.select_searchtext()
#
# def select_searchtext(self): # review
# self.ui.searchString.selectAll()
# self.ui.searchString.setFocus()
#
# def selection_changed(self): # review
# if not self.ui.matchList.selectedItems():
# return
#
# item = self.ui.matchList.currentItem()
# track = item.data(Qt.UserRole)
# self.ui.dbPath.setText(track.path)
#
#
# class DownloadCSV(QDialog):
# def __init__(self, parent=None):
# super().__init__(parent)
#
# self.ui = Ui_DateSelect()
# self.ui.setupUi(self)
# self.ui.dateTimeEdit.setDate(QDate.currentDate())
# self.ui.dateTimeEdit.setTime(QTime(19, 59, 0))
# self.ui.buttonBox.accepted.connect(self.accept)
# self.ui.buttonBox.rejected.connect(self.reject)
#
#
# class SelectPlaylistDialog(QDialog):
# def __init__(self, parent=None, playlists=None, session=None):
# super().__init__(parent)
#
# if playlists is None:
# return
# self.ui = Ui_dlgSelectPlaylist()
# self.ui.setupUi(self)
# self.ui.lstPlaylists.itemDoubleClicked.connect(self.list_doubleclick)
# self.ui.buttonBox.accepted.connect(self.open)
# self.ui.buttonBox.rejected.connect(self.close)
# self.session = session
# self.playlist = None
# self.plid = None
#
# record = Settings.get_int_settings(
# self.session, "select_playlist_dialog_width")
# width = record.f_int or 800
# record = Settings.get_int_settings(
# self.session, "select_playlist_dialog_height")
# height = record.f_int or 600
# self.resize(width, height)
#
# for playlist in playlists:
# p = QListWidgetItem()
# p.setText(playlist.name)
# p.setData(Qt.UserRole, playlist)
# self.ui.lstPlaylists.addItem(p)
#
# def __del__(self): # review
# record = Settings.get_int_settings(
# self.session, "select_playlist_dialog_height")
# if record.f_int != self.height():
# record.update(self.session, {'f_int': self.height()})
#
# record = Settings.get_int_settings(
# self.session, "select_playlist_dialog_width")
# if record.f_int != self.width():
# record.update(self.session, {'f_int': self.width()})
#
# def list_doubleclick(self, entry): # review
# self.playlist = entry.data(Qt.UserRole)
# self.accept()
#
# def open(self): # review
# if self.ui.lstPlaylists.selectedItems():
# item = self.ui.lstPlaylists.currentItem()
# self.playlist = item.data(Qt.UserRole)
# self.accept()
if __name__ == "__main__":
# p = argparse.ArgumentParser()
# # Only allow at most one option to be specified
# group = p.add_mutually_exclusive_group()
# group.add_argument('-u', '--update',
# action="store_true", dest="update",
# default=False, help="Update database")
# # group.add_argument('-f', '--full-update',
# # action="store_true", dest="full_update",
# # default=False, help="Update database")
# # group.add_argument('-i', '--import', dest="fname", help="Input file")
# args = p.parse_args()
#
# # Run as required
# if args.update:
# log.debug("Updating database")
# with Session() as session:
# update_db(session)
# # elif args.full_update:
# # log.debug("Full update of database")
# # with Session() as session:
# # full_update_db(session)
# else:
# # Normal run
try:
Base.metadata.create_all(engine)
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec())
except Exception:
msg = "Unhandled Exception caught by musicmuster.main()"
log.exception(msg, exc_info=True, stack_info=True)