Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
PyQt5 Music Player
#11
This is how it could be done.

from functools import partial
import os
import sys
import mutagen
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
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')
 
        # Define and create the listbox
        self.musiclist = QListWidget()
        self.musiclist.setFrameShape(QFrame.Box)
        self.musiclist.setFrameShadow(QFrame.Sunken)
        self.musiclist.setStyleSheet('background-color: snow;')
 
        # Create some containers
        btn_box = QHBoxLayout()
        btn_box2 = QHBoxLayout()
        slider_box = QVBoxLayout()
        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)
        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
        file_btn = QPushButton('Get Audio')
        file_btn.released.connect(self._files)
        file_btn.setCursor(Qt.PointingHandCursor)
        file_btn.setStyleSheet(btn_style)
        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(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 the widget and set layout to container
        widget = QWidget()
        widget.setLayout(container)
        self.setCentralWidget(widget)
 
    # 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 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():
            #print(self.player.availableMetaData())
            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: ')
        self.track.setText('Track: ')
        self.artist.setText('Artist: ')
        self.album_title.setText('Album: ')
        self.track_title.setText('Track: ')
        self.released.setText('Released: ')
        self.genre.setText('Genre: ')
        self.art.setContentsMargins(5, 170, 5, 50)
        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 = mutagen.File(file)
                self.musiclist.addItem(str(track['TIT2']))
            except:
                track = self._truncate(file.rpartition("/")[2].rpartition(".")[0])
                self.musiclist.addItem(track)
                print("no header")
 
        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()
menator01 likes this post
Reply
#12
Will also have to add if text in the _truncate function or it will crash. This is when testing with a wav file. I have not tested with a ogg file yet.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#13
You can also use the position slider to seek

#!/usr/bin/python3
# -*- coding: utf-8 -*-
from functools import partial
import os
import sys
import mutagen
from PyQt5.QtCore import Qt, QUrl, QDir, QTime
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QGridLayout, \
QFrame, QGraphicsDropShadowEffect, QGraphicsView, QGraphicsScene, QLabel, \
QPushButton, QHBoxLayout, QStyle, QListWidget, QFileDialog, QSlider, QVBoxLayout, QDial
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.duration = 0
        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')
  
        # Define and create the listbox
        self.musiclist = QListWidget()
        self.musiclist.setFrameShape(QFrame.Box)
        self.musiclist.setFrameShadow(QFrame.Sunken)
        self.musiclist.setStyleSheet('background-color: snow;')
  
        # Create some containers
        btn_box = QHBoxLayout()
        btn_box2 = QHBoxLayout()
        slider_box = QVBoxLayout()
        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)
        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.positionChanged.connect(self.positionChanged)
        #self.player.durationChanged.connect(self.duration)
        self.player.durationChanged.connect(self.durationChanged)
        #self.slider.valueChanged.connect(self.timer)
        self.slider.sliderMoved.connect(self.seek)
  
        # 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
        file_btn = QPushButton('Get Audio')
        file_btn.released.connect(self._files)
        file_btn.setCursor(Qt.PointingHandCursor)
        file_btn.setStyleSheet(btn_style)
        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(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 the widget and set layout to container
        widget = QWidget()
        widget.setLayout(container)
        self.setCentralWidget(widget)

        # use slider to seek
    def seek(self, seconds):
        self.player.setPosition(seconds * 1000)

    def durationChanged(self, duration):
        duration =round(duration / 1000)

        self.duration = duration
        self.slider.setMaximum(duration)
        
        # song position
    def positionChanged(self, progress):
        progress = round(progress / 1000)

        if not self.slider.isSliderDown():
            self.slider.setValue(progress)

        self.updateDurationInfo(progress)

    def updateDurationInfo(self, currentInfo):
        duration = self.duration
        if currentInfo or duration:
            currentTime = QTime(round((currentInfo/3600)%60), round((currentInfo/60)%60),
                    round(currentInfo%60), round((currentInfo*1000)%1000))
            totalTime = QTime((round(duration/3600)%60), round((duration/60)%60),
                    round(duration%60), round(duration*1000)%1000)

            format = 'hh:mm:ss' if duration > 3600 else 'mm:ss'
            tStr = currentTime.toString(format) + " / " + totalTime.toString(format)
        else:
            tStr = ""

        self._timer.setText(tStr)
        
    # 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 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():
            #print(self.player.availableMetaData())
            if self.player.metaData(QMediaMetaData.ContributingArtist):
                self.artist.setText(self.player.metaData(QMediaMetaData.ContributingArtist))
            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()
        self.status.setText('Status: Now Playing')
  
    # 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: ')
        self.track.setText('Track: ')
        self.artist.setText('Artist: ')
        self.album_title.setText('Album: ')
        self.track_title.setText('Track: ')
        self.released.setText('Released: ')
        self.genre.setText('Genre: ')
        self.art.setContentsMargins(5, 170, 5, 50)
        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 = mutagen.File(file)
                self.musiclist.addItem(str(track['TIT2']))
            except:
                track = self._truncate(file.rpartition("/")[2].rpartition(".")[0])
                self.musiclist.addItem(track)
                print("no header")
  
        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()
Reply
#14
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


Reply
#15
I left off the header and footer and revamped the code a little.
Track info updates better and gets some data from the internet radio stations.
I can't seem to find a way to get the station names without the player playing. (Want to use the station names to the musiclist instead of the urls)

#! /usr/bin/env python3.9

import sys
from mutagen.mp3 import MP3
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist, QMediaMetaData
from PyQt5.QtCore import QUrl, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QListWidget, QWidget, QHBoxLayout, \
                            QVBoxLayout, QGridLayout, QLabel, QPushButton, QFrame, QSlider, \
                            QStyle, QRadioButton, QFileDialog
from PyQt5.QtGui import QIcon, QPixmap



class Window(QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Set the window title
        self.setWindowTitle('PyQt5 Audio Player')

        # Set some variables
        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist()
        self.musiclist = QListWidget()
        self.player.setPlaylist(self.playlist)

        # Changes cursor to hand when over button or link
        self.hand = Qt.PointingHandCursor

        # Set the spacing on the musiclist
        self.musiclist.setSpacing(2)

        # Create the needed containers
        container = QGridLayout()
        container.setSpacing(0)

        info_container = QGridLayout()
        info_frame = QFrame()
        info_frame.setContentsMargins(0, 3, 2, 3)
        info_frame.setMinimumHeight(300)
        info_frame.setMaximumWidth(361)
        info_frame.setFrameShape(QFrame.Box)
        info_frame.setFrameShadow(QFrame.Sunken)
        info_frame.setLayout(info_container)

        radio_frame = QFrame()
        radio_frame.setContentsMargins(0,0,0,0)
        radio_frame.setFrameShape(QFrame.Box)
        radio_frame.setFrameShadow(QFrame.Sunken)
        radio_frame.setMinimumHeight(60)
        radio_frame.setMaximumHeight(60)
        radio_container = QGridLayout()
        radio_frame.setLayout(radio_container)

        # Frame for the slider
        slider_box = QGridLayout()
        slider_frame = QFrame()
        slider_frame.setContentsMargins(0, 3, 2, 3)
        slider_frame.setFrameShape(QFrame.Box)
        slider_frame.setFrameShadow(QFrame.Sunken)
        slider_frame.setLayout(slider_box)

        # Control Box to hold volume, play, next, prev, stop
        control_box = QGridLayout()
        control_frame = QFrame()
        control_frame.setFrameShape(QFrame.Box)
        control_frame.setFrameShadow(QFrame.Sunken)
        control_frame.setContentsMargins(4,4,1,4)
        control_frame.setLayout(control_box)



        # Create some common styles
        self.musiclist.setFrameShadow(QFrame.Sunken)
        self.musiclist.setStyleSheet('''
                                     margin-top: 3px; margin-bottom: 3px; margin-left: 2px;
                                     background-color: snow; padding: 12px;
                                     ''')

        style = '''
                    background-color: snow; padding-left: 8px; padding-right: 20px;
                    border: 1px solid lightgray; font-weight: bold; font-size: 10pt;
                '''
        style2 = '''
                 background-color: snow; padding-left: 8px; padding-right: 20px;
                 border: 1px solid lightgray; font-weight: 400; font-size: 10pt; color: navy;
                 '''

        # Common button style
        self.btn_style = '''
                        QPushButton{background-color: skyblue; color: navy;
                                    font-size: 10pt; font-weight: 500; padding: 6px;
                                    margin-left: 6px;}
                        QPushButton:hover{background-color: lightskyblue; color: blue;
                                            font-weight: 600; padding: 6px;}
                        QPushButton:pressed{background-color: dodgerblue; color: lightblue;
                                            font-weight: 400; padding: 4px;}
                    '''

        # Create the needed labels. Labels without self will not change data.
        #Labels with self will change data
        status_label = QLabel('Status:')
        status_label.setMinimumHeight(30)
        status_label.setMaximumHeight(30)
        status_label.setMinimumWidth(80)
        status_label.setMaximumWidth(80)
        status_label.setStyleSheet(style)

        self.status_label = QLabel('Now Stopped')
        self.status_label.setMinimumHeight(30)
        self.status_label.setMaximumHeight(30)
        self.status_label.setMinimumWidth(200)
        self.status_label.setMaximumWidth(200)
        self.status_label.setStyleSheet(style2)

        track_label = QLabel('Track:')
        track_label.setMinimumHeight(30)
        track_label.setMaximumHeight(30)
        track_label.setMinimumWidth(80)
        track_label.setMaximumWidth(80)
        track_label.setStyleSheet(style)

        self.track_label = QLabel()
        self.track_label.setMinimumHeight(30)
        self.track_label.setMaximumHeight(30)
        self.track_label.setMinimumWidth(420)
        self.track_label.setStyleSheet(style2)

        artist = QLabel('Artist:')
        artist.setStyleSheet(style)
        artist.setMaximumHeight(30)
        artist.setMinimumHeight(30)

        title = QLabel('Title:')
        title.setStyleSheet(style)
        title.setMaximumHeight(30)
        title.setMinimumHeight(30)

        album = QLabel('Album')
        album.setStyleSheet(style)
        album.setMaximumHeight(30)
        album.setMinimumHeight(30)

        released = QLabel('Released:')
        released.setStyleSheet(style)
        released.setMaximumHeight(30)
        released.setMinimumHeight(30)

        genre = QLabel('Genre:')
        genre.setStyleSheet(style)
        genre.setMaximumHeight(30)
        genre.setMinimumHeight(30)

        self.artist = QLabel()
        self.artist.setStyleSheet(style2)
        self.artist.setMinimumWidth(235)
        self.artist.setMaximumHeight(30)
        self.artist.setMinimumHeight(30)

        self.title = QLabel()
        self.title.setStyleSheet(style2)
        self.title.setMinimumWidth(235)
        self.title.setMaximumHeight(30)
        self.title.setMinimumHeight(30)

        self.album = QLabel()
        self.album.setStyleSheet(style2)
        self.album.setMinimumWidth(235)
        self.album.setMaximumHeight(30)
        self.album.setMinimumHeight(30)

        self.released = QLabel()
        self.released.setStyleSheet(style2)
        self.released.setMinimumWidth(235)
        self.released.setMaximumHeight(30)
        self.released.setMinimumHeight(30)

        self.genre = QLabel()
        self.genre.setStyleSheet(style2)
        self.genre.setMinimumWidth(235)
        self.genre.setMaximumHeight(30)
        self.genre.setMinimumHeight(30)

        self.cover_art = QLabel()
        self.cover_art.setMinimumSize(300, 300)
        self.cover_art.setFrameShape(QFrame.Box)
        self.cover_art.setFrameShadow(QFrame.Sunken)

        # Just a couple of spacers to keep everything push up when expanding
        self.spacer = QLabel()
        self.spacer2 = QLabel()
        self.duration_labelheader = QLabel('Duration:')
        self.duration_labelheader.setMinimumHeight(40)
        self.duration_labelheader.setMaximumHeight(40)
        self.duration_labelheader.setStyleSheet('font-size: 11pt; font-weight:500;')
        self.duration_timer = QLabel('00:00:00/00:00:00')
        self.duration_timer.setStyleSheet('font-size: 10pt; font-weight: 400; color: gray;')


        # Create the buttons
        self.get_btn = QPushButton('Get Audio')
        self.get_btn.setStyleSheet(self.btn_style)
        self.get_btn.setCursor(self.hand)
        self.get_btn.released.connect(self._files)

        clear_btn = QPushButton('Clear Playlist')
        clear_btn.setStyleSheet(self.btn_style)
        clear_btn.setCursor(self.hand)
        clear_btn.released.connect(self._clear)

        self.volume_slider = QSlider(Qt.Horizontal)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setTickInterval(10)
        self.volume_slider.setValue(70)
        self.volume_slider.setTickPosition(QSlider.TicksAbove)

        self.volume_label = QLabel(f'Volume: {self.volume_slider.value()}')
        self.volume_label.setMinimumWidth(200)
        self.volume_label.setStyleSheet('font-size: 11pt; padding-left: 25px;')

        self.play_btn = QPushButton()
        self.play_btn.setIcon(self.play_btn.style().standardIcon(QStyle.SP_MediaPlay))
        self.play_btn.setCursor(self.hand)
        self.play_btn.released.connect(self.player.play)

        stop_btn = QPushButton()
        stop_btn.setIcon(stop_btn.style().standardIcon(QStyle.SP_MediaStop))
        stop_btn.setCursor(self.hand)
        stop_btn.released.connect(self.player.stop)

        prev_btn = QPushButton()
        prev_btn.setIcon(prev_btn.style().standardIcon(QStyle.SP_MediaSkipBackward))
        prev_btn.setCursor(self.hand)
        prev_btn.released.connect(self._prev)

        next_btn = QPushButton()
        next_btn.setIcon(next_btn.style().standardIcon(QStyle.SP_MediaSkipForward))
        next_btn.setCursor(self.hand)
        next_btn.released.connect(self._next)

        # Radio buttons to choose internet radio or local files
        self.radio_btn = QRadioButton('Internet Radio')
        self._local_file_btn = QRadioButton('Local Files')
        self._local_file_btn.setChecked(True)

        # Create the slider for track length
        self.track_slider = QSlider(Qt.Horizontal)
        self.track_slider.setRange(0, 100)
        self.track_slider.setTickInterval(10)
        self.track_slider.setTickPosition(QSlider.TicksAbove)


        # Add the control buttons to the control box
        control_box.addWidget(self.volume_slider, 0,0, 1, 1)
        control_box.addWidget(self.volume_label, 0, 1, 1, 1)
        control_box.addWidget(self.play_btn, 0, 2, 1, 1)
        control_box.addWidget(stop_btn,0, 3, 1, 1)
        control_box.addWidget(prev_btn,0, 4, 1, 1)
        control_box.addWidget(next_btn, 0, 5, 1, 1)

        # Add the radio buttons to the radio container
        radio_container.addWidget(self.radio_btn, 0, 0, 1, 1)
        radio_container.addWidget(self._local_file_btn, 0, 1, 1, 1)

        # Add the slider to the slider box
        slider_box.addWidget(self.duration_labelheader, 0, 0, 1, 1)
        slider_box.addWidget(self.duration_timer, 0, 1, 1, 1)
        slider_box.addWidget(self.track_slider,1, 0,1, 3)

        # Add widgets to info_frame/container
        info_container.addWidget(artist, 0, 0, 1, 1)
        info_container.addWidget(title, 1, 0, 1, 1)
        info_container.addWidget(album, 2, 0, 1, 1)
        info_container.addWidget(released, 3, 0, 1, 1)
        info_container.addWidget(genre, 4, 0, 1, 1)
        info_container.addWidget(self.artist, 0, 1, 1, 1)
        info_container.addWidget(self.title, 1, 1, 1, 1)
        info_container.addWidget(self.album, 2, 1, 1, 1)
        info_container.addWidget(self.released, 3, 1, 1, 1)
        info_container.addWidget(self.genre, 4, 1, 1, 1)
        info_container.addWidget(self.cover_art, 5, 0, 1, 3)
        info_container.addWidget(self.spacer, 6, 0, 1, 3)
        info_container.addWidget(self.spacer2, 7, 0, 1, 3)
        info_container.addWidget(radio_frame, 8, 0, 1, 3)

        # Add widgets and layout to container layout
        container.addWidget(status_label, 1, 0, 1, 1)
        container.addWidget(self.status_label, 1, 1, 1, 1)
        container.addWidget(track_label, 1, 2, 1, 1)
        container.addWidget(self.track_label, 1, 3, 1, 1)
        container.addWidget(self.get_btn, 1, 4, 1, 1)
        container.addWidget(clear_btn, 1, 5, 1, 1)
        container.addWidget(info_frame, 2, 0, 1, 4)
        container.addWidget(self.musiclist,2, 3, 1, 3)
        container.addWidget(slider_frame, 3, 0, 1, 3)
        container.addWidget(control_frame, 3, 3, 1, 3)

        widget = QWidget()
        widget.setLayout(container)
        self.setCentralWidget(widget)

        # Set channels for updates and changes happening in the gui
        self.volume_slider.valueChanged.connect(self._volume, self.volume_slider.value())
        self.radio_btn.toggled.connect(self._music)
        self.player.metaDataChanged.connect(self._update)
        self.player.stateChanged.connect(self._state)
        self.player.positionChanged.connect(self._slider_pos)
        self.player.durationChanged.connect(self._duration)
        self.track_slider.valueChanged.connect(self._timer)
        self.musiclist.itemDoubleClicked.connect(self._doubleclick)

    # Method for playing a track if double clicked in the music list
    def _doubleclick(self):
        self.playlist.setCurrentIndex(self.musiclist.currentRow())
        self.player.play()

    # Method for updating slider position
    def _slider_pos(self, position):
            self.track_slider.setValue(position)

    # Method for setting the range of the duration
    def _duration(self, duration):
        self.track_slider.setRange(0, duration)

    # Updates the duration timer
    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.track_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.duration_timer.setText(f'{elapsed_hours:02d}:{elapsed_minutes:02d}:{elapsed_seconds:02d} / {total_hours:02d}:{total_minutes:02d}:{total_seconds:02d}')

    # Checks player state and updates accorrdingly
    def _state(self):
        if self.player.state() == self.player.PlayingState:
            self.play_btn.setIcon(self.play_btn.style().standardIcon(QStyle.SP_MediaPause))
            self.play_btn.released.connect(self.player.pause)
            self.status_label.setText('Now Playing')
        elif self.player.state() == self.player.PausedState:
            self.play_btn.setIcon(self.play_btn.style().standardIcon(QStyle.SP_MediaPlay))
            self.play_btn.released.connect(self.player.play)
            self.status_label.setText('Now Paused')
        else:
            self.play_btn.setIcon(self.play_btn.style().standardIcon(QStyle.SP_MediaPlay))
            self.play_btn.released.connect(self.player.play)
            self.status_label.setText('Now Stopped')

    # Methods for the next/prev actions
    def _prev(self):
        if self.playlist.previousIndex() == -1:
            self.playlist.setCurrentIndex(self.playlist.mediaCount()-1)
        else:
            self.playlist.previous()
        if self.playlist.currentIndexChanged:
            self.musiclist.setCurrentRow(self.playlist.currentIndex())
        self.player.play()

    def _next(self):
        self.playlist.next()
        if self.playlist.currentIndex() == -1:
            self.playlist.setCurrentIndex(0)
        self.musiclist.setCurrentRow(self.playlist.currentIndex())
        self.player.play()

    # Method for getting the music files
    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(QUrl.fromLocalFile(file)))
            try:
                self.track = MP3(file)
                self.musiclist.addItem(str(self.track['TIT2']))
            except:
                self.track = self._truncate(file.rpartition('/')[2].rpartition('.')[0])
                self.musiclist.addItem(self.track)

        self.musiclist.setCurrentRow(0)
        self.playlist.setCurrentIndex(0)

    # Method for updating much of the text information
    def _update(self):
        self.musiclist.setCurrentRow(self.playlist.currentIndex())
        try:
            if self.player.isMetaDataAvailable():
                if self.player.metaData(QMediaMetaData.AlbumArtist):
                    self.artist.setText(self.player.metaData(QMediaMetaData.AlbumArtist))

                if self.player.metaData(QMediaMetaData.Title):
                    if self.radio_btn.isChecked():
                        info = self.player.metaData(QMediaMetaData.Title).split('-')
                        self.artist.setText(info[0])
                        self.title.setText(info[1])
                        self.track_label.setText(info[1])
                    else:
                        self.title.setText(self._truncate(self.player.metaData(QMediaMetaData.Title)))
                        self.track_label.setText(self._truncate(self.player.metaData(QMediaMetaData.Title)))

                if self.player.metaData(QMediaMetaData.AlbumTitle):
                    self.album.setText(self._truncate(self.player.metaData(QMediaMetaData.AlbumTitle)))
                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.CoverArtImage):
                    pixmap = QPixmap(self.player.metaData(QMediaMetaData.CoverArtImage))
                    pixmap = pixmap.scaled(328, 295)
                    self.cover_art.setPixmap(pixmap)
                    self.cover_art.setStyleSheet('padding: 5px;')
            else:
                self.artist.setText('')
                self.title.setText('')
                self.album.setText('')
                self.released.setText('')
                self.genre.setText('')
                self.cover_art.setPixmap(QPixmap())
                self.track_label.setText('')

        except TypeError:
            pass

    # Method for shortening text
    def _truncate(self, text, length=25):
        if text:
            if len(text) <= length:
                return text
            else:
                return f"{' '.join(text[:length+1].split(' ')[0:-1])} ...."

    # Method for playing either radio or local audio files
    def _music(self):
        if self.radio_btn.isChecked():
            self.musiclist.clear()
            self.playlist.clear()
            self.get_btn.setEnabled(False)
            self.get_btn.setStyleSheet('''QPushButton{background-color: lightgray; color: black;
                        font-size: 10pt; font-weight: 50; padding: 6px;
                        margin-left: 6px;}''')

            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.musiclist.addItem(station)
            self.player.play()

        else:
            self._clear()
            self.get_btn.setEnabled(True)
            self.get_btn.setStyleSheet(self.btn_style)

    # Method for updating the volume
    def _volume(self, value):
        self.volume_label.setText(f'Volume: {value}')
        self.player.setVolume(value)

    # Method for clearing playlist, musiclist, and other data
    def _clear(self):
        self.playlist.clear()
        self.musiclist.clear()
        self.status_label.setText('Now Stopped')
        self.track_label.setText('')
        self._local_file_btn.setChecked(True)
        self._update()


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


