GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 262694...f858ef )
by Mandeep
01:05
created

MusicPlayer.playback_menu()   B

Complexity

Conditions 1

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 1
1
import os
2
import pkg_resources
3
import sys
4
5
import natsort
6
7
from PyQt5.QtCore import Qt, QFileInfo, QTime, QTimer, QUrl
8
from PyQt5.QtGui import QIcon, QPixmap
9
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer, QMediaPlaylist
10
from PyQt5.QtWidgets import (QAction, QApplication, QDesktopWidget, QDockWidget, QFileDialog,
11
                             QLabel, QListWidget, QListWidgetItem, QMainWindow, QSizePolicy,
12
                             QSlider, QToolBar, QVBoxLayout, QWidget)
13
14
from mosaic import about, configuration, defaults, information, library, metadata
15
16
17
class MusicPlayer(QMainWindow):
18
    """MusicPlayer houses all of elements that directly interact with the main window."""
19
20
    def __init__(self, parent=None):
21
        """Initialize the QMainWindow widget.
22
23
        The window title, window icon, and window size are initialized here as well
24
        as the following widgets: QMediaPlayer, QMediaPlaylist, QMediaContent, QMenuBar,
25
        QToolBar, QLabel, QPixmap, QSlider, QDockWidget, QListWidget, QWidget, and
26
        QVBoxLayout. The connect signals for relavant widgets are also initialized.
27
        """
28
        super(MusicPlayer, self).__init__(parent)
29
        self.setWindowTitle('Mosaic')
30
        window_icon = pkg_resources.resource_filename('mosaic.images', 'icon.png')
31
        self.setWindowIcon(QIcon(window_icon))
32
        self.resize(defaults.Settings().window_size(), defaults.Settings().window_size() + 63)
33
34
        # Initiates Qt objects to be used by MusicPlayer
35
        self.player = QMediaPlayer()
36
        self.playlist = QMediaPlaylist()
37
        self.playlist_location = defaults.Settings().playlist_path()
38
        self.content = QMediaContent()
39
        self.menu = self.menuBar()
40
        self.toolbar = QToolBar()
41
        self.art = QLabel()
42
        self.pixmap = QPixmap()
43
        self.slider = QSlider(Qt.Horizontal)
44
        self.duration_label = QLabel()
45
        self.playlist_dock = QDockWidget('Playlist', self)
46
        self.library_dock = QDockWidget('Media Library', self)
47
        self.playlist_view = QListWidget()
48
        self.library_view = library.MediaLibraryView()
49
        self.library_model = library.MediaLibraryModel()
50
        self.preferences = configuration.PreferencesDialog()
51
        self.widget = QWidget()
52
        self.layout = QVBoxLayout(self.widget)
53
        self.duration = 0
54
        self.playlist_dock_state = None
55
        self.library_dock_state = None
56
57
        # Sets QWidget() as the central widget of the main window
58
        self.setCentralWidget(self.widget)
59
        self.layout.setContentsMargins(0, 0, 0, 0)
60
        self.art.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
61
62
        # Initiates the playlist dock widget and the library dock widget
63
        self.addDockWidget(defaults.Settings().dock_position(), self.playlist_dock)
64
        self.playlist_dock.setWidget(self.playlist_view)
65
        self.playlist_dock.setVisible(defaults.Settings().playlist_on_start())
66
        self.playlist_dock.setFeatures(QDockWidget.DockWidgetClosable)
67
68
        self.addDockWidget(defaults.Settings().dock_position(), self.library_dock)
69
        self.library_dock.setWidget(self.library_view)
70
        self.library_dock.setVisible(defaults.Settings().media_library_on_start())
71
        self.library_dock.setFeatures(QDockWidget.DockWidgetClosable)
72
        self.tabifyDockWidget(self.playlist_dock, self.library_dock)
73
74
        # Sets the range of the playback slider and sets the playback mode as looping
75
        self.slider.setRange(0, self.player.duration() / 1000)
76
        self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
77
78
        # OSX system menu bar causes conflicts with PyQt5 menu bar
79
        if sys.platform == 'darwin':
80
            self.menu.setNativeMenuBar(False)
81
82
        # Initiates Settings in the defaults module to give access to settings.toml
83
        defaults.Settings()
84
85
        # Signals that connect to other methods when they're called
86
        self.player.metaDataChanged.connect(self.display_meta_data)
87
        self.slider.sliderMoved.connect(self.seek)
88
        self.player.durationChanged.connect(self.song_duration)
89
        self.player.positionChanged.connect(self.song_position)
90
        self.player.stateChanged.connect(self.set_state)
91
        self.playlist_view.itemActivated.connect(self.activate_playlist_item)
92
        self.library_view.activated.connect(self.open_media_library)
93
        self.playlist.currentIndexChanged.connect(self.change_index)
94
        self.playlist.mediaInserted.connect(self.initialize_playlist)
95
        self.playlist_dock.visibilityChanged.connect(self.dock_visiblity_change)
96
        self.library_dock.visibilityChanged.connect(self.dock_visiblity_change)
97
        self.preferences.dialog_media_library.media_library_line.textChanged.connect(
98
            self.change_media_library_path)
99
        self.preferences.dialog_view_options.dropdown_box.currentIndexChanged.connect(
100
            self.change_window_size)
101
        self.art.mousePressEvent = self.press_playback
102
103
        # Creating the menu controls, media controls, and window size of the music player
104
        self.menu_controls()
105
        self.media_controls()
106
        self.load_saved_playlist()
107
108
    def menu_controls(self):
109
        """Initiate the menu bar and add it to the QMainWindow widget."""
110
        self.file = self.menu.addMenu('File')
111
        self.edit = self.menu.addMenu('Edit')
112
        self.playback = self.menu.addMenu('Playback')
113
        self.view = self.menu.addMenu('View')
114
        self.help_ = self.menu.addMenu('Help')
115
116
        self.file_menu()
117
        self.edit_menu()
118
        self.playback_menu()
119
        self.view_menu()
120
        self.help_menu()
121
122
    def media_controls(self):
123
        """Create the bottom toolbar and controls used for media playback."""
124
        self.addToolBar(Qt.BottomToolBarArea, self.toolbar)
125
        self.toolbar.setMovable(False)
126
127
        play_icon = pkg_resources.resource_filename('mosaic.images', 'md_play.png')
128
        self.play_action = QAction(QIcon(play_icon), 'Play', self)
129
        self.play_action.triggered.connect(self.player.play)
130
131
        stop_icon = pkg_resources.resource_filename('mosaic.images', 'md_stop.png')
132
        self.stop_action = QAction(QIcon(stop_icon), 'Stop', self)
133
        self.stop_action.triggered.connect(self.player.stop)
134
135
        previous_icon = pkg_resources.resource_filename('mosaic.images', 'md_previous.png')
136
        self.previous_action = QAction(QIcon(previous_icon), 'Previous', self)
137
        self.previous_action.triggered.connect(self.previous)
138
139
        next_icon = pkg_resources.resource_filename('mosaic.images', 'md_next.png')
140
        self.next_action = QAction(QIcon(next_icon), 'Next', self)
141
        self.next_action.triggered.connect(self.playlist.next)
142
143
        repeat_icon = pkg_resources.resource_filename('mosaic.images', 'md_repeat_none.png')
144
        self.repeat_action = QAction(QIcon(repeat_icon), 'Repeat', self)
145
        self.repeat_action.triggered.connect(self.repeat_song)
146
147
        self.toolbar.addAction(self.play_action)
148
        self.toolbar.addAction(self.stop_action)
149
        self.toolbar.addAction(self.previous_action)
150
        self.toolbar.addAction(self.next_action)
151
        self.toolbar.addAction(self.repeat_action)
152
        self.toolbar.addWidget(self.slider)
153
        self.toolbar.addWidget(self.duration_label)
154
155
    def file_menu(self):
156
        """Add a file menu to the menu bar.
157
158
        The file menu houses the Open File, Open Multiple Files, Open Playlist,
159
        Open Directory, and Exit Application menu items.
160
        """
161
        self.open_action = QAction('Open File', self)
162
        self.open_action.setShortcut('CTRL+O')
