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 ( 83dd25...ed70cc )
by Mandeep
37s
created

MusicPlayer.open_multiple_files()   A

Complexity

Conditions 3

Size

Total Lines 16

Duplication

Lines 12
Ratio 75 %

Importance

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