Posts: 992
Threads: 15
Joined: Dec 2016
Oct-18-2021, 05:12 PM
(This post was last modified: Oct-18-2021, 05:12 PM by Axel_Erfurt.)
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
Posts: 984
Threads: 104
Joined: Sep 2019
Oct-18-2021, 06:44 PM
(This post was last modified: Oct-18-2021, 06:44 PM by menator01.)
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.
Posts: 992
Threads: 15
Joined: Dec 2016
Oct-19-2021, 05:21 PM
(This post was last modified: Oct-19-2021, 05:21 PM by Axel_Erfurt.)
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()
Posts: 984
Threads: 104
Joined: Sep 2019
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()
Posts: 984
Threads: 104
Joined: Sep 2019
Oct-31-2021, 04:35 AM
(This post was last modified: Oct-31-2021, 07:55 AM by menator01.)
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()
Posts: 984
Threads: 104
Joined: Sep 2019
Nov-02-2021, 09:34 AM
(This post was last modified: Nov-07-2021, 09:01 PM by menator01.)
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()
Posts: 992
Threads: 15
Joined: Dec 2016
The cover image should be resized on window resizing.
Whats that black line above the duration slider?
Posts: 984
Threads: 104
Joined: Sep 2019
Nov-03-2021, 08:30 AM
(This post was last modified: Nov-03-2021, 08:30 AM by menator01.)
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?
Posts: 992
Threads: 15
Joined: Dec 2016
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.
Posts: 984
Threads: 104
Joined: Sep 2019
Ok. I will look at that tomorrow night when I get home from work.
|