Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Configurable tuner client data.

Use force_client= in the config file: a built-in client name or a JSON literal.
  • Loading branch information...
commit e831d7a9aac789a71b0fb95aae1f49c27dc0d19f 1 parent ce9d3c4
Kevin Mehall kevinmehall authored
26 bin/pithos
View
@@ -34,6 +34,7 @@ import webbrowser
import os
import urllib2
import dbus
+import json
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
@@ -57,6 +58,7 @@ from pithos.plugin import load_plugins
from pithos.dbus_service import PithosDBusProxy, try_to_raise
from pithos.sound_menu import PithosSoundMenu
from pithos.pandora import *
+from pithos.pandora.data import *
def openBrowser(url):
@@ -184,7 +186,7 @@ class PithosWindow(gtk.Window):
if not self.preferences['username']:
self.show_preferences(is_startup=True)
- self.pandora = make_pandora(self.preferences, self.cmdopts.test)
+ self.pandora = make_pandora(self.cmdopts.test)
self.set_proxy()
self.set_audio_format()
self.pandora_connect()
@@ -354,8 +356,26 @@ class PithosWindow(gtk.Window):
self.worker_run('set_audio_quality', (self.preferences['audio_quality'],))
def pandora_connect(self, message="Logging in...", callback=None):
- args = (self.preferences['username'],
- self.preferences['password'])
+ if self.preferences['pandora_one']:
+ client = client_keys[default_one_client_id]
+ else:
+ client = client_keys[default_client_id]
+
+ # Allow user to override client settings
+ force_client = self.preferences['force_client']
+ if force_client in client_keys:
+ client = client_keys[force_client]
+ elif force_client and force_client[0] == '{':
+ try:
+ client = json.loads(force_client)
+ except:
+ logging.error("Could not parse force_client json")
+
+ args = (
+ client,
+ self.preferences['username'],
+ self.preferences['password'],
+ )
def pandora_ready(*ignore):
logging.info("Pandora connected")
3  pithos/PreferencesPithosDialog.py
View
@@ -23,6 +23,7 @@
import gobject
from pithos.pithosconfig import *
+from pithos.pandora.data import *
from pithos.plugins.scrobble import LastFmAuth
try:
@@ -98,6 +99,8 @@ def __load_preferences(self):
# If set, allow insecure permissions. Implements CVE-2011-1500
"unsafe_permissions": False,
"audio_quality": default_audio_quality,
+ "pandora_one": False,
+ "force_client": None,
}
try:
6 pithos/pandora/__init__.py
View
@@ -16,9 +16,9 @@
from pithos.pandora.pandora import *
-def make_pandora(prefs, testing=False):
+def make_pandora(testing=False):
if testing:
from pithos.pandora.fake import FakePandora
- return FakePandora(prefs)
+ return FakePandora()
else:
- return Pandora(prefs)
+ return Pandora()
30 pithos/pandora/data.py
View
@@ -0,0 +1,30 @@
+client_keys = {
+ 'android-generic':{
+ 'deviceModel': 'android-generic',
+ 'username': 'android',
+ 'password': 'AC7IBG09A3DTSYM4R41UJWL07VLN8JI7',
+ 'rpcUrl': '://tuner.pandora.com/services/json/?',
+ 'encryptKey': '6#26FRL$ZWD',
+ 'decryptKey': 'R=U!LH$O2B#',
+ 'version' : '5',
+ },
+ 'pandora-one':{
+ 'deviceModel': 'D01',
+ 'username': 'pandora one',
+ 'password': 'TVCKIBGS9AO9TSYLNNFUML0743LH82D',
+ 'rpcUrl': '://internal-tuner.pandora.com/services/json/?',
+ 'encryptKey': '2%3WCL*JU$MP]4',
+ 'decryptKey': 'U#IO$RZPAB%VX2',
+ 'version' : '5',
+ }
+}
+default_client_id = "android-generic"
+default_one_client_id = "pandora-one"
+
+# See http://pan-do-ra-api.wikia.com/wiki/Json/5/station.getPlaylist
+valid_audio_formats = [
+ ('highQuality', 'High'),
+ ('mediumQuality', 'Medium'),
+ ('lowQuality', 'Low'),
+]
+default_audio_quality = 'mediumQuality'
2  pithos/pandora/fake.py
View
@@ -19,7 +19,7 @@
import logging
class FakePandora(Pandora):
- def __init__(self, prefs):
+ def __init__(self):
super(FakePandora, self).__init__(prefs)
self.counter = 0
self.show_fail_window()
38 pithos/pandora/pandora.py
View
@@ -26,14 +26,6 @@
# credentials.
# See http://pan-do-ra-api.wikia.com/wiki/Json/5 for API documentation.
-PROTOCOL_VERSION = '5'
-RPC_URL = "://tuner.pandora.com/services/json/?"
-DEVICE_MODEL = 'android-generic'
-PARTNER_USERNAME = 'android'
-PARTNER_PASSWORD = 'AC7IBG09A3DTSYM4R41UJWL07VLN8JI7'
-ENCRYPT_KEY = '6#26FRL$ZWD'
-DECRYPT_KEY = 'R=U!LH$O2B#'
-
HTTP_TIMEOUT = 30
USER_AGENT = 'pithos'
@@ -67,13 +59,8 @@ def pad(s, l):
return s + "\0" * (l - len(s))
class Pandora(object):
- def __init__(self, prefs):
- self.partner_username = prefs.get('partner_username', PARTNER_USERNAME)
- self.partner_password = prefs.get('partner_password', PARTNER_PASSWORD)
- self.device_model = prefs.get('device_model', DEVICE_MODEL)
- self.blowfish_encode = Blowfish(prefs.get('encrypt_key', ENCRYPT_KEY))
- self.blowfish_decode = Blowfish(prefs.get('decrypt_key', DECRYPT_KEY))
- self.rpc_url = prefs.get('rpc_url', RPC_URL)
+ def __init__(self):
+ pass
def pandora_encrypt(self, s):
return "".join([self.blowfish_encode.encrypt(pad(s[i:i+8], 8)).encode('hex') for i in xrange(0, len(s), 8)])
@@ -94,7 +81,7 @@ def json_call(self, method, args={}, https=False, blowfish=True):
url_arg_strings.append('method=%s'%method)
protocol = 'https' if https else 'http'
- url = protocol + self.rpc_url + '&'.join(url_arg_strings)
+ url = protocol + self.rpcUrl + '&'.join(url_arg_strings)
if self.time_offset:
args['syncTime'] = int(time.time()+self.time_offset)
@@ -150,7 +137,7 @@ def json_call(self, method, args={}, https=False, blowfish=True):
raise PandoraError("Login Error", code, submsg="Invalid username or password")
elif code == API_ERROR_LISTENER_NOT_AUTHORIZED:
raise PandoraError("Pandora Error", code,
- submsg="A Pandora One account is required to access this feature.")
+ submsg="A Pandora One account is required to access this feature. Uncheck 'Pandora One' in Settings.")
elif code == API_ERROR_PARTNER_NOT_AUTHORIZED:
raise PandoraError("Login Error", code,
submsg="Invalid Pandora partner keys. A Pithos update may be required.")
@@ -166,10 +153,21 @@ def set_audio_quality(self, fmt):
def set_url_opener(self, opener):
self.opener = opener
- def connect(self, user, password):
- self.partnerId = self.userId = self.partnerAuthToken = self.userAuthToken = self.time_offset = None
+ def connect(self, client, user, password):
+ self.partnerId = self.userId = self.partnerAuthToken = None
+ self.userAuthToken = self.time_offset = None
+
+ self.rpcUrl = client['rpcUrl']
+ self.blowfish_encode = Blowfish(client['encryptKey'])
+ self.blowfish_decode = Blowfish(client['decryptKey'])
+
+ partner = self.json_call('auth.partnerLogin', {
+ 'deviceModel': client['deviceModel'],
+ 'username': client['username'], # partner username
+ 'password': client['password'], # partner password
+ 'version': client['version']
+ },https=True, blowfish=False)
- partner = self.json_call('auth.partnerLogin', {'deviceModel': self.device_model, 'username': self.partner_username, 'password': self.partner_password, 'version': PROTOCOL_VERSION}, https=True, blowfish=False)
self.partnerId = partner['partnerId']
self.partnerAuthToken = partner['partnerAuthToken']
9 pithos/pithosconfig.py
View
@@ -21,20 +21,11 @@
VERSION = '0.3.17'
-
import os
class project_path_not_found(Exception):
pass
-# See http://pan-do-ra-api.wikia.com/wiki/Json/5/station.getPlaylist
-valid_audio_formats = [
- ('highQuality', 'High'),
- ('mediumQuality', 'Medium'),
- ('lowQuality', 'Low'),
-]
-default_audio_quality = 'mediumQuality'
-
def get_data_file(*path_segments):
"""Get the full path to a data file.
Please sign in to comment.
Something went wrong with that request. Please try again.