Oct-29-2021, 06:49 PM
Added internet radio with some predefined stations to the program. Probably going to re-write the code later on and try to make it a little cleaner.
#! /usr/bin/env python3.9 # PtQt5 Music Player # Copyright (C) 2021 menator01 # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. import os import sys from mutagen.mp3 import MP3 from PyQt5.QtCore import Qt, QUrl, QDir from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QGridLayout, \ QFrame, QGraphicsDropShadowEffect, QGraphicsView, QGraphicsScene, QLabel, \ QPushButton, QHBoxLayout, QStyle, QListWidget, QFileDialog, QSlider, QVBoxLayout, QDial, \ QRadioButton from PyQt5.QtGui import QGradient, QFont, QColor, QCursor, QIcon, QPixmap from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist, \ QMediaMetaData class Window(QMainWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setWindowTitle('PyQt5 Music Player') # Create some variables self.url = QUrl() self.player = QMediaPlayer() self.playlist = QMediaPlaylist(self.player) self.player.setPlaylist(self.playlist) # Create the status and track labels common_style = ''' font-weight: bold; font-size: 10pt; ''' self.status = QLabel('Status: Now Stopped') self.status.setStyleSheet(common_style) self.status.setFrameShape(QFrame.Box) self.status.setFrameShadow(QFrame.Sunken) self.track = QLabel('Track: ') self.track.setStyleSheet(common_style) self.track.setFrameShape(QFrame.Box) self.track.setFrameShadow(QFrame.Sunken) # Labels for the track information artist = QLabel('Artist:') artist.setStyleSheet(common_style) album = QLabel('Album:') album.setStyleSheet(common_style) track = QLabel('Track:') track.setStyleSheet(common_style) released = QLabel('Album Release:') released.setStyleSheet(common_style) genre = QLabel('Genre:') genre.setStyleSheet(common_style) self.artist = QLabel() self.artist.setStyleSheet(common_style) self.album_title = QLabel() self.album_title.setStyleSheet(common_style) self.track_title = QLabel() self.track_title.setStyleSheet(common_style) self.released = QLabel() self.released.setStyleSheet(common_style) self.genre = QLabel() self.genre.setStyleSheet(common_style) self.art = QLabel() self.art.setContentsMargins(5, 170, 5, 5) # Timer Label self._timer = QLabel('Duration: 00:00:00 / 00:00:00') self.radio = QRadioButton('Internet Radio') self.local = QRadioButton('Local Files') self.local.setChecked(True) # Define and create the listbox self.musiclist = QListWidget() self.musiclist.setFrameShape(QFrame.Box) self.musiclist.setFrameShadow(QFrame.Sunken) # Create some containers btn_box = QHBoxLayout() btn_box2 = QHBoxLayout() slider_box = QGridLayout() slider_box.setContentsMargins(5,80,5,5) dial_box = QVBoxLayout() container = QGridLayout() info_container = QGridLayout() info_container.setSpacing(8) frame = QFrame() frame.setMinimumWidth(390) frame.setFrameShape(QFrame.Box) frame.setFrameShadow(QFrame.Sunken) frame.setLayout(info_container) img_frame = QFrame() img_frame.setMinimumHeight(100) # Create slider frame and control slider_frame = QFrame() slider_frame.setFrameShape(QFrame.Box) slider_frame.setFrameShadow(QFrame.Sunken) slider_frame.setMinimumHeight(30) slider_frame.setLayout(slider_box) self.slider = QSlider(Qt.Horizontal) slider_box.addWidget(self._timer, 0, 0, 1, 1) slider_box.addWidget(self.radio, 0, 2, 1, 1) slider_box.addWidget(self.local, 0, 3, 1, 1) slider_box.addWidget(self.slider, 1, 0, 1, 4) self.radio.toggled.connect(self.music_type) # slider_box.addWidget(self.radio) # slider_box.addWidget(self.local) # slider_box.addWidget(self._timer) # slider_box.addWidget(self.slider) # Create volume frame and control dial_frame = QFrame() dial_frame.setFrameShape(QFrame.Box) dial_frame.setFrameShadow(QFrame.Sunken) dial_frame.setLayout(dial_box) self.dial = QDial() self.dial.setRange(0, 100) self.dial.setNotchesVisible(True) self.dial.setSliderPosition(70) self.dial.setValue(70) self.dial.setMinimumWidth(200) self.dial.setMaximumWidth(200) self.dial.setMinimumHeight(100) dial_box.addWidget(QLabel('Volume')) dial_box.addWidget(self.dial) # Used to update various aspects of gui self.playlist.currentIndexChanged.connect(self.update) self.dial.valueChanged.connect(self._volume, self.dial.value()) self.player.metaDataChanged.connect(self.meta_data) self.musiclist.itemDoubleClicked.connect(self._doubleclick) self.player.positionChanged.connect(self.track_position) self.player.durationChanged.connect(self.duration) self.slider.valueChanged.connect(self.timer) # Add track information to the info container info_container.addWidget(artist, 0, 0, 1, 1) info_container.addWidget(self.artist, 0, 1, 1, 1) info_container.addWidget(album, 1, 0, 1, 1) info_container.addWidget(self.album_title, 1, 1, 1, 1) info_container.addWidget(track, 2, 0, 1, 1) info_container.addWidget(self.track_title, 2, 1, 1, 1) info_container.addWidget(released, 3, 0, 1, 1) info_container.addWidget(self.released, 3, 1, 1, 1) info_container.addWidget(genre, 4, 0, 1, 1) info_container.addWidget(self.genre, 4, 1, 1, 1) info_container.addWidget(self.art, 5, 0, 1, 2) # Create the control buttons & button styles btn_style = '''QPushButton{background-color: skyblue;} QPushButton:hover{background-color: lightskyblue; color: dodgerblue; \ font-weight: bold;}''' # Create buttons for getting audio files and clearing playlist self.file_btn = QPushButton('Get Audio') self.file_btn.released.connect(self._files) self.file_btn.setCursor(Qt.PointingHandCursor) self.file_btn.setStyleSheet(btn_style) self.file_btn.setMaximumWidth(100) clear_btn = QPushButton('Clear List') clear_btn.released.connect(self._clear) clear_btn.setCursor(Qt.PointingHandCursor) clear_btn.setStyleSheet(btn_style) clear_btn.setMaximumWidth(100) # Create & style the control buttons self.play_btn = QPushButton('Play') self.play_btn.released.connect(self._state) self.play_btn.setCursor(Qt.PointingHandCursor) self.play_btn.setStyleSheet(btn_style) self.prev_btn = QPushButton('Prev') self.prev_btn.released.connect(self._prev) self.prev_btn.setCursor(Qt.PointingHandCursor) self.prev_btn.setStyleSheet(btn_style) self.next_btn = QPushButton('Next') self.next_btn.released.connect(self._next) self.next_btn.setCursor(Qt.PointingHandCursor) self.next_btn.setStyleSheet(btn_style) self.stop_btn = QPushButton('Stop') self.stop_btn.released.connect(self._stop) self.stop_btn.setCursor(Qt.PointingHandCursor) self.stop_btn.setStyleSheet(btn_style) self.exit_btn = QPushButton('Exit') self.exit_btn.released.connect(sys.exit) self.exit_btn.setCursor(Qt.PointingHandCursor) self.exit_btn.setStyleSheet('QPushButton{background-color: firebrick;} \ QPushButton:hover{background-color: red; color: white; \ font-weight: bold;}') # Add the buttons to layout btn_box.addWidget(self.file_btn) btn_box.addWidget(clear_btn) btn_box2.addWidget(self.play_btn) btn_box2.addWidget(self.prev_btn) btn_box2.addWidget(self.next_btn) btn_box2.addWidget(self.stop_btn) btn_box2.addWidget(self.exit_btn) # Add layouts to container layout container.addWidget(self._header_footer(100, 100, 40, 'PyQt5 Music Player'), 0, 0, 1, 3) container.addWidget(self.status, 1, 0, 1, 1) container.addWidget(self.track, 1, 1, 1, 1) container.addLayout(btn_box, 1, 2, 1, 1) container.addWidget(frame, 2, 0, 2, 1) container.addWidget(self.musiclist, 2, 1, 1, 2) container.addLayout(btn_box2, 3, 1, 1, 2) container.addWidget(slider_frame, 4, 0, 1, 2) container.addWidget(dial_frame, 4, 2, 1, 1) container.addWidget(self._header_footer(40, 40, 10, 'my-python.org - 10/16/2021'), 5, 0, 1, 3) # Create and set the layout to container widget = QWidget() widget.setLayout(container) self.setCentralWidget(widget) def music_type(self): if self.radio.isChecked(): self.file_btn.setEnabled(False) self.status.setText('Status: Now Playing') stations = [ 'http://us4.internet-radio.com:8258/stream', 'http://us5.internet-radio.com:8267/stream', 'https://us9.maindigitalstream.com/ssl/bigrock991', 'https://playerservices.streamtheworld.com/api/livestream-redirect/KGFKAM.mp3', 'https://cob-ais.leanstream.co/CFJBFM-MP3', 'http://37.59.195.28:8045', 'https://playerservices.streamtheworld.com/api/livestream-redirect/WIRLAM.mp3', 'https://ais-sa2.cdnstream1.com/2383_128' ] for station in stations: self.playlist.addMedia(QMediaContent(QUrl(station))) self.player.play() self.play_btn.setText('Pause') self.musiclist.addItem(station) self.musiclist.setCurrentRow(0) else: self.file_btn.setEnabled(True) # Volume control def _volume(self, val=70): self.player.setVolume(val) # Sets position of slider def track_position(self, position): self.slider.setValue(position) # Duration of the track playing def duration(self, duration): self.slider.setRange(0, duration) # Updates duration of track playing def timer(self): total_milliseconds = self.player.duration() total_seconds, total_milliseconds = divmod(total_milliseconds,1000) total_minutes, total_seconds = divmod(total_seconds,60) total_hours, total_minutes = divmod(total_minutes, 60) elapsed_milliseconds = self.slider.value() elapsed_seconds, elapsed_milliseconds = divmod(elapsed_milliseconds,1000) elapsed_minutes, elapsed_seconds = divmod(elapsed_seconds, 60) elapsed_hours, elapsed_minutes = divmod(elapsed_minutes, 60) self._timer.setText(f'Duration: {elapsed_hours:02d}:{elapsed_minutes:02d}:{elapsed_seconds:02d} / {total_hours:02d}:{total_minutes:02d}:{total_seconds:02d}') # Shorten text that may be too long def _truncate(self, text, length=25): if text: if len(text) <= length: return text else: return f"{' '.join(text[:length+1].split(' ')[0:-1])} ...." # Get music metadata def meta_data(self): if self.player.isMetaDataAvailable(): if self.player.metaData(QMediaMetaData.AlbumArtist): self.artist.setText(self.player.metaData(QMediaMetaData.AlbumArtist)) if self.player.metaData(QMediaMetaData.AlbumTitle): self.album_title.setText(self._truncate(self.player.metaData(QMediaMetaData.AlbumTitle))) if self.player.metaData(QMediaMetaData.Title): self.track_title.setText(self._truncate(self.player.metaData(QMediaMetaData.Title))) if self.player.metaData(QMediaMetaData.Year): self.released.setText(f'{self.player.metaData(QMediaMetaData.Year)}') if self.player.metaData(QMediaMetaData.Genre): self.genre.setText(self.player.metaData(QMediaMetaData.Genre)) if self.player.metaData(QMediaMetaData.Title): self.track.setText(f'Track: {self._truncate(self.player.metaData(QMediaMetaData.Title),20)}') if self.player.metaData(QMediaMetaData.CoverArtImage): pixmap = QPixmap(self.player.metaData(QMediaMetaData.CoverArtImage)) pixmap = pixmap.scaled(int(pixmap.width()/3), int(pixmap.height()/3)) self.art.setPixmap(pixmap) self.art.setContentsMargins(0,32 , 0, 5) # Create the header def _header_footer(self, minheight, maxheight, fontsize, text): shadow = QGraphicsDropShadowEffect() shadow.setBlurRadius(3) shadow.setOffset(3, 3) scene = QGraphicsScene() view = QGraphicsView() view.setMinimumSize(800, minheight) view.setMaximumHeight(maxheight) view.setScene(scene) gradient = QGradient(QGradient.RichMetal) scene.setBackgroundBrush(gradient) font = QFont('comic sans ms', fontsize, QFont.Bold) text = scene.addText(text) text.setDefaultTextColor(QColor(250,250,250)) text.setFont(font) text.setGraphicsEffect(shadow) return view # Method for double clicking a track and play def _doubleclick(self): self.playlist.setCurrentIndex(self.musiclist.currentRow()) self.player.play() # Method for clearing the playlist and musiclist def _clear(self): self.player.stop() self.musiclist.clear() self.playlist.clear() self.play_btn.setText('Play') self.status.setText('Status: Now Stopped') self.track.setText('Track: ') self.radio.setChecked(False) self.local.setChecked(True) pixmap = QPixmap() self.art.setPixmap(pixmap) self.dial.setSliderPosition(70) self.dial.setValue(70) # Method for adding tracks to the playlist and musiclist def _files(self): files = QFileDialog.getOpenFileNames(None, 'Get Audio Files', filter='Audio Files (*.mp3 *.ogg *.wav)') for file in files[0]: self.playlist.addMedia(QMediaContent(self.url.fromLocalFile(file))) try: track = MP3(file) self.musiclist.addItem(str(track['TIT2'])) except: track = self._truncate(file.rpartition('/')[2].rpartition('.')[0]) self.musiclist.addItem(track) self.musiclist.setCurrentRow(0) self.playlist.setCurrentIndex(0) # Methods for the control buttons def _prev(self): if self.playlist.previousIndex() == -1: self.playlist.setCurrentIndex(self.playlist.mediaCount()-1) else: self.playlist.previous() def _next(self): self.playlist.next() if self.playlist.currentIndex() == -1: self.playlist.setCurrentIndex(0) self.player.play() def _stop(self): self.player.stop() self.play_btn.setText('Play') self.playlist.setCurrentIndex(0) self.musiclist.setCurrentRow(0) self.status.setText('Status: Now Stopped') def _state(self): if self.playlist.mediaCount() > 0: if self.player.state() != QMediaPlayer.PlayingState: self.play_btn.setText('Pause') self.status.setText('Status: Now Playing') self.player.play() else: self.play_btn.setText('Play') self.player.pause() self.status.setText('Status: Now Paused') else: pass # Method for updating the listbox when the playlist updates def update(self): self.musiclist.setCurrentRow(self.playlist.currentIndex()) if self.playlist.currentIndex() < 0: self.musiclist.setCurrentRow(0) self.playlist.setCurrentIndex(0) def main(): app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec()) if __name__ == '__main__': main()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags