Created
May 21, 2014 04:49
-
-
Save bibich/34915615d68ad050a27c to your computer and use it in GitHub Desktop.
Qobuz player for linux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# | |
# Copyright 2014 Bibichette.com Ltd. | |
# | |
# Authors: Julien Chanseaume <@bibichette> | |
# | |
# | |
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
# Version 2, December 2004 | |
# | |
# Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | |
# | |
# Everyone is permitted to copy and distribute verbatim or modified | |
# copies of this license document, and changing it is allowed as long | |
# as the name is changed. | |
# | |
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
# | |
# 0. You just DO WHAT THE FUCK YOU WANT TO. | |
# | |
# | |
""" | |
A really simple player for Qobuz on Linux. It uses the Qobuz | |
web player but in a native window. | |
Why ? why not. I've just added basic support for media keys and | |
notifications. | |
There is some dependecies like python-webkit, pynotify, appindicator | |
you could already have installed. just try. | |
install | |
--------------- | |
sudo cp qobuz.py /usr/local/bin/qobuz | |
sudo chmod +x /usr/local/bin/qobuz | |
qobuz | |
""" | |
import gobject | |
import gtk | |
import appindicator | |
import webkit | |
import dbus | |
import pynotify | |
import json | |
#import pprint | |
from datetime import datetime | |
from dbus.mainloop.glib import DBusGMainLoop | |
class Qobuz(gtk.Window): | |
"""Quobuz""" | |
def __init__(self): | |
super(Qobuz, self).__init__() | |
self.js_injected = False | |
self.quobuz_title = "Qobuz" | |
self.old_title = "Qobuz" | |
self.set_title(self.quobuz_title) | |
self.connect("destroy", gtk.main_quit) | |
self.maximize() | |
scroll = gtk.ScrolledWindow() | |
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) | |
self.add(scroll) | |
self.browser = webkit.WebView() | |
self.browser.get_settings().set_property("enable-webgl", True) | |
scroll.add(self.browser) | |
self.browser.open("http://player.qobuz.com"); | |
self.browser.connect('title-changed', self.title_changed) | |
self.browser.connect('load-finished', self.init_updater) | |
def trigger_action(self, action): | |
print "trigger action {}".format(action) | |
if (action == "play"): | |
self.browser.execute_script( | |
"jQuery('#player-play-button').click();") | |
elif (action == "pause"): | |
self.browser.execute_script( | |
"jQuery('#player-play-button').click();") | |
elif (action == "next"): | |
self.browser.execute_script( | |
"jQuery('#player-next-button').click();") | |
elif (action == "previous"): | |
self.browser.execute_script( | |
"jQuery('#player-previous-button').click();") | |
else: | |
print "Unknow action" | |
def title_changed(self, widget, frame, title): | |
if not self.js_injected: | |
return | |
if title.startswith("{\"app\":"): | |
try: | |
metadata = json.loads(title) | |
self.metadata = metadata.get("app", None) | |
self.handle_metadata() | |
except Exception as inst: | |
print inst | |
else: | |
print "title changed {}".format(title) | |
clean_title = title.replace(" - Qobuz Player", "") | |
if clean_title != self.old_title: | |
self.old_title = clean_title | |
self.browser.execute_script("qbAppUpdate();") | |
def init_updater(self, webview, webframe): | |
if self.js_injected: | |
return | |
self.js_injected = True | |
print "inject script" | |
script = """ | |
function qbAppUpdate(){ | |
var data = {}; | |
if (qbPlayer.playerManager.currentTrack){ | |
data["isPlaying"] = qbPlayer.playerManager.isPlaying(); | |
data["song"] = qbPlayer.playerManager.currentTrack; | |
document.title = JSON.stringify({app: data}); | |
} | |
} | |
""" | |
webview.execute_script(script) | |
def handle_metadata(self): | |
#print pprint.pprint(self.metadata) | |
if self.metadata is None: | |
print "no Metadata" | |
return | |
#if not self.metadata[u"isPlaying"]: | |
# return | |
if self.metadata[u"song"] is None: | |
print "no Song" | |
return | |
song = self.metadata[u"song"] | |
metadata = song.get(u"metadata", None) | |
if metadata is None: | |
print "no Song metadata" | |
return | |
self.notify( | |
'''<b>song: {title}</b> | |
<br />album: {albumTitle} | |
<br />artist: {artistName}''' | |
.format(**metadata)) | |
self.set_title("{title} {albumTitle} {artistName} - Qobuz" | |
.format(**metadata)) | |
def notify(self, message, image=None): | |
"""Display a notification using pynotify | |
""" | |
notification = pynotify.Notification('Qobuz', message, image) | |
notification.set_urgency(pynotify.URGENCY_NORMAL) | |
notification.set_timeout(pynotify.EXPIRES_DEFAULT) | |
notification.show() | |
class MediaKeyHandler(): | |
def __init__(self, qobuz): | |
self.qobuz = qobuz | |
self.keys = { | |
"Play" : "play", | |
"Pause" : "pause", | |
"Next" : "next", | |
"Previous" : "previous", | |
} | |
try: | |
self.bus = dbus.Bus(dbus.Bus.TYPE_SESSION) | |
self.bus_object = self.bus.get_object( | |
'org.gnome.SettingsDaemon', | |
'/org/gnome/SettingsDaemon/MediaKeys' | |
) | |
except: | |
print "Gnome SettingsDaemon not found, multimedia keys will not interact with Qobuz" | |
return | |
try: | |
self.bus_object.GrabMediaPlayerKeys( | |
"Qobuz", | |
0, | |
dbus_interface='org.gnome.SettingsDaemon.MediaKeys' | |
) | |
except: | |
pass | |
self.bus_object.connect_to_signal( | |
'MediaPlayerKeyPressed', | |
self.handle_mediakey | |
) | |
def handle_mediakey(self, *mmkeys): | |
for key in mmkeys: | |
if not key in self.keys or not self.keys[key]: | |
continue | |
self.qobuz.trigger_action(self.keys[key]) | |
def set_loop(self, loop): | |
self.bus.set_default_main_loop(loop) | |
class QobuzIndicator(appindicator.Indicator): | |
def __init__(self, qobuz): | |
pynotify.init('Qobuz') | |
super(QobuzIndicator, self).__init__( | |
"quobuz-client", | |
"sound-icon", | |
appindicator.CATEGORY_APPLICATION_STATUS) | |
self.qobuz = qobuz | |
self.set_status(appindicator.STATUS_ACTIVE) | |
self.set_attention_icon("multimedia-audio-player") | |
# create a menu | |
menu = gtk.Menu() | |
# buttons | |
buttons = ( | |
('previous', 'Previous'), | |
('pause', 'Play / Pause'), | |
('next', 'Next'), | |
('quit', 'Quit'), | |
) | |
for button in buttons: | |
menu_item = gtk.MenuItem(button[1]) | |
menu.append(menu_item) | |
menu_item.connect("activate", self.menu_activated, | |
button[0]) | |
menu_item.show() | |
self.set_menu(menu) | |
def menu_activated(self, widget, action): | |
if action == 'quit': | |
print "bye bye" | |
gtk.main_quit() | |
else: | |
self.qobuz.trigger_action(action) | |
if __name__ == "__main__": | |
DBusGMainLoop(set_as_default=True) | |
qobuz = Qobuz() | |
qobuz.show_all() | |
print "Media Key Support" | |
mediakey = MediaKeyHandler(qobuz) | |
print "Indicator Support" | |
indicator = QobuzIndicator(qobuz) | |
gtk.main() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment