window.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. """
  2. [Module documentaion here]
  3. @author:[author], [year]
  4. """
  5. from path import Path
  6. from PyQt5.QtGui import QIcon
  7. from PyQt5.QtWidgets import QMainWindow, QListWidgetItem, QTableWidgetItem, QFileDialog
  8. from core.logging_ import Logger
  9. from core.models import MusicFolder
  10. from core.repositories import MusicFolderRepository, TrackRepository, SessionTrackRepository, SessionRepository
  11. from ui.qt.dlg_playlist import DlgPlaylist
  12. from ui.qt.dlg_select_playlist import DlgSelectPlaylist
  13. from ui.qt.main_ui import Ui_mainWindow
  14. from ui.qt.widgets.explorertable import ExplorerTable
  15. from ui.qt.widgets.playlist_table import PlaylistTable
  16. logger = Logger.get()
  17. class MainWindow(QMainWindow):
  18. def __init__(self, settings=None):
  19. super().__init__()
  20. self.settings = settings or {}
  21. self.selected_playlist = None
  22. self.playing_queue = []
  23. self.selected_track = None # /!\ the selected track is not necessarily the one being played!
  24. self.createWidgets()
  25. def createWidgets(self):
  26. self.ui = Ui_mainWindow()
  27. self.ui.setupUi(self)
  28. # Stack and menus
  29. self.ui.stack.setCurrentIndex(0)
  30. menu_items = {
  31. 0: (':/img/rsc/dance.png', 'Ma séance'),
  32. # 1: (':/img/rsc/writing-tool.png', 'Préparer'),
  33. 2: (':/img/rsc/map.png', 'Explorer'),
  34. # 3: (':/img/rsc/calendar.png', 'Agenda'),
  35. 4: (':/img/rsc/settings.png', 'Paramètres'),
  36. }
  37. for i, item in menu_items.items():
  38. icon, lbl = item
  39. item = QListWidgetItem(QIcon(icon), lbl)
  40. item.index = i
  41. self.ui.menu.addItem(item)
  42. # Menu item clicked
  43. self.ui.menu.itemClicked.connect(self.menu_item_selected)
  44. # Page 1 - Homepage
  45. self.ui.sessionPlaylist.trackDoubleClicked.connect(self.play_playlist)
  46. self.ui.sessionBtnChange.clicked.connect(self.selectPlaylist)
  47. self.ui.frameNotes.ui.sessionNotes.textChanged.connect(self.sessionNotesChanged)
  48. self.ui.btnSessionStart.clicked.connect(self.play_playlist)
  49. # Page 3 - explorer
  50. self.ui.explorerTable.populate()
  51. self.ui.explorerTable.trackSelected.connect(self.newTrackSelected)
  52. self.ui.explorerTable.trackDoubleClicked.connect(self.play_track)
  53. self.ui.btnExplorerRefresh.clicked.connect(self.refresh_explorer_tree)
  54. self.ui.explorerLineSearch.editingFinished.connect(self.explorerSearchChanged)
  55. self.ui.explorerLineSearch.textChanged.connect(lambda s: (s or self.explorerSearchChanged())) # when search bar is cleared
  56. self.ui.explorerBtnSearch.clicked.connect(self.explorerSearchChanged)
  57. self.ui.explorerTrackMetaStack.setCurrentIndex(0)
  58. self.ui.explorerTrackNotepad.textChanged.connect(self.explorerTrackNotesChanged)
  59. self.ui.explorerTrackPlay.clicked.connect(self.explorerPlaySelected)
  60. self.ui.btnSelectPlaylist.clicked.connect(self.selectPlaylist)
  61. self.ui.explorerAddToPlaylist.clicked.connect(self.add_to_playlist)
  62. self.ui.explorerRemoveFromPlaylist.clicked.connect(self.remove_from_playlist)
  63. self.ui.explorerPlaylist.trackSelected.connect(self.newTrackSelected)
  64. self.ui.explorerPlaylist.trackDoubleClicked.connect(self.play_playlist)
  65. # Page 5 - settings
  66. self.ui.musicFoldersTable.setColumnHidden(0, 1)
  67. self.ui.musicFoldersAddButton.clicked.connect(self.add_music_folder)
  68. self.ui.musicFoldersRemoveButton.clicked.connect(self.remove_music_folder)
  69. self.populate_music_folders_table()
  70. # vlc frame
  71. self.ui.vlcFrame.trackStarted.connect(self.vlcTrackStarted)
  72. self.ui.vlcFrame.playlistEnded.connect(self.vlcPlaylistEnded)
  73. # Apply settings
  74. if self.settings:
  75. self.ui.vlcFrame.set_volume(int(self.settings['volume']))
  76. if self.settings['muted']:
  77. self.ui.vlcFrame.toggle_muted()
  78. playlist_id = self.settings['playlist']
  79. if playlist_id is not None and playlist_id > 0:
  80. playlist_repo = SessionRepository()
  81. playlist = playlist_repo.get_by_id(playlist_id)
  82. self.loadPlaylist(playlist)
  83. def menu_item_selected(self, e):
  84. self.ui.stack.setCurrentIndex(e.index)
  85. def indexation_started(self):
  86. self.ui.statusbar.setStatusTip("Indexation en cours...")
  87. def indexation_ended(self):
  88. self.ui.statusbar.setStatusTip("Indexation terminée.")
  89. def refresh_explorer_tree(self):
  90. self.ui.explorerTable.populate()
  91. self.ui.explorerLineSearch.clear()
  92. def newTrackSelected(self, track=None):
  93. sender = self.sender()
  94. self.selected_track = None
  95. self.ui.explorerTrackMetaStack.setCurrentIndex(0)
  96. self.ui.explorerTrackPlay.setEnabled(False)
  97. self.ui.explorerAddToPlaylist.setEnabled(False)
  98. self.ui.explorerRemoveFromPlaylist.setEnabled(False)
  99. self.ui.explorerTrackNotepad.setText("")
  100. if not track:
  101. return
  102. self.selected_track = track
  103. # track infos
  104. self.ui.explorerLblTrackTitle.setText(track.title)
  105. self.ui.explorerLblTrackArtist.setText(track.artist)
  106. self.ui.explorerLblTrackAlbum.setText(track.album)
  107. self.ui.explorerLblTrackNumber.setText(
  108. str(track.track_num if track.track_num is not None else "")
  109. )
  110. self.ui.explorerTrackNotepad.textChanged.disconnect(self.explorerTrackNotesChanged)
  111. self.ui.explorerTrackNotepad.setHtml(track.note)
  112. self.ui.explorerTrackNotepad.textChanged.connect(self.explorerTrackNotesChanged)
  113. self.ui.explorerTrackMetaStack.setCurrentIndex(1)
  114. self.ui.explorerTrackPlay.setEnabled(True)
  115. if type(sender) is ExplorerTable:
  116. self.ui.explorerAddToPlaylist.setEnabled(True)
  117. elif type(sender) is PlaylistTable:
  118. self.ui.explorerRemoveFromPlaylist.setEnabled(True)
  119. else:
  120. raise RuntimeError("Unknown sender")
  121. def populate_music_folders_table(self):
  122. music_folders = MusicFolderRepository().get_all()
  123. self.ui.musicFoldersTable.setRowCount(0)
  124. self.ui.musicFoldersTable.setRowCount(len(music_folders))
  125. music_folder_statuses = [
  126. ('Inconnu', 'status_unknown.png'),
  127. ('Valide', 'status_found.png'),
  128. ('Inaccessible', 'status_unavailable.png')
  129. ]
  130. for i, music_folder in enumerate(music_folders):
  131. self.ui.musicFoldersTable.setItem(i, 0, QTableWidgetItem(music_folder.id))
  132. status_lbl, status_pic = music_folder_statuses[music_folder.status]
  133. self.ui.musicFoldersTable.setItem(i, 1, QTableWidgetItem(QIcon(status_pic), status_lbl))
  134. self.ui.musicFoldersTable.setItem(i, 2, QTableWidgetItem(music_folder.path))
  135. def explorerPlaySelected(self):
  136. track = self.ui.explorerTable.selected_track()
  137. if track is None:
  138. return
  139. self.play_track(track)
  140. def play_track(self, track):
  141. logger.info("Start playing: %s" % track)
  142. self.ui.vlcFrame.load_track(track)
  143. self.ui.vlcFrame.play()
  144. def add_music_folder(self):
  145. path = QFileDialog.getExistingDirectory(self, "Sélectionnez le dossier à ajouter")
  146. if not path:
  147. return
  148. path = Path(path)
  149. repo = MusicFolderRepository()
  150. music_folders = repo.get_all()
  151. # for folder in music_folders:
  152. # if path == Path(folder.path):
  153. # QMessageBox.warning(self, "Ajout invalide", "Ce dossier a déjà été ajouté")
  154. # return
  155. #
  156. # if is_subdir_of(path, Path(folder.path)):
  157. # QMessageBox.warning(self, "Ajout invalide", "Ce dossier est contenu dans un dossier existant")
  158. # return
  159. folder = MusicFolder(path=path)
  160. repo.create(folder, True)
  161. self.populate_music_folders_table()
  162. def remove_music_folder(self):
  163. pass
  164. def createOrEditPlaylist(self):
  165. r = DlgPlaylist.edit(self)
  166. def selectPlaylist(self):
  167. playlist = DlgSelectPlaylist.select(self)
  168. if playlist:
  169. self.loadPlaylist(playlist)
  170. def loadPlaylist(self, playlist):
  171. # home page
  172. self.ui.sessionPlaylist.setEnabled(True)
  173. self.ui.frameNotes.setEnabled(True)
  174. self.ui.sessionLblTitle.setText(playlist.name)
  175. self.ui.sessionPlaylist.populate(playlist)
  176. self.ui.frameNotes.ui.sessionNotes.textChanged.disconnect(self.sessionNotesChanged)
  177. self.ui.frameNotes.ui.sessionNotes.setHtml(playlist.notes)
  178. self.ui.frameNotes.ui.sessionNotes.textChanged.connect(self.sessionNotesChanged)
  179. # explorer page
  180. self.ui.explorerPlaylist.setEnabled(True)
  181. self.ui.explorerLblPlaylistTitle.setText(playlist.name)
  182. self.ui.explorerPlaylist.populate(playlist)
  183. self.selected_playlist = playlist
  184. def add_to_playlist(self):
  185. track = self.ui.explorerTable.selected_track()
  186. if track is None:
  187. return
  188. playlist = self.ui.explorerPlaylist.playlist
  189. if playlist is None:
  190. return
  191. track_repo = TrackRepository()
  192. track_repo.add_to_session(
  193. track.id,
  194. playlist.id
  195. )
  196. self.ui.explorerPlaylist.populate(playlist)
  197. self.ui.sessionPlaylist.populate(playlist)
  198. def remove_from_playlist(self):
  199. session_track = self.ui.explorerPlaylist.selected_session_track()
  200. if session_track is None:
  201. return
  202. session_track_repo = SessionTrackRepository()
  203. session_track_repo.delete(session_track, True)
  204. self.ui.explorerPlaylist.populate()
  205. self.ui.sessionPlaylist.populate()
  206. def play_playlist(self, track=None):
  207. playlist = self.selected_playlist
  208. track_repo = TrackRepository()
  209. tracks = track_repo.get_by_session_id(playlist.id)
  210. start_at = tracks.index(track) if track else 0
  211. logger.info("Start playing playlist: %s from index %s" % (playlist, start_at))
  212. self.ui.vlcFrame.load_playlist(playlist, tracks, start_at)
  213. self.ui.vlcFrame.play()
  214. def vlcTrackStarted(self, track):
  215. if self.selected_playlist:
  216. self.ui.explorerPlaylist.set_is_playing(track)
  217. self.ui.sessionPlaylist.set_is_playing(track)
  218. else:
  219. self.ui.explorerPlaylist.set_is_playing(None)
  220. self.ui.sessionPlaylist.set_is_playing(None)
  221. def vlcPlaylistEnded(self):
  222. self.ui.explorerPlaylist.set_is_playing(None)
  223. self.ui.sessionPlaylist.set_is_playing(None)
  224. def sessionNotesChanged(self):
  225. if not self.selected_playlist:
  226. return
  227. notes = self.ui.frameNotes.ui.sessionNotes.toHtml()
  228. session_repo = SessionRepository()
  229. # session = session_repo.get_by_id(self.selected_playlist.id)
  230. self.selected_playlist.notes = notes
  231. session_repo.commit()
  232. def explorerTrackNotesChanged(self):
  233. if not self.selected_track:
  234. return
  235. note = self.ui.explorerTrackNotepad.toHtml()
  236. track_repo = TrackRepository()
  237. self.selected_track.note = note
  238. track_repo.commit()
  239. def explorerSearchChanged(self):
  240. searchText = self.ui.explorerLineSearch.text()
  241. self.ui.explorerTable.filterBySearchText(searchText)
  242. def currentSettings(self):
  243. volume = self.ui.vlcFrame.volume
  244. playlist_id = self.selected_playlist.id if self.selected_playlist else None
  245. muted = self.ui.vlcFrame.is_muted
  246. return {
  247. "playlist": playlist_id,
  248. "volume": volume,
  249. "muted": muted,
  250. }
  251. def __del__(self):
  252. if self.qdb.isOpen():
  253. self.qdb.close()