On this website, you will find tutorials for installing & managing software, lists of the best linux resources, and in depth guides to linux.
Join us on SlackBuilding a Music Controlling Python Start Page for Linux
Robert Washbourne - 3 years ago - programming, themes, python
By using the Gnome GLib apis, we can use the Playerctl Linux package with Python. Bundling this with bottle, a simple Python server, and websockets to listen for updates, we have a music controlling homepage.
Dependencies
You will of course need Python. Here, I use python2 because gevent (the websocket) does not support Python3.
Linux packages you need:
Python dependencies (install with pip):
- bottle
- bottle-websocket
Github
You can download all the code for this project on DevPy's github here.
You can clone this to your computer with
git clone https://github.com/devpytech/musicpage.git
Adding a websocket
First we need to import the (extensive) list of packages.
#!/usr/bin/env python2
from bottle.ext.websocket import GeventWebSocketServer
import gi
gi.require_version('Playerctl', '1.0')
from gi.repository import GLib, GObject, Playerctl
from bottle.ext.websocket import websocket
from geventwebsocket.exceptions import WebSocketError
from bottle import run, get
import threading
import os
from gevent import monkey
monkey.patch_all()
print("starting websocket")
Adding a route
When the main page connects to the websocket, we send the attributes so we can update the display.
@get('/websocket', apply=[websocket]) #provide a websocket connection
def echo(ws):
print("connected")
# send the music status
ws.send('%s,%s,%s,%s' % (os.popen("playerctl metadata mpris:artUrl").read(), Playerctl.Player().get_title(), Playerctl.Player().get_artist(), Playerctl.Player().get_property("status")))
Playerctl process
We can make a new Playerctl player with player = Playerctl.Player()
and run a loop to check for updates with player.on('metadata', on_track_change)
and GLib.MainLoop().run()
. When the track is updated, we send a message to the webpage.
@get('/websocket', apply=[websocket])
def echo(ws):
print("connected")
# send the music status
ws.send('%s,%s,%s,%s' % (os.popen("playerctl metadata mpris:artUrl").read(), Playerctl.Player().get_title(), Playerctl.Player().get_artist(), Playerctl.Player().get_property("status")))
def on_track_change(player, e):
#this function will run when the music is updated (paused, skipped, etc.)
try: #see if the connection is still open
ws.send('%s,%s,%s,%s' % (os.popen("playerctl metadata mpris:artUrl").read(), Playerctl.Player().get_title(), Playerctl.Player().get_artist(), Playerctl.Player().get_property("status")))
except WebSocketError: #if it's not open, stop the process
player.stop()
loop.quit()
ws.close()
player = Playerctl.Player()
player.on('metadata', on_track_change) #listen for music updates
loop = GLib.MainLoop()
loop.run() #run the playerctl updater
print("Disconnected from websocket")
run(host='0.0.0.0', port=7000, server=GeventWebSocketServer) #run the server
Frontend
In this section, we create the webpage that is shown (and updated in real time) for the user.
Settings
Here we add color choosing and links that we use in html later.
#!/usr/bin/env python2
from bottle import route, run, request, get, post, static_file
import gi
gi.require_version('Playerctl', '1.0')
from gi.repository import GLib, GObject, Playerctl
from subprocess import Popen
import sys
import os
Popen([sys.executable, './sendup.py']) #run the websocket script in the same folder (used to update info)
############
# SETTINGS
############
links = [['DevPy','http://DevPy.me'],['Reddit','http://Reddit.com'],['Google','http://Google.com']] #List of links to add to page
theme = 'dark' #light or dark theme (sets colors)
############
# /SETTINGS
############
if theme == 'dark':
bgc = '#222'
fgc = 'rgba(255,255,255,0.6)'
bw = 'gray'
else:
bgc = '#fff'
fgc = '#111'
bw = 'black'
for x in range(len(links)):
#create list of links
links[x] = '<a class="db link no-underline underline-hover '+bw+'" href="'+links[x][1]+'">'+links[x][0]+'</a>'
linkhtml = '''<div class="db center" style="width: 182px;"><div class="mt4 lh-copy">'''
#add the links together
for x in links:
linkhtml += x
linkhtml += '''</div></div>'''
HTML
We generate the initial look of the app. The artist, song, and image are initialized as well.
@get('/') # or @route('/login')
def index():
player = Playerctl.Player() #grab a player
try: #Check if a music app is open (spotify, audacious, vlc,...)
title = player.get_title()
artist = player.get_artist()
img = os.popen("playerctl metadata mpris:artUrl").read()
except: #if nothing is open just show blank music
title = ''
artist = ''
img = ''
return '''
<html>
<head>
<title>Start Page</title>
<link rel="stylesheet" href="/static/tachyons.css" />
<style>
/* Here we add the icons for play/pause/next/forward */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: local('Material Icons'), local('MaterialIcons-Regular'), url(/static/mat.woff2) format('woff2');
}
.material-icons {
cursor: pointer;
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 30px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
}
.material-icons.md-light { color: rgba(0, 0, 0, 0.54); }
.material-icons.md-dark { color: rgba(255, 255, 255, 1); }
</style>
</head>
<body style="background-color:'''+bgc+'''">
<div style="position:relative; top: 35%%; transform: translateY(-50%%); ">
<div class="mt4 db center black link" style="width: 178px;">
<img id="img" style="width:180px;height:178px;background-color:#222;"class="db ba b--black-10" src="%s">
<dl class="mt2 lh-copy">
<dt class="clip">Title</dt>
<dd id="title" class="ml0 fw9" style="color:''' % (img)+fgc+'''">%s</dd>
<dt class="clip">Artist</dt>
<dd id="artist" class="ml0 gray">%s</dd>
</dl>
<div style="text-align:center;color:''' % (title, artist)+fgc+'''">
<a onclick="previous();" style="float:left;"><i class="material-icons md-'''+theme+'''">skip_previous</i></a>
<a onclick="toggle();"><i id ="stateicon" style="width:32px;" class="material-icons md-'''+theme+'''">pause_arrow</i></a>
<a onclick="next();" style="float:right;"><i class="material-icons md-'''+theme+'''">skip_next</i></a>
</div>
</div>
''' + linkhtml + # add the scripts below here
Scripts
Here we add some jquery scripts to talk to the server.
# attach this to the previous html code
'''
</div>
<script src="/static/jquery-3.1.1.min.js"></script>
<script type="text/javascript">
var ws;
ws = new WebSocket("ws://0.0.0.0:7000/websocket");
ws.onopen = function (evt) {
ws.send("Connected");
};
ws.onclose = function (evt) {
ws.send("Closed");
};
//in this function we listen for updates from the server and write them to the page.
ws.onmessage = function (evt) {
var array = evt.data.split(',');
if (array.length == 4) {
console.log(array);
$("#img").attr("src", array[0]);
$("#title").text(array[1]);
$("#artist").text(array[2]);
if (array[3] === 'Playing') {
$('#stateicon').text('pause');
} else {
$('#stateicon').text('play_arrow');
}
ws.send("Updated");
} else {
ws.send("OK.")
}
};
</script>
<script>
// when the buttons are pressed, post the server
function next() {
$.post('/next')
return false;
}
function previous() {
$.post('/previous')
return false;
}
function toggle() {
$.post('/toggle');
return false;
}
</script>
</body>
</html>
'''
Post replies
Control the music when the client POSTs the server.
@post('/next')
def next_song():
Playerctl.Player().next()
@post('/previous')
def next_song():
Playerctl.Player().previous()
@post('/toggle')
def next_song():
Playerctl.Player().play_pause()
@post('/status')
def is_playing():
return Playerctl.Player().get_property("status")
@route('/static/<filename:path>') #This is for static files
def send_static(filename):
return static_file(filename, root=os.path.dirname(os.path.realpath(' ')))
run(host='0.0.0.0', port=8080) #run the server
Finishing up
Now you should have a nice music controlling start page. You can make this show as the new tab on Chrome with this extension, and on Firefox with this one. Just set the url to 0.0.0.0:8080
.
Here I use my method to show a music visualizer on the desktop, plus my openbox setup.