forked from Fornoth/spotify-connect-web
-
Notifications
You must be signed in to change notification settings - Fork 0
/
connect.py
151 lines (127 loc) · 5.91 KB
/
connect.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env python
import os
import argparse
import signal
import sys
import json
import uuid
from connect_ffi import ffi, lib, C
from console_callbacks import audio_arg_parser, mixer, error_callback, connection_callbacks, debug_callbacks, playback_callbacks, playback_setup
from lastfm import lastfm_arg_parser
from utils import print_zeroconf_vars
class Connect:
def __init__(self, error_cb = error_callback, web_arg_parser = None):
arg_parsers = [audio_arg_parser, lastfm_arg_parser]
if web_arg_parser:
arg_parsers.append(web_arg_parser)
arg_parser = argparse.ArgumentParser(description='Web interface for Spotify Connect', parents=arg_parsers)
arg_parser.add_argument('--debug', '-d', help='enable libspotify_embedded/flask debug output', action="store_true")
arg_parser.add_argument('--key', '-k', help='path to spotify_appkey.key (can be obtained from https://developer.spotify.com/my-account/keys )', default='spotify_appkey.key')
arg_parser.add_argument('--username', '-u', help='your spotify username')
arg_parser.add_argument('--password', '-p', help='your spotify password')
arg_parser.add_argument('--name', '-n', help='name that shows up in the spotify client', default='TestConnect')
arg_parser.add_argument('--bitrate', '-b', help='Sets bitrate of audio stream (may not actually work)', choices=[90, 160, 320], type=int, default=160)
arg_parser.add_argument('--credentials', '-c', help='File to load and save credentials from/to', default='credentials.json')
self.args = arg_parser.parse_args()
print "Using libspotify_embedded version: {}".format(ffi.string(lib.SpGetLibraryVersion()))
try:
with open(self.args.key) as f:
app_key = ffi.new('uint8_t *')
f.readinto(ffi.buffer(app_key))
app_key_size = len(f.read()) + 1
except IOError as e:
print "Error opening app key: {}.".format(e)
print "If you don't have one, it can be obtained from https://developer.spotify.com/my-account/keys"
sys.exit(1)
self.credentials = dict({
'device-id': str(uuid.uuid4()),
'username': None,
'blob': None
})
try:
with open(self.args.credentials) as f:
self.credentials.update(
{ k: v.encode('utf-8') if isinstance(v, unicode) else v
for (k,v)
in json.loads(f.read()).iteritems() })
except IOError:
pass
if self.args.username:
self.credentials['username'] = self.args.username
userdata = ffi.new_handle(self)
if self.args.debug:
lib.SpRegisterDebugCallbacks(debug_callbacks, userdata)
self.config = {
'version': 4,
'buffer': C.malloc(0x100000),
'buffer_size': 0x100000,
'app_key': app_key,
'app_key_size': app_key_size,
'deviceId': ffi.new('char[]', self.credentials['device-id']),
'remoteName': ffi.new('char[]', self.args.name),
'brandName': ffi.new('char[]', 'DummyBrand'),
'modelName': ffi.new('char[]', 'DummyModel'),
'client_id': ffi.new('char[]', '0'),
'deviceType': lib.kSpDeviceTypeAudioDongle,
'error_callback': error_cb,
'userdata': userdata,
}
init = ffi.new('SpConfig *' , self.config)
init_status = lib.SpInit(init)
print "SpInit: {}".format(init_status)
if init_status != 0:
print "SpInit failed, exiting"
sys.exit(1)
lib.SpRegisterConnectionCallbacks(connection_callbacks, userdata)
lib.SpRegisterPlaybackCallbacks(playback_callbacks, userdata)
mixer_volume = int(mixer.getvolume()[0] * 655.35)
lib.SpPlaybackUpdateVolume(mixer_volume)
bitrates = {
90: lib.kSpBitrate90k,
160: lib.kSpBitrate160k,
320: lib.kSpBitrate320k
}
lib.SpPlaybackSetBitrate(bitrates[self.args.bitrate])
playback_setup()
print_zeroconf_vars()
if self.credentials['username'] and self.args.password:
self.login(password=self.args.password)
elif self.credentials['username'] and self.credentials['blob']:
self.login(blob=self.credentials['blob'])
else:
if __name__ == '__main__':
raise ValueError("No username given, and none stored")
def login(self, username=None, password=None, blob=None, zeroconf=None):
if username is not None:
self.credentials['username'] = username
elif self.credentials['username']:
username = self.credentials['username']
else:
raise ValueError("No username given, and none stored")
if password is not None:
lib.SpConnectionLoginPassword(username, password)
elif blob is not None:
lib.SpConnectionLoginBlob(username, blob)
elif zeroconf is not None:
lib.SpConnectionLoginZeroConf(username, *zeroconf)
else:
raise ValueError("Must specify a login method (password, blob or zeroconf)")
def signal_handler(signal, frame):
lib.SpConnectionLogout()
lib.SpFree()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
#Only run if script is run directly and not by an import
if __name__ == "__main__":
@ffi.callback('void(SpError err, void *userdata)')
def console_error_callback(error, userdata):
if error == lib.kSpErrorLoginBadCredentials:
print 'Invalid username or password'
#sys.exit() doesn't work inside of a ffi callback
C.exit(1)
else:
error_callback(msg)
connect = Connect(console_error_callback)
while 1:
lib.SpPumpEvents()