""" [Module documentaion here] @author:[author], [year] """ from PyQt5 import QtWidgets from PyQt5.QtCore import QSize from path import Path from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QMainWindow, QListWidgetItem, QTableWidgetItem, QFileDialog, QMessageBox from core.logging_ import Logger from core.models import MusicFolder, TrackTag, Track from core.repositories import MusicFolderRepository, TrackRepository, SessionTrackRepository, SessionRepository, \ TrackTagRepository, TagRepository from ui.qt.dlg_meta_editor import DlgMetaEditor from ui.qt.dlg_playlist import DlgPlaylist from ui.qt.dlg_select_playlist import DlgSelectPlaylist from ui.qt.main_ui import Ui_mainWindow from ui.qt.widgets.explorertable import ExplorerTable from ui.qt.widgets.playlist_table import PlaylistTable logger = Logger.get() class MainWindow(QMainWindow): def __init__(self, settings=None): super().__init__() self.settings = settings or {} self.selected_playlist = None self.playing_queue = [] self.selected_track = None # /!\ the selected track is not necessarily the one being played! self.createWidgets() def createWidgets(self): self.ui = Ui_mainWindow() self.ui.setupUi(self) # Stack and menus self.ui.stack.setCurrentIndex(0) menu_items = { 0: (':/img/rsc/dance.png', 'Ma séance'), 1: (':/img/rsc/map.png', 'Explorer'), # 2: (':/img/rsc/calendar.png', 'Agenda'), 3: (':/img/rsc/settings.png', 'Paramètres'), } for i, item in menu_items.items(): icon, lbl = item item = QListWidgetItem(QIcon(icon), lbl) item.index = i self.ui.menu.addItem(item) QtWidgets.qApp.focusChanged.connect(self.focus_changed) # Menu item clicked self.ui.menu.itemClicked.connect(self.menu_item_selected) # Page 1 - Homepage self.ui.sessionPlaylist.trackDoubleClicked.connect(self.play_playlist) self.ui.sessionBtnChange.clicked.connect(self.selectPlaylist) self.ui.btnSessionStart.clicked.connect(self.play_playlist) # Page 3 - explorer self.ui.explorerTable.populate() self.ui.explorerTable.trackSelected.connect(self.newTrackSelected) self.ui.explorerTable.trackDoubleClicked.connect(self.play_track) self.ui.btnExplorerRefresh.clicked.connect(self.refresh_explorer_tree) self.ui.explorerLineSearch.editingFinished.connect(self.explorerFiltersChanged) self.ui.explorerLineSearch.textChanged.connect(self.explorerFiltersChanged) # when search bar is cleared self.ui.explorerBtnSearch.clicked.connect(self.explorerFiltersChanged) self.populate_explorer_filter_tags() self.ui.explorerFilterTags.currentIndexChanged.connect(self.explorerFiltersChanged) self.ui.explorerTrackEdit.clicked.connect(self.showMetaEditor) self.ui.lineSearchTags.textChanged.connect(self.ui.explorerTrackTagsTable.filter) self.ui.explorerTrackTagsTable.tagChecked.connect(self.addTrackTags) self.ui.explorerTrackTagsTable.tagUnchecked.connect(self.removeTrackTags) self.ui.explorerTrackMetaStack.setCurrentIndex(0) self.ui.explorerTrackPlay.clicked.connect(self.explorerPlaySelected) self.ui.btnSelectPlaylist.clicked.connect(self.selectPlaylist) self.ui.explorerAddToPlaylist.clicked.connect(self.add_to_playlist) self.ui.explorerPlaylist.trackSelected.connect(self.newTrackSelected) self.ui.explorerPlaylist.trackDoubleClicked.connect(self.play_playlist) self.ui.btnPlaylistMoveToTop.clicked.connect(self.ui.explorerPlaylist.move_selected_to_top) self.ui.btnPlaylistMoveUp.clicked.connect(self.ui.explorerPlaylist.move_selected_up) self.ui.btnPlaylistMoveDown.clicked.connect(self.ui.explorerPlaylist.move_selected_down) self.ui.btnPlaylistMoveToBottom.clicked.connect(self.ui.explorerPlaylist.move_selected_to_bottom) self.ui.btnPlaylistRemove.clicked.connect(self.remove_from_playlist) # Page 5 - settings self.ui.settingsMusicFoldersTable.setColumnHidden(0, 1) self.ui.musicFoldersAddButton.clicked.connect(self.add_music_folder) self.ui.musicFoldersRemoveButton.clicked.connect(self.remove_music_folder) self.populate_music_folders_table() self.ui.tagsTableAddButton.clicked.connect(self.addTag) self.ui.tableTagsRemoveButton.clicked.connect(self.removeTag) self.ui.settingsTagsTableWidget.selectable = False self.ui.settingsTagsTableWidget.populate() self.ui.settingsTagsTableWidget.contentChanged.connect(self.ui.explorerTrackTagsTable.populate) # vlc frame self.ui.vlcFrame.trackStarted.connect(self.vlcTrackStarted) self.ui.vlcFrame.playlistEnded.connect(self.vlcPlaylistEnded) # Apply settings if self.settings: self.ui.vlcFrame.set_volume(int(self.settings['volume'])) if self.settings['muted']: self.ui.vlcFrame.toggle_muted() playlist_id = self.settings['playlist'] if playlist_id is not None and playlist_id > 0: playlist_repo = SessionRepository() playlist = playlist_repo.get_by_id(playlist_id) self.loadPlaylist(playlist) def focus_changed(self, old, new): if old is self.ui.explorerTrackNotepad: self.saveTrackNotes() def menu_item_selected(self, e): self.ui.stack.setCurrentIndex(e.index) def indexation_started(self): self.ui.statusbar.setStatusTip("Indexation en cours...") def indexation_ended(self): self.ui.statusbar.setStatusTip("Indexation terminée.") def refresh_explorer_tree(self, tracks=None): if tracks: self.ui.explorerTable.update_with(tracks) else: self.ui.explorerTable.populate() self.ui.explorerLineSearch.clear() def newTrackSelected(self, track=None): sender = self.sender() self.selected_track = None self.ui.explorerTrackMetaStack.setCurrentIndex(0) self.ui.explorerTrackPlay.setEnabled(False) self.ui.explorerAddToPlaylist.setEnabled(False) self.ui.btnPlaylistRemove.setEnabled(False) self.ui.explorerTrackNotepad.setText("") self.ui.frameMissingFileWarning.setVisible(False) if not track: return self.selected_track = track # track infos self.update_meta() self.ui.explorerTrackNotepad.setHtml(track.note) self.ui.explorerTrackTagsTable.populate(track) self.ui.explorerTrackMetaStack.setCurrentIndex(1) # Commands if self.selected_track.status == Track.STATUS_UNAVAILABLE: self.ui.frameMissingFileWarning.setVisible(True) else: self.ui.explorerTrackPlay.setEnabled(True) if type(sender) is ExplorerTable: self.ui.explorerAddToPlaylist.setEnabled(True) elif type(sender) is PlaylistTable: self.ui.btnPlaylistRemove.setEnabled(True) else: raise RuntimeError("Unknown sender") self.ui.explorerTable.viewport().repaint() self.ui.explorerPlaylist.viewport().repaint() self.ui.sessionPlaylist.viewport().repaint() def update_meta(self): if self.selected_track: title, artist, album, track_num = self.selected_track.title, \ self.selected_track.artist, \ self.selected_track.album, \ self.selected_track.track_num else: title, artist, album, track_num = "", "", "", "" self.ui.explorerLblTrackTitle.setText(title) self.ui.explorerLblTrackArtist.setText(artist) self.ui.explorerLblTrackAlbum.setText(album) self.ui.explorerLblTrackNumber.setText( str(track_num if track_num is not None else "") ) def populate_explorer_filter_tags(self): tag_repo = TagRepository() self.ui.explorerFilterTags.clear() self.ui.explorerFilterTags.addItem("Toutes les étiquettes", 0) self.ui.explorerFilterTags.setCurrentIndex(0) for tag in tag_repo.get_all(): self.ui.explorerFilterTags.addItem(tag.label, tag.id) def populate_music_folders_table(self): music_folders = MusicFolderRepository().get_all() self.ui.settingsMusicFoldersTable.setRowCount(0) self.ui.settingsMusicFoldersTable.setRowCount(len(music_folders)) music_folder_statuses = [ ('Inconnu', ':/img/rsc/unknown.png'), ('Valide', ':/img/rsc/valid.png'), ('Inaccessible', ':/img/rsc/invalid.png') ] for i, music_folder in enumerate(music_folders): self.ui.settingsMusicFoldersTable.setItem(i, 0, QTableWidgetItem(music_folder.id)) status_lbl, status_pic = music_folder_statuses[music_folder.status] self.ui.settingsMusicFoldersTable.setItem(i, 1, QTableWidgetItem(QIcon(status_pic), status_lbl)) self.ui.settingsMusicFoldersTable.setItem(i, 2, QTableWidgetItem(music_folder.path)) def addTag(self): self.ui.settingsTagsTableWidget.add() def removeTag(self): if QMessageBox.question(self, "Confirmer", "Êtes-vous sûr(e) de vouloir supprimer cette étiquette?") != QMessageBox.Yes: return self.ui.settingsTagsTableWidget.removeSelected() def explorerPlaySelected(self): track = self.ui.explorerTable.selected_track() if track is None: return self.play_track(track) def play_track(self, track): if not Path(track.path).exists(): return logger.info("Start playing: %s" % track) self.ui.vlcFrame.load_track(track) self.ui.vlcFrame.play() def showMetaEditor(self): if not self.selected_track: return r = DlgMetaEditor.edit(self, self.selected_track) if r: self.update_meta() def add_music_folder(self): path = QFileDialog.getExistingDirectory(self, "Sélectionnez le dossier à ajouter") if not path: return path = Path(path) repo = MusicFolderRepository() music_folders = repo.get_all() # for folder in music_folders: # if path == Path(folder.path): # QMessageBox.warning(self, "Ajout invalide", "Ce dossier a déjà été ajouté") # return # # if is_subdir_of(path, Path(folder.path)): # QMessageBox.warning(self, "Ajout invalide", "Ce dossier est contenu dans un dossier existant") # return folder = MusicFolder(path=path) repo.create(folder, True) self.populate_music_folders_table() def remove_music_folder(self): pass def createOrEditPlaylist(self): r = DlgPlaylist.edit(self) def selectPlaylist(self): playlist = DlgSelectPlaylist.select(self) if playlist: self.loadPlaylist(playlist) def loadPlaylist(self, playlist): # home page self.ui.sessionPlaylist.setEnabled(True) self.ui.frameNotes.setEnabled(True) self.ui.sessionLblTitle.setText(playlist.name) self.ui.sessionPlaylist.populate(playlist) self.ui.frameNotes.set_playlist(playlist) # explorer page self.ui.explorerPlaylist.setEnabled(True) self.ui.explorerLblPlaylistTitle.setText(playlist.name) self.ui.explorerPlaylist.populate(playlist) self.selected_playlist = playlist def add_to_playlist(self): track = self.ui.explorerTable.selected_track() if track is None: return playlist = self.ui.explorerPlaylist.playlist if playlist is None: return track_repo = TrackRepository() track_repo.add_to_session( track.id, playlist.id ) self.ui.explorerPlaylist.update() self.ui.sessionPlaylist.update() def remove_from_playlist(self): session_track = self.ui.explorerPlaylist.selected_session_track() if session_track is None: return session_track_repo = SessionTrackRepository() session_track_repo.delete(session_track, True) self.ui.explorerPlaylist.update() self.ui.sessionPlaylist.update() def play_playlist(self, track=None): if track.status == Track.STATUS_UNAVAILABLE: track = None playlist = self.selected_playlist track_repo = TrackRepository() tracks = track_repo.get_by_session_id(playlist.id) tracks = [t for t in tracks if t.status != Track.STATUS_UNAVAILABLE] if not tracks: return start_at = tracks.index(track) if track else 0 logger.info("Start playing playlist: %s from index %s" % (playlist, start_at)) self.ui.vlcFrame.load_playlist(playlist, tracks, start_at) self.ui.vlcFrame.play() def vlcTrackStarted(self, track): if self.selected_playlist: self.ui.explorerPlaylist.set_is_playing(track) self.ui.sessionPlaylist.set_is_playing(track) else: self.ui.explorerPlaylist.set_is_playing(None) self.ui.sessionPlaylist.set_is_playing(None) def vlcPlaylistEnded(self): self.ui.explorerPlaylist.set_is_playing(None) self.ui.sessionPlaylist.set_is_playing(None) def addTrackTags(self, tag_id): track_id = self.selected_track.id track_tag_repo = TrackTagRepository() tt = TrackTag(track_id=track_id, tag_id=tag_id) track_tag_repo.create(tt) track_tag_repo.commit() def removeTrackTags(self, tag_id): track_id = self.selected_track.id track_tag_repo = TrackTagRepository() track_tag_repo.query().filter(TrackTag.track_id == track_id).filter(TrackTag.tag_id == tag_id).delete() track_tag_repo.commit() def saveTrackNotes(self): if not self.selected_track: return note = self.ui.explorerTrackNotepad.toHtml() track_repo = TrackRepository() self.selected_track.note = note track_repo.commit() print('track notes saved') def explorerFiltersChanged(self): searchText = self.ui.explorerLineSearch.text() tag_id = int(self.ui.explorerFilterTags.currentData()) self.ui.explorerTable.filterBy(tag_id, searchText) def filesIndexed(self, tracks): self.statusBar().showMessage(f"{len(tracks)} fichiers indexés", 3000) self.refresh_explorer_tree(tracks) def musicFolderStatusChanged(self, music_folder_id): self.populate_music_folders_table() def currentSettings(self): volume = self.ui.vlcFrame.volume playlist_id = self.selected_playlist.id if self.selected_playlist else None muted = self.ui.vlcFrame.is_muted return { "playlist": playlist_id, "volume": volume, "muted": muted, } def __del__(self): if self.qdb.isOpen(): self.qdb.close()