163
        self.open_action.triggered.connect(self.open_file)
164
165
        self.open_multiple_files_action = QAction('Open Multiple Files', self)
166
        self.open_multiple_files_action.setShortcut('CTRL+SHIFT+O')
167
        self.open_multiple_files_action.triggered.connect(self.open_multiple_files)
168
169
        self.open_playlist_action = QAction('Open Playlist', self)
170
        self.open_playlist_action.setShortcut('CTRL+P')
171
        self.open_playlist_action.triggered.connect(self.open_playlist)
172
173
        self.open_directory_action = QAction('Open Directory', self)
174
        self.open_directory_action.setShortcut('CTRL+D')
175
        self.open_directory_action.triggered.connect(self.open_directory)
176
177
        self.exit_action = QAction('Quit', self)
178
        self.exit_action.setShortcut('CTRL+Q')
179
        self.exit_action.triggered.connect(self.closeEvent)
180
181
        self.file.addAction(self.open_action)
182
        self.file.addAction(self.open_multiple_files_action)
183
        self.file.addAction(self.open_playlist_action)
184
        self.file.addAction(self.open_directory_action)
185
        self.file.addSeparator()
186
        self.file.addAction(self.exit_action)
187
188
    def edit_menu(self):
189
        """Add an edit menu to the menu bar.
190
191
        The edit menu houses the preferences item that opens a preferences dialog
192
        that allows the user to customize features of the music player.
193
        """
194
        self.preferences_action = QAction('Preferences', self)
195
        self.preferences_action.setShortcut('CTRL+SHIFT+P')
196
        self.preferences_action.triggered.connect(lambda: self.preferences.exec_())
197
198
        self.edit.addAction(self.preferences_action)
199
200
    def playback_menu(self):
201
        """Add a playback menu to the menu bar.
202
203
        The playback menu houses
204
        """
205
        self.play_playback_action = QAction('Play', self)
206
        self.play_playback_action.setShortcut('P')
207
        self.play_playback_action.triggered.connect(self.player.play)
208
209
        self.stop_playback_action = QAction('Stop', self)
210
        self.stop_playback_action.setShortcut('S')
211
        self.stop_playback_action.triggered.connect(self.player.stop)
212
213
        self.previous_playback_action = QAction('Previous', self)
214
        self.previous_playback_action.setShortcut('B')
215
        self.previous_playback_action.triggered.connect(self.previous)
216
217
        self.next_playback_action = QAction('Next', self)
218
        self.next_playback_action.setShortcut('N')
219
        self.next_playback_action.triggered.connect(self.playlist.next)
220
221
        self.playback.addAction(self.play_playback_action)
222
        self.playback.addAction(self.stop_playback_action)
223
        self.playback.addAction(self.previous_playback_action)
224
        self.playback.addAction(self.next_playback_action)
225
226
    def view_menu(self):
227
        """Add a view menu to the menu bar.
228
229
        The view menu houses the Playlist, Media Library, Minimalist View, and Media
230
        Information menu items. The Playlist item toggles the playlist dock into and
231
        out of view. The Media Library items toggles the media library dock into and
232
        out of view. The Minimalist View item resizes the window and shows only the
233
        menu bar and player controls. The Media Information item opens a dialog that
234
        shows information relevant to the currently playing song.
235
        """
236
        self.dock_action = self.playlist_dock.toggleViewAction()
237
        self.dock_action.setShortcut('CTRL+ALT+P')
238
239
        self.library_dock_action = self.library_dock.toggleViewAction()
240
        self.library_dock_action.setShortcut('CTRL+ALT+L')
241
242
        self.minimalist_view_action = QAction('Minimalist View', self)
243
        self.minimalist_view_action.setShortcut('CTRL+ALT+M')
244
        self.minimalist_view_action.setCheckable(True)
245
        self.minimalist_view_action.triggered.connect(self.minimalist_view)
246
247
        self.view_media_info_action = QAction('Media Information', self)
248
        self.view_media_info_action.setShortcut('CTRL+SHIFT+M')
249
        self.view_media_info_action.triggered.connect(self.media_information_dialog)
250
251
        self.view.addAction(self.dock_action)
