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