window.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. """
  2. [Module documentaion here]
  3. @author:[author], [year]
  4. """
  5. from PyQt5 import QtWidgets
  6. from path import Path
  7. from PyQt5.QtGui import QIcon
  8. from PyQt5.QtWidgets import QMainWindow, QListWidgetItem, QMessageBox
  9. from core.logging_ import Logger
  10. from core.models import TrackTag, Track
  11. from core.repositories import TrackRepository, SessionTrackRepository, SessionRepository, \
  12. TrackTagRepository, TagRepository
  13. from ui.qt.dlg_meta_editor import DlgMetaEditor
  14. from ui.qt.dlg_playlist import DlgPlaylist
  15. from ui.qt.dlg_select_playlist import DlgSelectPlaylist
  16. from ui.qt.main_ui import Ui_mainWindow
  17. from ui.qt.widgets.explorertable import ExplorerTable
  18. from ui.qt.widgets.playlist_table import PlaylistTable
  19. logger = Logger.get()
  20. class MainWindow(QMainWindow):
  21. def __init__(self, settings=None):
  22. super().__init__()
  23. self.settings = settings or {}
  24. self.selected_playlist = None
  25. self.playing_queue = []
  26. self.selected_track = None # /!\ the selected track is not necessarily the one being played!
  27. self.createWidgets()
  28. def createWidgets(self):
  29. self.ui = Ui_mainWindow()
  30. self.ui.setupUi(self)
  31. # Stack and menus
  32. self.ui.stack.setCurrentIndex(0)
  33. menu_items = {
  34. 0: (':/img/rsc/dance.png', 'Ma séance'),
  35. 1: (':/img/rsc/map.png', 'Explorer'),
  36. # 2: (':/img/rsc/calendar.png', 'Agenda'),
  37. 3: (':/img/rsc/settings.png', 'Paramètres'),
  38. }
  39. for i, item in menu_items.items():
  40. icon, lbl = item
  41. item = QListWidgetItem(QIcon(icon), lbl)
  42. item.index = i
  43. self.ui.menu.addItem(item)
  44. QtWidgets.qApp.focusChanged.connect(self.focus_changed)
  45. # Menu item clicked
  46. self.ui.menu.itemClicked.connect(self.menu_item_selected)
  47. # Page 1 - Homepage
  48. self.ui.sessionPlaylist.trackDoubleClicked.connect(self.play_playlist)
  49. self.ui.sessionBtnChange.clicked.connect(self.selectPlaylist)
  50. self.ui.btnSessionStart.clicked.connect(self.play_playlist)
  51. # Page 3 - explorer
  52. self.ui.explorerTable.populate()
  53. self.ui.explorerTable.trackSelected.connect(self.newTrackSelected)
  54. self.ui.explorerTable.trackDoubleClicked.connect(self.play_track)
  55. self.ui.btnExplorerRefresh.clicked.connect(self.refresh_explorer_tree)
  56. self.ui.explorerLineSearch.editingFinished.connect(self.explorerFiltersChanged)
  57. self.ui.explorerLineSearch.textChanged.connect(self.explorerFiltersChanged) # when search bar is cleared
  58. self.ui.explorerBtnSearch.clicked.connect(self.explorerFiltersChanged)
  59. self.populate_explorer_filter_tags()
  60. self.ui.explorerTrackEdit.clicked.connect(self.showMetaEditor)
  61. self.ui.lineSearchTags.textChanged.connect(self.ui.explorerTrackTagsTable.filter)
  62. self.ui.explorerTrackTagsTable.tagChecked.connect(self.addTrackTags)
  63. self.ui.explorerTrackTagsTable.tagUnchecked.connect(self.removeTrackTags)
  64. self.ui.explorerTrackMetaStack.setCurrentIndex(0)
  65. self.ui.btnPlaySelectedTrack.clicked.connect(self.playSelectedTrack)
  66. self.ui.btnSelectPlaylist.clicked.connect(self.selectPlaylist)
  67. self.ui.explorerAddToPlaylist.clicked.connect(self.add_to_playlist)
  68. self.ui.explorerPlaylist.droppedEvent.connect(self.add_to_playlist)
  69. self.ui.explorerPlaylist.trackSelected.connect(self.newTrackSelected)
  70. self.ui.explorerPlaylist.trackDoubleClicked.connect(self.play_playlist)
  71. self.ui.btnPlaylistMoveToTop.clicked.connect(self.ui.explorerPlaylist.move_selected_to_top)
  72. self.ui.btnPlaylistMoveUp.clicked.connect(self.ui.explorerPlaylist.move_selected_up)
  73. self.ui.btnPlaylistMoveDown.clicked.connect(self.ui.explorerPlaylist.move_selected_down)
  74. self.ui.btnPlaylistMoveToBottom.clicked.connect(self.ui.explorerPlaylist.move_selected_to_bottom)
  75. self.ui.btnPlaylistMoveToTop.clicked.connect(self.ui.sessionPlaylist.update)
  76. self.ui.btnPlaylistMoveUp.clicked.connect(self.ui.sessionPlaylist.update)
  77. self.ui.btnPlaylistMoveDown.clicked.connect(self.ui.sessionPlaylist.update)
  78. self.ui.btnPlaylistMoveToBottom.clicked.connect(self.ui.sessionPlaylist.update)
  79. self.ui.btnPlaylistRemove.clicked.connect(self.remove_from_playlist)
  80. # Page 5 - settings
  81. self.ui.musicFoldersAddButton.clicked.connect(self.ui.settingsMusicFoldersTable.add_music_folder)
  82. self.ui.musicFoldersRemoveButton.clicked.connect(self.ui.settingsMusicFoldersTable.remove_music_folder)
  83. self.ui.settingsMusicFoldersTable.populate()
  84. self.ui.tagsTableAddButton.clicked.connect(self.addTag)
  85. self.ui.tableTagsRemoveButton.clicked.connect(self.removeTag)
  86. self.ui.settingsTagsTableWidget.selectable = False
  87. self.ui.settingsTagsTableWidget.populate()
  88. self.ui.settingsTagsTableWidget.contentChanged.connect(self.ui.explorerTrackTagsTable.populate)
  89. self.ui.settingsTagsTableWidget.contentChanged.connect(self.populate_explorer_filter_tags)
  90. # vlc frame
  91. self.ui.vlcFrame.trackStarted.connect(self.vlcTrackStarted)
  92. self.ui.vlcFrame.playlistEnded.connect(self.vlcPlaylistEnded)
  93. # Apply settings
  94. if self.settings:
  95. self.ui.vlcFrame.set_volume(int(self.settings['volume']))
  96. self.ui.vlcFrame.set_muted(self.settings['muted'])
  97. playlist_id = self.settings['playlist']
  98. if playlist_id is not None and playlist_id > 0:
  99. playlist_repo = SessionRepository()
  100. playlist = playlist_repo.get_by_id(playlist_id)
  101. self.loadPlaylist(playlist)
  102. def focus_changed(self, old, new):
  103. if old is self.ui.explorerTrackNotepad:
  104. self.saveTrackNotes()
  105. def menu_item_selected(self, e):
  106. self.ui.stack.setCurrentIndex(e.index)
  107. def indexation_started(self):
  108. self.ui.statusbar.setStatusTip("Indexation en cours...")
  109. def indexation_ended(self):
  110. self.ui.statusbar.setStatusTip("Indexation terminée.")
  111. def refresh_explorer_tree(self, tracks=None):
  112. if tracks:
  113. self.ui.explorerTable.update_tracks(tracks)
  114. else:
  115. self.ui.explorerTable.populate()
  116. self.ui.explorerLineSearch.clear()
  117. self.ui.explorerFilterTags.setCurrentIndex(0)
  118. def newTrackSelected(self, track=None):
  119. sender = self.sender()
  120. self.selected_track = None
  121. self.ui.explorerTrackMetaStack.setCurrentIndex(0)
  122. self.ui.btnPlaySelectedTrack.setEnabled(False)
  123. self.ui.explorerAddToPlaylist.setEnabled(False)
  124. self.ui.btnPlaylistRemove.setEnabled(False)
  125. self.ui.explorerTrackNotepad.setText("")
  126. self.ui.frameMissingFileWarning.setVisible(False)
  127. if not track:
  128. return
  129. self.selected_track = track
  130. # track infos
  131. self.update_meta()
  132. self.ui.explorerTrackNotepad.setHtml(track.note)
  133. self.ui.explorerTrackTagsTable.populate(track)
  134. self.ui.explorerTrackMetaStack.setCurrentIndex(1)
  135. # Commands
  136. if self.selected_track.status == Track.STATUS_UNAVAILABLE:
  137. self.ui.frameMissingFileWarning.setVisible(True)
  138. else:
  139. self.ui.btnPlaySelectedTrack.setEnabled(True)
  140. if type(sender) is ExplorerTable:
  141. self.ui.explorerAddToPlaylist.setEnabled(True)
  142. elif type(sender) is PlaylistTable:
  143. self.ui.btnPlaylistRemove.setEnabled(True)
  144. else:
  145. raise RuntimeError("Unknown sender")
  146. self.ui.explorerTable.viewport().repaint()
  147. self.ui.explorerPlaylist.viewport().repaint()
  148. self.ui.sessionPlaylist.viewport().repaint()
  149. def update_meta(self):
  150. if self.selected_track:
  151. title, artist, album, track_num = self.selected_track.title, \
  152. self.selected_track.artist, \
  153. self.selected_track.album, \
  154. self.selected_track.track_num
  155. else:
  156. title, artist, album, track_num = "", "", "", ""
  157. self.ui.explorerLblTrackTitle.setText(title)
  158. self.ui.explorerLblTrackArtist.setText(artist)
  159. self.ui.explorerLblTrackAlbum.setText(album)
  160. self.ui.explorerLblTrackNumber.setText(
  161. str(track_num if track_num is not None else "")
  162. )
  163. if self.selected_track:
  164. # refresh tables
  165. self.refresh_explorer_tree([self.selected_track])
  166. if self.selected_playlist:
  167. self.ui.explorerPlaylist.update()
  168. self.ui.sessionPlaylist.update()
  169. def populate_explorer_filter_tags(self):
  170. tag_repo = TagRepository()
  171. try:
  172. self.ui.explorerFilterTags.currentIndexChanged.disconnect(self.explorerFiltersChanged)
  173. except TypeError:
  174. pass
  175. self.ui.explorerFilterTags.clear()
  176. self.ui.explorerFilterTags.addItem("Toutes les étiquettes", 0)
  177. self.ui.explorerFilterTags.setCurrentIndex(0)
  178. for tag in tag_repo.get_all():
  179. self.ui.explorerFilterTags.addItem(tag.label, tag.id)
  180. self.ui.explorerFilterTags.currentIndexChanged.connect(self.explorerFiltersChanged)
  181. def addTag(self):
  182. self.ui.settingsTagsTableWidget.add()
  183. def removeTag(self):
  184. if QMessageBox.question(self, "Confirmer", "Êtes-vous sûr(e) de vouloir supprimer cette étiquette?") != QMessageBox.Yes:
  185. return
  186. self.ui.settingsTagsTableWidget.removeSelected()
  187. def playSelectedTrack(self):
  188. if self.selected_track:
  189. self.play_track(self.selected_track)
  190. def play_track(self, track):
  191. if not Path(track.path).exists():
  192. return
  193. logger.info("Start playing: %s" % track)
  194. self.ui.vlcFrame.load_track(track)
  195. self.ui.vlcFrame.play()
  196. def showMetaEditor(self):
  197. if not self.selected_track:
  198. return
  199. r = DlgMetaEditor.edit(self, self.selected_track)
  200. if r:
  201. self.update_meta()
  202. def createOrEditPlaylist(self):
  203. r = DlgPlaylist.edit(self)
  204. def selectPlaylist(self):
  205. playlist = DlgSelectPlaylist.select(self)
  206. if playlist:
  207. self.loadPlaylist(playlist)
  208. def loadPlaylist(self, playlist):
  209. # home page
  210. self.ui.sessionPlaylist.setEnabled(True)
  211. self.ui.frameNotes.setEnabled(True)
  212. self.ui.sessionLblTitle.setText(playlist.name)
  213. self.ui.sessionPlaylist.populate(playlist)
  214. self.ui.frameNotes.set_playlist(playlist)
  215. # explorer page
  216. self.ui.explorerPlaylist.setEnabled(True)
  217. self.ui.explorerLblPlaylistTitle.setText(playlist.name)
  218. self.ui.explorerPlaylist.populate(playlist)
  219. self.selected_playlist = playlist
  220. def playlistItemDropped(self, event):
  221. if event.source() == ExplorerTable:
  222. self.add_to_playlist()
  223. def add_to_playlist(self):
  224. track = self.ui.explorerTable.selected_track()
  225. if track is None:
  226. return
  227. playlist = self.ui.explorerPlaylist.playlist
  228. if playlist is None:
  229. return
  230. track_repo = TrackRepository()
  231. track_repo.add_to_session(
  232. track.id,
  233. playlist.id
  234. )
  235. self.ui.explorerPlaylist.update()
  236. self.ui.sessionPlaylist.update()
  237. def remove_from_playlist(self):
  238. session_track = self.ui.explorerPlaylist.selected_session_track()
  239. if session_track is None:
  240. return
  241. session_track_repo = SessionTrackRepository()
  242. session_track_repo.delete(session_track, True)
  243. self.ui.explorerPlaylist.update()
  244. self.ui.sessionPlaylist.update()
  245. def play_playlist(self, track=None):
  246. if track.status == Track.STATUS_UNAVAILABLE:
  247. track = None
  248. playlist = self.selected_playlist
  249. track_repo = TrackRepository()
  250. tracks = track_repo.get_by_session_id(playlist.id)
  251. tracks = [t for t in tracks if t.status != Track.STATUS_UNAVAILABLE]
  252. if not tracks:
  253. return
  254. start_at = tracks.index(track) if track else 0
  255. logger.info("Start playing playlist: %s from index %s" % (playlist, start_at))
  256. self.ui.vlcFrame.load_playlist(playlist, tracks, start_at)
  257. self.ui.vlcFrame.play()
  258. def vlcTrackStarted(self, track):
  259. if self.selected_playlist:
  260. self.ui.explorerPlaylist.set_is_playing(track)
  261. self.ui.sessionPlaylist.set_is_playing(track)
  262. else:
  263. self.ui.explorerPlaylist.set_is_playing(None)
  264. self.ui.sessionPlaylist.set_is_playing(None)
  265. def vlcPlaylistEnded(self):
  266. self.ui.explorerPlaylist.set_is_playing(None)
  267. self.ui.sessionPlaylist.set_is_playing(None)
  268. def addTrackTags(self, tag_id):
  269. track_id = self.selected_track.id
  270. track_tag_repo = TrackTagRepository()
  271. tt = TrackTag(track_id=track_id, tag_id=tag_id)
  272. track_tag_repo.create(tt)
  273. track_tag_repo.commit()
  274. def removeTrackTags(self, tag_id):
  275. track_id = self.selected_track.id
  276. track_tag_repo = TrackTagRepository()
  277. track_tag_repo.query().filter(TrackTag.track_id == track_id).filter(TrackTag.tag_id == tag_id).delete()
  278. track_tag_repo.commit()
  279. def saveTrackNotes(self):
  280. if not self.selected_track:
  281. return
  282. note = self.ui.explorerTrackNotepad.toHtml()
  283. track_repo = TrackRepository()
  284. self.selected_track.note = note
  285. track_repo.commit()
  286. def explorerFiltersChanged(self):
  287. searchText = self.ui.explorerLineSearch.text()
  288. tag_id = int(self.ui.explorerFilterTags.currentData())
  289. self.ui.explorerTable.filterBy(tag_id, searchText)
  290. def filesIndexed(self, tracks):
  291. self.statusBar().showMessage(f"{len(tracks)} fichiers indexés", 3000)
  292. self.refresh_explorer_tree(tracks)
  293. def musicFolderStatusChanged(self, music_folder_id):
  294. self.ui.settingsMusicFoldersTable.populate()
  295. def currentSettings(self):
  296. volume = self.ui.vlcFrame.volume
  297. playlist_id = self.selected_playlist.id if self.selected_playlist else None
  298. muted = self.ui.vlcFrame.is_muted
  299. return {
  300. "playlist": playlist_id,
  301. "volume": volume,
  302. "muted": muted,
  303. }
  304. def __del__(self):
  305. if self.qdb.isOpen():
  306. self.qdb.close()