252
        self.view.addAction(self.library_dock_action)
253
        self.view.addSeparator()
254
        self.view.addAction(self.minimalist_view_action)
255
        self.view.addSeparator()
256
        self.view.addAction(self.view_media_info_action)
257
258
    def help_menu(self):
259
        """Add a help menu to the menu bar.
260
261
        The help menu houses the about dialog that shows the user information
262
        related to the application.
263
        """
264
        self.about_action = QAction('About', self)
265
        self.about_action.setShortcut('CTRL+H')
266
        self.about_action.triggered.connect(lambda: about.AboutDialog().exec_())
267
268
        self.help_.addAction(self.about_action)
269
270
    def open_file(self):
271
        """Open the selected file and add it to a new playlist."""
272
        filename, ok = QFileDialog.getOpenFileName(
273
            self, 'Open File', '', 'Audio (*.mp3 *.flac)', '',
274
            QFileDialog.ReadOnly)
275
276
        if ok:
277
            file_info = QFileInfo(filename).fileName()
278
            playlist_item = QListWidgetItem(file_info)
279
            self.playlist.clear()
280
            self.playlist_view.clear()
281
            self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(filename)))
282
            self.player.setPlaylist(self.playlist)
283
            playlist_item.setToolTip(file_info)
284
            self.playlist_view.addItem(playlist_item)
285
            self.playlist_view.setCurrentRow(0)
286
            self.player.play()
287
288
    def open_multiple_files(self):
289
        """Open the selected files and add them to a new playlist."""
290
        filenames, ok = QFileDialog.getOpenFileNames(
291
            self, 'Open Multiple Files', '',
292
            'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly)
293
294
        if ok:
295
            self.playlist.clear()
296
            self.playlist_view.clear()
297
            for file in natsort.natsorted(filenames, alg=natsort.ns.PATH):
298
                file_info = QFileInfo(file).fileName()
299
                playlist_item = QListWidgetItem(file_info)
300
                self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file)))
301
                self.player.setPlaylist(self.playlist)
302
                playlist_item.setToolTip(file_info)
303
                self.playlist_view.addItem(playlist_item)
304
                self.playlist_view.setCurrentRow(0)
305
                self.player.play()
306
307
    def open_playlist(self):
308
        """Load an M3U or PLS file into a new playlist."""
309
        playlist, ok = QFileDialog.getOpenFileName(
310
            self, 'Open Playlist', '',
311
            'Playlist (*.m3u *.pls)', '', QFileDialog.ReadOnly)
312
        if ok:
313
            playlist = QUrl.fromLocalFile(playlist)
314
            self.playlist.clear()
315
            self.playlist_view.clear()
316
            self.playlist.load(playlist)
317
            self.player.setPlaylist(self.playlist)
318
319
            for song_index in range(self.playlist.mediaCount()+1):
320
                file_info = self.playlist.media(song_index).canonicalUrl().fileName()
321
                playlist_item = QListWidgetItem(file_info)
322
                playlist_item.setToolTip(file_info)
323
                self.playlist_view.addItem(playlist_item)
324
325
            self.playlist_view.setCurrentRow(0)
326
            self.player.play()
327
328
    def load_saved_playlist(self):
329
        """Load the saved playlist if user setting permits."""
330
        saved_playlist = "{}/.m3u" .format(self.playlist_location)
331
        if os.path.exists(saved_playlist):
332
            playlist = QUrl().fromLocalFile(saved_playlist)
333
            self.playlist.load(playlist)
334
            self.player.setPlaylist(self.playlist)
335
336
            for song_index in range(self.playlist.mediaCount()+1):
337
                file_info = self.playlist.media(song_index).canonicalUrl().fileName()
338
                playlist_item = QListWidgetItem(file_info)
339
                playlist_item.setToolTip(file_info)
340
                self.playlist_view.addItem(playlist_item)
341
342
            self.playlist_view.setCurrentRow(0)
343
344
    def open_directory(self):
345
        """Open the selected directory and add the files within to an empty playlist."""
346
        directory = QFileDialog.getExistingDirectory(
347
            self, 'Open Directory', '', QFileDialog.ReadOnly)