Reply
#16
Added a method for adding and removing stations. Alternating colors on track list. Only works when the app is running. After closing and restarting the default will be the only stations.

#! /usr/bin/env python3.9
import sys
from mutagen.mp3 import MP3
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist, QMediaMetaData
from PyQt5.QtCore import QUrl, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QListWidget, QWidget, QHBoxLayout, \
                            QVBoxLayout, QGridLayout, QLabel, QPushButton, QFrame, QSlider, \
                            QStyle, QRadioButton, QFileDialog, QLineEdit, QMessageBox
from PyQt5.QtGui import QIcon, QPixmap, QGradient, QPalette, QBrush


class Window(QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Set the window title
        self.setWindowTitle('PyQt5 Audio Player')

        # Set window background color
        p = QPalette()
        gradient = QGradient(QGradient.RichMetal)
        p.setBrush(QPalette.Window, QBrush(gradient))
        self.setPalette(p)


        # Set some variables
        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist()
        self.musiclist = QListWidget()
        self.entry = QLineEdit()
        self.entry.setPlaceholderText('Add a station. Example - https://url/to/internet/station/stream')
        self.entry.setFocusPolicy(Qt.StrongFocus)
        self.player.setPlaylist(self.playlist)

        # Changes cursor to hand when over button or link
        self.hand = Qt.PointingHandCursor

        # Set the spacing on the musiclist
        self.musiclist.setSpacing(2)

        # Create the needed containers
        container = QGridLayout()
        container.setSpacing(0)

        info_container = QGridLayout()
        info_frame = QFrame()
        info_frame.setContentsMargins(0, 3, 2, 3)
        info_frame.setMinimumHeight(300)
        info_frame.setMaximumWidth(361)
        info_frame.setFrameShape(QFrame.Box)
        info_frame.setFrameShadow(QFrame.Sunken)
        info_frame.setLayout(info_container)

        radio_frame = QFrame()
        radio_frame.setContentsMargins(0,0,0,0)
        radio_frame.setFrameShape(QFrame.Box)
        radio_frame.setFrameShadow(QFrame.Sunken)
        radio_frame.setMinimumHeight(60)
        radio_frame.setMaximumHeight(60)
        radio_container = QGridLayout()
        radio_frame.setLayout(radio_container)

        # Frame for the slider
        slider_box = QGridLayout()
        slider_frame = QFrame()
        slider_frame.setContentsMargins(0, 3, 2, 3)
        slider_frame.setFrameShape(QFrame.Box)
        slider_frame.setFrameShadow(QFrame.Sunken)
        slider_frame.setLayout(slider_box)

        # Control Box to hold volume, play, next, prev, stop
        control_box = QGridLayout()
        control_frame = QFrame()
        control_frame.setFrameShape(QFrame.Box)
        control_frame.setFrameShadow(QFrame.Sunken)
        control_frame.setContentsMargins(4,4,1,4)
        control_frame.setLayout(control_box)

        # Station edit frame
        self.station_frame = QFrame()
        self.station_frame.setFrameShape(QFrame.Box)
        self.station_frame.setFrameShadow(QFrame.Sunken)

        station_box = QGridLayout()
        self.station_frame.setLayout(station_box)
        self.station_frame.setMaximumHeight(60)
        self.station_frame.hide()


        # Create some common styles
        self.musiclist.setFrameShadow(QFrame.Sunken)
        self.musiclist.setAlternatingRowColors(True)
        self.musiclist.setStyleSheet('''
                                     margin-top: 3px; margin-bottom: 3px; margin-left: 2px;
                                     alternate-background-color: lightblue; background-color:white;
                                     padding: 12px;
                                     ''')

        style = '''
                    background-color: snow; padding-left: 8px; padding-right: 20px;
                    border: 1px solid lightgray; font-weight: bold; font-size: 10pt;
                '''
        style2 = '''
                 background-color: snow; padding-left: 8px; padding-right: 20px;
                 border: 1px solid lightgray; font-weight: 400; font-size: 10pt; color: navy;
                 '''

        # Common button style
        self.btn_style = '''
                        QPushButton{background-color: skyblue; color: navy;
                                    font-size: 10pt; font-weight: 500; padding: 6px;
                                    margin-left: 6px;}
                        QPushButton:hover{background-color: lightskyblue; color: blue;
                                            font-weight: 600; padding: 6px;}
                        QPushButton:pressed{background-color: dodgerblue; color: lightblue;
                                            font-weight: 400; padding: 4px;}
                    '''

        # Create the needed labels. Labels without self will not change data.
        #Labels with self will change data
        status_label = QLabel('Status:')
        status_label.setMinimumHeight(30)
        status_label.setMaximumHeight(30)
        status_label.setMinimumWidth(80)
        status_label.setMaximumWidth(80)
        status_label.setStyleSheet(style)

        self.status_label = QLabel('Now Stopped')
        self.status_label.setMinimumHeight(30)
        self.status_label.setMaximumHeight(30)
        self.status_label.setMinimumWidth(200)
        self.status_label.setMaximumWidth(200)
        self.status_label.setStyleSheet(style2)

        track_label = QLabel('Track:')
        track_label.setMinimumHeight(30)
        track_label.setMaximumHeight(30)
        track_label.setMinimumWidth(80)
        track_label.setMaximumWidth(80)
        track_label.setStyleSheet(style)

        self.track_label = QLabel()
        self.track_label.setMinimumHeight(30)
        self.track_label.setMaximumHeight(30)
        self.track_label.setMinimumWidth(420)
        self.track_label.setStyleSheet(style2)

        artist = QLabel('Artist:')
        artist.setStyleSheet(style)
        artist.setMaximumHeight(30)
        artist.setMinimumHeight(30)

        title = QLabel('Title:')
        title.setStyleSheet(style)
        title.setMaximumHeight(30)
        title.setMinimumHeight(30)

        album = QLabel('Album')
        album.setStyleSheet(style)
        album.setMaximumHeight(30)
        album.setMinimumHeight(30)

        released = QLabel('Released:')
        released.setStyleSheet(style)
        released.setMaximumHeight(30)
        released.setMinimumHeight(30)

        genre = QLabel('Genre:')
        genre.setStyleSheet(style)
        genre.setMaximumHeight(30)
        genre.setMinimumHeight(30)

        self.artist = QLabel()
        self.artist.setStyleSheet(style2)
        self.artist.setMinimumWidth(235)
        self.artist.setMaximumHeight(30)
        self.artist.setMinimumHeight(30)

        self.title = QLabel()
        self.title.setStyleSheet(style2)
        self.title.setMinimumWidth(235)
        self.title.setMaximumHeight(30)
        self.title.setMinimumHeight(30)

        self.album = QLabel()
        self.album.setStyleSheet(style2)
        self.album.setMinimumWidth(235)
        self.album.setMaximumHeight(30)
        self.album.setMinimumHeight(30)

        self.released = QLabel()
        self.released.setStyleSheet(style2)
        self.released.setMinimumWidth(235)
        self.released.setMaximumHeight(30)
        self.released.setMinimumHeight(30)

        self.genre = QLabel()
        self.genre.setStyleSheet(style2)
        self.genre.setMinimumWidth(235)
        self.genre.setMaximumHeight(30)
        self.genre.setMinimumHeight(30)

        self.cover_art = QLabel()
        self.cover_art.setMinimumSize(300, 300)
        self.cover_art.setFrameShape(QFrame.Box)
        self.cover_art.setFrameShadow(QFrame.Sunken)

        # Just a couple of spacers to keep everything push up when expanding
        self.spacer = QLabel()
        self.spacer2 = QLabel()
        self.duration_labelheader = QLabel('Duration:')
        self.duration_labelheader.setMinimumHeight(40)
        self.duration_labelheader.setMaximumHeight(40)
        self.duration_labelheader.setStyleSheet('font-size: 11pt; font-weight:500;')
        self.duration_timer = QLabel('00:00:00/00:00:00')
        self.duration_timer.setStyleSheet('font-size: 10pt; font-weight: 400; color: gray;')


        # Create the buttons
        self.get_btn = QPushButton('Get Audio')
        self.get_btn.setStyleSheet(self.btn_style)
        self.get_btn.setCursor(self.hand)
        self.get_btn.released.connect(self._files)

        clear_btn = QPushButton('Clear Playlist')
        clear_btn.setStyleSheet(self.btn_style)
        clear_btn.setCursor(self.hand)
        clear_btn.released.connect(self._clear)

        self.volume_slider = QSlider(Qt.Horizontal)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setTickInterval(10)
        self.volume_slider.setValue(70)
        self.volume_slider.setTickPosition(QSlider.TicksAbove)

        self.volume_label = QLabel(f'Volume: {self.volume_slider.value()}')
        self.volume_label.setMinimumWidth(200)
        self.volume_label.setStyleSheet('font-size: 11pt; padding-left: 25px;')

        self.play_btn = QPushButton()
        self.play_btn.setIcon(self.play_btn.style().standardIcon(QStyle.SP_MediaPlay))
        self.play_btn.setCursor(self.hand)
        self.play_btn.released.connect(self.player.play)

        stop_btn = QPushButton()
        stop_btn.setIcon(stop_btn.style().standardIcon(QStyle.SP_MediaStop))
        stop_btn.setCursor(self.hand)
        stop_btn.released.connect(self.player.stop)

        prev_btn = QPushButton()
        prev_btn.setIcon(prev_btn.style().standardIcon(QStyle.SP_MediaSkipBackward))
        prev_btn.setCursor(self.hand)
        prev_btn.released.connect(self._prev)

        next_btn = QPushButton()
        next_btn.setIcon(next_btn.style().standardIcon(QStyle.SP_MediaSkipForward))
        next_btn.setCursor(self.hand)
        next_btn.released.connect(self._next)

        # Radio buttons to choose internet radio or local files
        self.radio_btn = QRadioButton('Internet Radio')
        self._local_file_btn = QRadioButton('Local Files')
        self._local_file_btn.setChecked(True)

        # Buttons for adding and removing stations
        self.add_btn = QPushButton('Add Station')
        self.add_btn.setStyleSheet(self.btn_style)
        self.add_btn.setCursor(self.hand)
        self.add_btn.released.connect(self.add_station)

        self.del_btn = QPushButton('Remove Station')
        self.del_btn.setStyleSheet(self.btn_style)
        self.del_btn.setCursor(self.hand)
        self.del_btn.released.connect(self.remove_station)

        # Create the slider for track length
        self.track_slider = QSlider(Qt.Horizontal)
        self.track_slider.setRange(0, 100)
        # self.track_slider.setTickInterval(10)
        # self.track_slider.setTickPosition(QSlider.TicksAbove)

        # Add the control buttons to the control box
        control_box.addWidget(self.volume_slider, 0,0, 1, 1)
        control_box.addWidget(self.volume_label, 0, 1, 1, 1)
        control_box.addWidget(self.play_btn, 0, 2, 1, 1)
        control_box.addWidget(stop_btn,0, 3, 1, 1)
        control_box.addWidget(prev_btn,0, 4, 1, 1)
        control_box.addWidget(next_btn, 0, 5, 1, 1)

        # Add station edit controls to station grid box
        station_box.addWidget(self.add_btn, 0, 0, 1, 1)
        station_box.addWidget(self.del_btn, 0, 1, 1, 1)
        station_box.addWidget(self.entry, 0, 2, 1, 3)

        # Add the radio buttons to the radio container
        radio_container.addWidget(self.radio_btn, 0, 0, 1, 1)
        radio_container.addWidget(self._local_file_btn, 0, 1, 1, 1)

        # Add the slider to the slider box
        slider_box.addWidget(self.duration_labelheader, 0, 0, 1, 1)
        slider_box.addWidget(self.duration_timer, 0, 1, 1, 1)
        slider_box.addWidget(self.track_slider,1, 0,1, 3)

        # Add widgets to info_frame/container
        info_container.addWidget(artist, 0, 0, 1, 1)
        info_container.addWidget(title, 1, 0, 1, 1)
        info_container.addWidget(album, 2, 0, 1, 1)
        info_container.addWidget(released, 3, 0, 1, 1)
        info_container.addWidget(genre, 4, 0, 1, 1)
        info_container.addWidget(self.artist, 0, 1, 1, 1)
        info_container.addWidget(self.title, 1, 1, 1, 1)
        info_container.addWidget(self.album, 2, 1, 1, 1)
        info_container.addWidget(self.released, 3, 1, 1, 1)
        info_container.addWidget(self.genre, 4, 1, 1, 1)
        info_container.addWidget(self.cover_art, 5, 0, 1, 3)
        info_container.addWidget(self.spacer, 6, 0, 1, 3)
        info_container.addWidget(self.spacer2, 7, 0, 1, 3)
        info_container.addWidget(radio_frame, 8, 0, 1, 3)

        # Add widgets and layout to container layout
        container.addWidget(status_label, 1, 0, 1, 1)
        container.addWidget(self.status_label, 1, 1, 1, 1)
        container.addWidget(track_label, 1, 2, 1, 1)
        container.addWidget(self.track_label, 1, 3, 1, 1)
        container.addWidget(self.get_btn, 1, 4, 1, 1)
        container.addWidget(clear_btn, 1, 5, 1, 1)
        container.addWidget(info_frame, 2, 0, 1, 4)
        container.addWidget(self.musiclist,2, 3, 1, 3)
        container.addWidget(self.station_frame, 3, 0, 1, 6)
        container.addWidget(slider_frame, 4, 0, 1, 3)
        container.addWidget(control_frame, 4, 3, 1, 3)

        widget = QWidget()
        widget.setLayout(container)
        self.setCentralWidget(widget)

        # Set channels for updates and changes happening in the gui
        self.volume_slider.valueChanged.connect(self._volume, self.volume_slider.value())
        self.radio_btn.toggled.connect(self._music)
        self.player.metaDataChanged.connect(self._update)
        self.player.stateChanged.connect(self._state)
        self.player.positionChanged.connect(self._slider_pos)
        self.player.durationChanged.connect(self._duration)
        self.track_slider.valueChanged.connect(self._timer)
        self.musiclist.itemDoubleClicked.connect(self._doubleclick)

    # Method for adding stations
    def add_station(self):
        station = self.entry.text()
        if station and 'http://' in station or 'https://' in station:
            self.musiclist.addItem(station)
            self.playlist.addMedia(QMediaContent(QUrl(station)))
        else:
            message = QMessageBox.warning(self,'Warning!','Please use the correct \
format to add your station. \nExample: https://some/url/to/station/stream')
        self.entry.clear()

    # Method for removing stations
    def remove_station(self):
        if self.musiclist.count() > 0:
            row = self.musiclist.currentRow()
            item = self.musiclist.takeItem(row)
            del item
            self.playlist.removeMedia(row)
        else:
            message = QMessageBox.warning(self, 'Warning!', 'There is no more stations to remove!')

    # Method for playing a track if double clicked in the music list
    def _doubleclick(self):
        self.playlist.setCurrentIndex(self.musiclist.currentRow())
        self.player.play()

    # Method for updating slider position
    def _slider_pos(self, position):
            self.track_slider.setValue(position)

    # Method for setting the range of the duration
    def _duration(self, duration):
        self.track_slider.setRange(0, duration)

    # Updates the duration timer
    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.track_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.duration_timer.setText(f'{elapsed_hours:02d}:{elapsed_minutes:02d}:{elapsed_seconds:02d} / {total_hours:02d}:{total_minutes:02d}:{total_seconds:02d}')

    # Checks player state and updates accorrdingly
    def _state(self):
        if self.player.state() == self.player.PlayingState:
            self.play_btn.setIcon(self.play_btn.style().standardIcon(QStyle.SP_MediaPause))
            self.play_btn.released.connect(self.player.pause)
            self.status_label.setText('Now Playing')
        elif self.player.state() == self.player.PausedState:
            self.play_btn.setIcon(self.play_btn.style().standardIcon(QStyle.SP_MediaPlay))
            self.play_btn.released.connect(self.player.play)
            self.status_label.setText('Now Paused')
        else:
            self.play_btn.setIcon(self.play_btn.style().standardIcon(QStyle.SP_MediaPlay))
            self.play_btn.released.connect(self.player.play)
            self.status_label.setText('Now Stopped')

    # Methods for the next/prev actions
    def _prev(self):
        if self.playlist.previousIndex() == -1:
            self.playlist.setCurrentIndex(self.playlist.mediaCount()-1)
        else:
            self.playlist.previous()
        if self.playlist.currentIndexChanged:
            self.musiclist.setCurrentRow(self.playlist.currentIndex())
        self.player.play()

    def _next(self):
        self.playlist.next()
        if self.playlist.currentIndex() == -1:
            self.playlist.setCurrentIndex(0)
        self.musiclist.setCurrentRow(self.playlist.currentIndex())
        self.player.play()

    # Method for getting the music files
    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(QUrl.fromLocalFile(file)))
            try:
                self.track = MP3(file)
                self.musiclist.addItem(str(self.track['TIT2']))
            except:
                self.track = self._truncate(file.rpartition('/')[2].rpartition('.')[0])
                self.musiclist.addItem(self.track)

        self.musiclist.setCurrentRow(0)
        self.playlist.setCurrentIndex(0)

    # Method for updating much of the text information
    def _update(self):
        try:
            self.musiclist.setCurrentRow(self.playlist.currentIndex())
        except:
            pass
        try:
            if self.player.isMetaDataAvailable():
                if self.player.metaData(QMediaMetaData.AlbumArtist):
                    self.artist.setText(self.player.metaData(QMediaMetaData.AlbumArtist))

                if self.player.metaData(QMediaMetaData.Title):
                    if self.radio_btn.isChecked():
                        info = self.player.metaData(QMediaMetaData.Title).split('-')
                        self.artist.setText(self._truncate(info[0]))
                        self.title.setText(self._truncate(info[1]))
                        self.track_label.setText(info[1])
                    else:
                        self.title.setText(self._truncate(self.player.metaData(QMediaMetaData.Title)))
                        self.track_label.setText(self._truncate(self.player.metaData(QMediaMetaData.Title)))

                if self.player.metaData(QMediaMetaData.AlbumTitle):
                    self.album.setText(self._truncate(self.player.metaData(QMediaMetaData.AlbumTitle)))
                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.CoverArtImage):
                    pixmap = QPixmap(self.player.metaData(QMediaMetaData.CoverArtImage))
                    pixmap = pixmap.scaled(328, 295)
                    self.cover_art.setPixmap(pixmap)
                    self.cover_art.setStyleSheet('padding: 5px;')
            else:
                self.artist.setText('')
                self.title.setText('')
                self.album.setText('')
                self.released.setText('')
                self.genre.setText('')
                self.cover_art.setPixmap(QPixmap())
                self.track_label.setText('')

        except TypeError:
            pass

    # Method for shortening text
    def _truncate(self, text, length=25):
        if text:
            if len(text) <= length:
                return text
            else:
                return f"{' '.join(text[:length+1].split(' ')[0:-1])} ...."

    # Method for playing either radio or local audio files
    def _music(self):
        if self.radio_btn.isChecked():
            self.station_frame.show()
            self.entry.setFocus()
            self.musiclist.clear()
            self.playlist.clear()
            self.get_btn.setEnabled(False)
            self.get_btn.setStyleSheet('''QPushButton{background-color: lightgray; color: black;
                        font-size: 10pt; font-weight: 50; padding: 6px;
                        margin-left: 6px;}''')

            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.musiclist.addItem(station)
            self.player.play()

        else:
            self._clear()
            self.get_btn.setEnabled(True)
            self.get_btn.setStyleSheet(self.btn_style)
            self.station_frame.hide()

    # Method for updating the volume
    def _volume(self, value):
        self.volume_label.setText(f'Volume: {value}')
        self.player.setVolume(value)

    # Method for clearing playlist, musiclist, and other data
    def _clear(self):
        self.playlist.clear()
        self.musiclist.clear()
        self.status_label.setText('Now Stopped')
        self.track_label.setText('')
        self._local_file_btn.setChecked(True)
        self.player.setVolume(70)
        self.volume_slider.setValue(70)
        self._update()

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


Reply
#17
The cover image should be resized on window resizing.

Whats that black line above the duration slider?

[Image: aplayer1.png?raw=1]
Larz60+ likes this post
Reply
#18
I think it's the image pushing down on the container. Not there until the image loads.
Only way I've found to get rid of it is to remove the ticks.
The image stays the same on my pc. (linux, haven't tried on windows yet.) Full screen or not. Are you using a mac?
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#19
I am using Linux Mint 20.2.
The picture stays the same for me. The problem is that when the window is made smaller, the picture is not reduced and the radio buttons are no longer clearly visible. This can also be seen on my screenshot above.
I guess it's a layout problem.
Reply
#20
Ok. I will look at that tomorrow night when I get home from work.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Basic Music Player with tkinter menator01 4 4,725 Jul-31-2021, 04:27 AM
Last Post: ndc85430

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020