348
349
        if directory:
350
            self.playlist.clear()
351
            self.playlist_view.clear()
352
            for dirpath, __, files in os.walk(directory):
353
                for filename in natsort.natsorted(files, alg=natsort.ns.PATH):
354
                    file = os.path.join(dirpath, filename)
355
                    if filename.endswith(('mp3', 'flac')):
356
                        self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file)))
357
                        playlist_item = QListWidgetItem(filename)
358
                        playlist_item.setToolTip(filename)
359
                        self.playlist_view.addItem(playlist_item)
360
361
            self.player.setPlaylist(self.playlist)
362
            self.playlist_view.setCurrentRow(0)
363
            self.player.play()
364
365
    def open_media_library(self, index):
366
        """Open a directory or file from the media library into an empty playlist."""
367
        self.playlist.clear()
368
        self.playlist_view.clear()
369
370
        if self.library_model.fileName(index).endswith(('mp3', 'flac')):
371
            self.playlist.addMedia(
372
                QMediaContent(QUrl().fromLocalFile(self.library_model.filePath(index))))
373
            self.playlist_view.addItem(self.library_model.fileName(index))
374
375
        elif self.library_model.isDir(index):
376
            directory = self.library_model.filePath(index)
377
            for dirpath, __, files in os.walk(directory):
378
                for filename in natsort.natsorted(files, alg=natsort.ns.PATH):
379
                    file = os.path.join(dirpath, filename)
380
                    if filename.endswith(('mp3', 'flac')):
381
                        self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file)))
382
                        playlist_item = QListWidgetItem(filename)
383
                        playlist_item.setToolTip(filename)
384
                        self.playlist_view.addItem(playlist_item)
385
386
        self.player.setPlaylist(self.playlist)
387
        self.player.play()
388
389
    def display_meta_data(self):
390
        """Display the current song's metadata in the main window.
391
392
        If the current song contains metadata, its cover art is extracted and shown in
393
        the main window while the track number, artist, album, and track title are shown
394
        in the window title.
395
        """
396
        if self.player.isMetaDataAvailable():
397
            file_path = self.player.currentMedia().canonicalUrl().toLocalFile()
398
            (album, artist, title, track_number, *__, artwork) = metadata.metadata(file_path)
399
400
            try:
401
                self.pixmap.loadFromData(artwork)
402
            except TypeError:
403
                self.pixmap = QPixmap(artwork)
404
405
            meta_data = '{} - {} - {} - {}' .format(
406
                    track_number, artist, album, title)
407
            self.setWindowTitle(meta_data)
408
409
            self.art.setScaledContents(True)
410
            self.art.setPixmap(self.pixmap)
411
412
            self.layout.addWidget(self.art)
413
414
    def initialize_playlist(self, start):
415
        """Display playlist and reset playback mode when media inserted into playlist."""
416
        if start == 0:
417
            if self.library_dock.isVisible():
418
                self.playlist_dock.setVisible(True)
419
                self.playlist_dock.show()
420
                self.playlist_dock.raise_()
421
422
            if self.playlist.playbackMode() != QMediaPlaylist.Sequential:
423
                self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
424
                repeat_icon = pkg_resources.resource_filename('mosaic.images', 'md_repeat_none.png')
425
                self.repeat_action.setIcon(QIcon(repeat_icon))
426
427
    def press_playback(self, event):
428
        """Change the playback of the player on cover art mouse event.
429
430
        When the cover art is clicked, the player will play the media if the player is
431
        either paused or stopped. If the media is playing, the media is set
432
        to pause.
433
        """
434
        if event.button() == 1 and configuration.Playback().cover_art_playback.isChecked():
435
            if (self.player.state() == QMediaPlayer.StoppedState or
436
                    self.player.state() == QMediaPlayer.PausedState):
437
                self.player.play()
438
            elif self.player.state() == QMediaPlayer.PlayingState:
439
                self.player.pause()
440
441
    def seek(self, seconds):
442
        """Set the position of the song to the position dragged to by the user."""
443
        self.player.setPosition(seconds * 1000)
444
445
    def song_duration(self, duration):
446
        """Set the slider to the duration of the currently played media."""
447
        duration /= 1000
448
        self.duration = duration
449
        self.slider.setMaximum(duration)
450
451
    def song_position(self, progress):
452
        """Move the horizontal slider in sync with the duration of the song.
453
454
        The progress is relayed to update_duration() in order
455
        to display the time label next to the slider.
456
        """
457
        progress /= 1000
458
459
        if not self.slider.isSliderDown():
460
            self.slider.setValue(progress)
461
462
        self.update_duration(progress)
463
464
    def update_duration(self, current_duration):
465
        """Calculate the time played and the length of the song.
466
467
        Both of these times are sent to duration_label() in order to display the
468
        times on the toolbar.
469
        """
470
        duration = self.duration
471
472
        if current_duration or duration:
473
            time_played = QTime((current_duration / 3600) % 60, (current_duration / 60) % 60,
474
                                (current_duration % 60), (current_duration * 1000) % 1000)
475
            song_length = QTime((duration / 3600) % 60, (duration / 60) % 60, (duration % 60),
476
                                (duration * 1000) % 1000)
477
478
            if duration > 3600:
479
                time_format = "hh:mm:ss"
480
            else:
481
                time_format = "mm:ss"
482
483
            time_display = "{} / {}" .format(time_played.toString(time_format),
484
                                             song_length.toString(time_format))
485
        else:
486
            time_display = ""
487
488
        self.duration_label.setText(time_display)
489
490
    def set_state(self, state):
491
        """Change the icon in the toolbar in relation to the state of the player.
492
493
        The play icon changes to the pause icon when a song is playing and
494
        the pause icon changes back to the play icon when either paused or
495
        stopped.
496
        """
497
        if self.player.state() == QMediaPlayer.PlayingState:
498
            pause_icon = pkg_resources.resource_filename('mosaic.images', 'md_pause.png')
499
            self.play_action.setIcon(QIcon(pause_icon))
500
            self.play_action.triggered.connect(self.player.pause)
501
502
        elif (self.player.state() == QMediaPlayer.PausedState or
503
              self.player.state() == QMediaPlayer.StoppedState):
504
            self.play_action.triggered.connect(self.player.play)
505
            play_icon = pkg_resources.resource_filename('mosaic.images', 'md_play.png')
506
            self.play_action.setIcon(QIcon(play_icon))
507
508
    def previous(self):
509
        """Move to the previous song in the playlist.
510
511
        Moves to the previous song in the playlist if the current song is less
512
        than five seconds in. Otherwise, restarts the current song.
513
        """
514
        if self.player.position() <= 5000:
515
            self.playlist.previous()
516
        else:
517
            self.player.setPosition(0)
518
519
    def repeat_song(self):
520
        """Set the current media to repeat and change the repeat icon accordingly.
521
522
        There are four playback modes: repeat none, repeat all, repeat once, and shuffle.
523
        Clicking the repeat button cycles through each playback mode.
524
        """
525
        if self.playlist.playbackMode() == QMediaPlaylist.Sequential:
526
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
527
            repeat_on_icon = pkg_resources.resource_filename('mosaic.images', 'md_repeat_all.png')
528
            self.repeat_action.setIcon(QIcon(repeat_on_icon))
529
530
        elif self.playlist.playbackMode() == QMediaPlaylist.Loop:
531
            self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop)
532
            repeat_on_icon = pkg_resources.resource_filename('mosaic.images', 'md_repeat_once.png')
533
            self.repeat_action.setIcon(QIcon(repeat_on_icon))
534
535
        elif self.playlist.playbackMode() == QMediaPlaylist.CurrentItemInLoop:
536
            self.playlist.setPlaybackMode(QMediaPlaylist.Random)
537
            repeat_icon = pkg_resources.resource_filename('mosaic.images', 'md_shuffle.png')
538
            self.repeat_action.setIcon(QIcon(repeat_icon))
539
540
        elif self.playlist.playbackMode() == QMediaPlaylist.Random:
541
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
542
            repeat_icon = pkg_resources.resource_filename('mosaic.images', 'md_repeat_none.png')
543
            self.repeat_action.setIcon(QIcon(repeat_icon))
544
545
    def activate_playlist_item(self, item):
546
        """Set the active media to the playlist item dobule-clicked on by the user."""
547
        current_index = self.playlist_view.row(item)
548
        if self.playlist.currentIndex() != current_index:
549
            self.playlist.setCurrentIndex(current_index)
550
551
        if self.player.state() != QMediaPlayer.PlayingState:
552
            self.player.play()
553
554
    def change_index(self, row):
555
        """Highlight the row in the playlist of the active media."""
556
        self.playlist_view.setCurrentRow(row)
557
558
    def minimalist_view(self):
559
        """Resize the window to only show the menu bar and audio controls."""
560
        if self.minimalist_view_action.isChecked():
561
562
            if self.playlist_dock.isVisible():
563
                self.playlist_dock_state = True
564
            if self.library_dock.isVisible():
565
                self.library_dock_state = True
566
567
            self.library_dock.close()
568
            self.playlist_dock.close()
569
570
            QTimer.singleShot(10, lambda: self.resize(500, 0))
571
572
        else:
573
            self.resize(defaults.Settings().window_size(), defaults.Settings().window_size() + 63)
574
575
            if self.library_dock_state:
576
                self.library_dock.setVisible(True)
577
578
            if self.playlist_dock_state:
579
                self.playlist_dock.setVisible(True)
580
581
    def dock_visiblity_change(self, visible):
582
        """Change the size of the main window when the docks are toggled."""
583
        if visible and self.playlist_dock.isVisible() and not self.library_dock.isVisible():
584
            self.resize(defaults.Settings().window_size() + self.playlist_dock.width() + 6,
585
                        self.height())
586
587
        elif visible and not self.playlist_dock.isVisible() and self.library_dock.isVisible():
588
            self.resize(defaults.Settings().window_size() + self.library_dock.width() + 6,
589
                        self.height())
590
591
        elif visible and self.playlist_dock.isVisible() and self.library_dock.isVisible():
592
            self.resize(defaults.Settings().window_size() + self.library_dock.width() + 6,
593
                        self.height())
594
595
        elif (not visible and not self.playlist_dock.isVisible() and not
596
                self.library_dock.isVisible()):
597
            self.resize(defaults.Settings().window_size(), defaults.Settings().window_size() + 63)
598
599
    def media_information_dialog(self):
600
        """Show a dialog of the current song's metadata."""
601
        if self.player.isMetaDataAvailable():
602
            file_path = self.player.currentMedia().canonicalUrl().toLocalFile()
603
        else:
604
            file_path = None
605
        dialog = information.InformationDialog(file_path)
606
        dialog.exec_()
607
608
    def change_window_size(self):
609
        """Change the window size of the music player."""
610
        self.playlist_dock.close()
611
        self.library_dock.close()
612
        self.resize(defaults.Settings().window_size(), defaults.Settings().window_size() + 63)
613
614
    def change_media_library_path(self, path):
615
        """Change the media library path to the new path selected in the preferences dialog."""
616
        self.library_model.setRootPath(path)
617
        self.library_view.setModel(self.library_model)
618
        self.library_view.setRootIndex(self.library_model.index(path))
619
620
    def closeEvent(self, event):
621
        """Override the PyQt close event in order to handle save playlist on close."""
622
        playlist = "{}/.m3u" .format(self.playlist_location)
623
        if defaults.Settings().save_playlist_on_close():
624
            self.playlist.save(QUrl().fromLocalFile(playlist), "m3u")
625
        else:
626
            if os.path.exists(playlist):
627
                os.remove(playlist) 
628
        QApplication.quit()
629
630
631
def main():
632
    """Create an instance of the music player and use QApplication to show the GUI.
633
634
    QDesktopWidget() is used to move the application to the center of the user's screen.
635
    """
636
    application = QApplication(sys.argv)
637
    window = MusicPlayer()
638
    desktop = QDesktopWidget().availableGeometry()
639
    width = (desktop.width() - window.width()) / 2
640
    height = (desktop.height() - window.height()) / 2
641
    window.show()
642
    window.move(width, height)
643
    sys.exit(application.exec_())
644